/**
 * The course card.
 *
 * Rendered by themes/buddyboss-theme-child/learndash/ld30/template-course-item.php,
 * which reaches seven surfaces through get_template_part(): the courses page,
 * the course archive, the ld_course_category archive, a member profile's courses
 * tab, the external courses archive and its AJAX refetch. This sheet therefore
 * loads unconditionally -- the markup and the rules that arrange it must not be
 * separable, because a card whose stylesheet did not load is not a card that
 * degrades, it is a pile.
 *
 *
 * WHAT THIS SHEET IS ALLOWED TO CONTEST
 *
 * Most of the card's vendor class names were dropped rather than overridden.
 * bb-course-cover, bb-cover-wrap, course-lesson-count, bb-course-excerpt,
 * course-progress-wrap and ld-status carried roughly 147 lines of theme CSS
 * between them; the elements now carry avia- names instead, so those rules
 * match nothing and there is nothing to win.
 *
 * Dropping a class is not free, though, and it is not enough to check that no
 * script reads it. Four of those six names were also load-bearing for the
 * courses widget:
 *
 *   bb-cover-wrap        two widget controls: cover height (a percentage the
 *                        theme wrote as padding-top) and media background
 *   bb-course-excerpt    two widget controls, colour and typography
 *   course-progress-wrap a theme rule behind the widget's progress toggle
 *   bb-course-cover      a theme rule behind the widget's media toggle
 *
 * The four control selectors are retargeted at our own class names in
 * avia-course-grid/widgets/courses/controls/learndash-courses-controls.php,
 * which is ours to change; the cover height control now writes an aspect-ratio
 * rather than a padding-top, and its 52% default is 25 / 13 exactly, so a
 * tenant that never touched it sees no change. The live toggles are
 * re-implemented here against data-avia-hide / data-avia-show, which the
 * widget emits beside the class names it always emitted.
 *
 * Two groups of those controls are dead rather than retargeted, because what
 * they styled is gone from the card: the excerpt's colour and typography, since
 * the excerpt was removed outright, and the four that size and colour a
 * progress bar, since progress is a ring now (progress.css) and a bar's height
 * is not a thing a ring has. Of the three toggles, media and progress still
 * hide their element and the excerpt's has nothing left to hide.
 *
 * The way to catch this class of break is to read every selector out of the
 * control file and count what each one matches in the browser, before and
 * after. A control whose selector matches nothing is a setting that silently
 * stopped applying.
 *
 * Three names stay, deliberately: bb-cover-list-item, bb-course-title and
 * bb-card-course-details. The widget's controls emit tenant CSS against
 * exactly those three -- card background, border radius, title colour and
 * typography, details padding -- and unlike the four above they are not ours
 * to retarget without also moving markup. What this sheet declares on them is
 * arrangement plus the body's padding, which is the designer's now. Colour,
 * background and radius are left alone, and a tenant that set any of the
 * controls still gets them: control CSS prints unlayered, which beats a layer.
 *
 * The exception, and it is a real behaviour change: the title's margin-bottom
 * control and the child theme's #page override of it both lose now, because
 * spacing between the body's children is the gap's job and two authorities on
 * one gap is what this file exists to end. Checked on this tenant: none of the
 * four courses-widget instances sets any card style control. Not verifiable
 * fleet-wide from a single database.
 *
 *
 * WHAT REPLACED THE BADGE ARITHMETIC
 *
 * The template used to position each badge itself, with an offset computed in
 * PHP from which other badges were present -- 44px or 15px for the format
 * badge, 74px or 45px for the start date, and a fifth number owed the moment a
 * third badge existed. The badges are now one absolutely positioned column with
 * a gap, so the stack has no numbers in it and adding a badge is adding a
 * badge.
 */

/*
 * The card. Cover on top, body taking the rest, so a row of cards ends level.
 *
 * It is also a query container, which is what lets the title size itself off
 * the card rather than off the window. What clips a title is the width of the
 * box it is in, and that is the column count and the sidebar's state as much
 * as it is the screen: the same 1512px laptop gives a card 281px with the
 * BuddyPanel open and 381px with it shut, and a tenant who sets the widget to
 * six columns gets 180px on any screen at all. A viewport query cannot see any
 * of that.
 */
.avia-card {
	container-type: inline-size;
	display: grid;
	grid-template-rows: auto 1fr;
}

/*
 * Two guards against one hazard: text with no break opportunity in it.
 *
 * min-content is the longest unbreakable run, normally the longest word -- ~70px
 * in this prose. But course prose is pasted, and pasted prose arrives with
 * non-breaking spaces between the words: 64 of the 500 published courses here
 * carry at least one, and four are glued throughout. In those, the whole
 * sentence is one 802px word.
 *
 * A grid item takes its min-content width as an automatic minimum, so without
 * min-width: 0 one such run widens the card's own column -- measured at 842px
 * inside a 281px card, with every card in the row stretched to match. The
 * list-view arm below says minmax(0, 1fr) for the same reason; this is that, for
 * the single-column case. The theme never met it because its card was
 * display: block, and block layout does not size to its content.
 *
 * It was found on the excerpt, which the card no longer renders, and the guards
 * stay because the hazard was never the excerpt: the same pasted text is what a
 * title is, and a course title can be a single glued phrase.
 *
 * min-width: 0 keeps the card's geometry, but the run still lays out at its own
 * width inside the card and is clipped at the edge mid-sentence, which is what
 * it did. overflow-wrap is the other half. It inherits, so declaring it on the
 * card's children reaches the title through the body, and it is anywhere rather
 * than break-word because only anywhere also lowers min-content -- the quantity
 * the first declaration is defending against.
 */
.avia-card > * {
	min-width: 0;
	overflow-wrap: anywhere;
}

/*
 * List view, which the member profile courses tab and the course archives still
 * offer. The theme built it by turning .bb-cover-list-item into a flex row, and
 * the arrangement is owned here now.
 *
 * #page-anchored, and display restated, for the reason the title carries the
 * same anchor at the foot of this file: on a surface where the cascade block is
 * not built this sheet prints unlayered, and unlayered it is .avia-card at
 * (0,1,0) against .bb-course-items.list-view .bb-cover-list-item at (0,3,0). The
 * layer was doing the work and only on the pages that had one -- measured on the
 * member profile, where the card was still laying out as the vendor's flex row
 * with its cover collapsed to zero width. A fixed-size ring hid that; a bar that
 * takes the width it is given does not.
 *
 * The cover column replaces four stepped vendor widths -- 240px, then 200px
 * under 1040, 140px under 460 and 110px under 375 -- with one continuous track.
 * It matches at both ends and runs up to ~40px wider than the theme did between
 * 1040 and 1200, which is a deliberate difference: four media queries existing
 * to interpolate a single number by hand is what clamp() is for.
 *
 * The cover fills the row and the row's floor comes from the card, not from the
 * cover. Leaving the ratio on the cover does not work: aspect-ratio resolves a
 * stretched grid item's height instead of contributing a minimum to the row, so
 * the cover stopped at its ratio and left the card's remaining height empty
 * beside the body. The width is declared once as a property and the floor is
 * derived from it, so the ratio has one place to change.
 */
#page .list-view .avia-card {
	--avia-card-cover-width: clamp(110px, 30vw, 240px);
	display: grid;
	grid-template-rows: none;
	grid-template-columns: var(--avia-card-cover-width) minmax(0, 1fr);
	min-height: calc(var(--avia-card-cover-width) * 0.7);
}

/*
 * A list row is wide enough that a bar spanning it stops being a gauge and
 * becomes a rule across the card -- measured at 1055px on the member profile.
 * 280px is the width the design file draws its Progress bar atom at, and past
 * roughly that the extra length adds no precision to a figure the card also
 * prints as a number.
 */
.list-view .avia-card .avia-progress {
	max-width: 280px;
}

.list-view .avia-card-cover {
	aspect-ratio: auto;
	height: 100%;
	border-start-end-radius: 0;
	border-end-start-radius: inherit;
}

/*
 * The cover.
 *
 * aspect-ratio replaces the theme's padding-top: 52% and is written as the
 * same ratio rather than a rounder one, so the rebuild does not quietly resize
 * every thumbnail in the estate: 25/13 is 52% exactly.
 *
 * The fill shows in one situation: behind an image that is transparent or that
 * failed to load. It is here so that a cover with nothing to show reads the
 * same whether the course has no thumbnail at all -- the placeholder below --
 * or has one whose file is missing, which would otherwise be bare white with
 * the browser's broken-image glyph in the corner. The theme covered this with
 * an opaque slate that also hid the fact that the file was missing.
 *
 * The cost is that the widget's media background control, which this sheet
 * retargets onto this element, now loses to that default from a later layer.
 * That is the second control spent this way, after the title's margin, and for
 * the same reason: one authority per property beats two.
 */
.avia-card-cover {
	position: relative;
	display: block;
	overflow: hidden;
	aspect-ratio: 25 / 13;
	background: var(--avia-color-fill-subtle);
}

/*
 * The cover's top corners follow the card's own, whatever a tenant set it to.
 * inherit takes the parent's computed radius, which is what replaced the two
 * rules that spelled it as arithmetic -- 9px for a 1px border and 8px for the
 * mandatory 2px one, both derived by hand from a 10px card.
 */
.avia-card-cover {
	border-start-start-radius: inherit;
	border-start-end-radius: inherit;
}

/*
 * A course with no featured image. The card is common enough that this is not
 * really a fallback: a tinted field with the AVIA mark at half opacity, which
 * reads as deliberate rather than as a missing file.
 */
.avia-card-cover:not(:has(img)) {
	background: radial-gradient(circle at 60% 40%, var(--avia-color-surface) 0%, #ebebeb 100%);
}

.avia-card-cover:not(:has(img))::before {
	content: '';
	position: absolute;
	inset: 0;
	background: radial-gradient(circle, #fff 56px, transparent 56px);
}

.avia-card-cover:not(:has(img))::after {
	content: '';
	position: absolute;
	inset: 0;
	z-index: var(--avia-z-base);
	background: url('/wp-content/plugins/avia-dashboard/assets/images/avia-icon.svg') center / 72px no-repeat;
	opacity: 0.5;
	filter: grayscale(1);
}

/*
 * The cover skin's overlay. The widget's Overlay Background Color control
 * paints this pseudo with the tenant's tint; it used to paint
 * .bb-cover-wrap:after, whose box the vendor sheet generated for markup the
 * card no longer renders, so the box is generated here instead. With no tint
 * set it is transparent, and the skin class is the widget's, printed on the
 * wrapper both course sections share.
 */
.learndash-course-list--cover .avia-card-cover::after {
	content: '';
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.avia-card-cover img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/*
 * The badge stack, set against the AVIA mark that is part of the artwork rather
 * than against the cover's corner.
 *
 * The mark is baked into the thumbnail, and it is a template: measured across
 * eight unrelated courses it is the same 43x43 square at (551, 30) of the same
 * 624x326 canvas, down to the pixel count. So its geometry is a ratio of the
 * cover, not a length -- 30/624 in from the edge and 51.5/326 down to its
 * centre -- and a fixed inset can only agree with it at one card width. These
 * two figures are that ratio, and they are what keeps the badge level with the
 * mark at every width instead of at 279px alone.
 *
 * The 25:13 cover crops a fraction off a 624:326 image, which moves the mark's
 * centre by about a fifth of a pixel at these sizes. Not worth carrying.
 *
 * A card whose artwork has no mark -- the placeholder, or a tenant's own upload
 * -- gets a badge inset that scales with the cover instead of a constant one,
 * which is no worse than the constant was.
 *
 * align-items keeps each pill the width of its own label rather than the width
 * of the column, which is what lets them sit in a flex column at all. The
 * translate lifts the stack by half a badge so the first one is centred on the
 * mark; a second badge then falls below it.
 */
.avia-card-badges {
	--avia-card-mark-inset: 4.808%;
	--avia-card-mark-center: 15.798%;
	/*
	 * Declared here rather than on the badge, because a custom property reaches
	 * a child and not a parent, and it is the stack that has to know the height
	 * to lift itself by half of one. 18px is what 12px of type at leading 1
	 * plus the 3px it used to be padded with came to.
	 */
	--avia-card-badge-height: 18px;
	position: absolute;
	inset-block-start: var(--avia-card-mark-center);
	inset-inline-start: var(--avia-card-mark-inset);
	translate: 0 calc(var(--avia-card-badge-height) / -2);
	z-index: var(--avia-z-raised);
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--avia-space-2);
}

/*
 * One badge, one appearance, colour supplied by the variant. The two format
 * colours below are the only raw values in this file, and they are here rather
 * than in tokens.css on purpose: they identify a course format and are not
 * roles in the palette, so promoting them would put two things in the token
 * file that no second component could ever legitimately read.
 */
.avia-card-badge {
	display: inline-flex;
	align-items: center;
	height: var(--avia-card-badge-height);
	padding-inline: var(--avia-space-3);
	border-radius: var(--avia-radius-pill);
	background: var(--avia-card-badge-bg, var(--avia-color-muted));
	color: var(--avia-color-accent-contrast);
	/*
	 * The card's smallest step, and it was 10px until the page was asked to carry
	 * three sizes rather than six. A tenth size existing only for one word on a
	 * cover is exactly the kind of step that made the count six.
	 *
	 * The word is set as the catalog writes it. Uppercasing it was buying emphasis
	 * the pill has already bought with a filled accent background, and at 12px it
	 * cost the badge half the cover's width -- SKYLDUNÁMSKEIÐ is a long word to
	 * shout. Losing the transform takes the 0.4px tracking with it: that spacing
	 * exists to keep capitals legible and only loosens lowercase.
	 */
	font-size: var(--avia-text-xs);
	font-weight: var(--avia-weight-semibold);
	line-height: var(--avia-leading-none);
	white-space: nowrap;
}

.avia-card-badge[data-kind="mandatory"] { --avia-card-badge-bg: var(--avia-color-accent); }
.avia-card-badge[data-kind="webinar"] { --avia-card-badge-bg: #2563eb; }
.avia-card-badge[data-kind="workshop"] { --avia-card-badge-bg: #0a875a; }
.avia-card-badge[data-kind="date"] { --avia-card-badge-bg: var(--avia-color-muted); }

/*
 * The seat states, worn by the same pill inside the series panel below: one
 * declaration of the box, whether it sits on a cover or in a list of runs. The
 * four replace four hardcoded WordPress-admin colours. Enrolled is the muted
 * one on purpose -- it is the only state that reports rather than invites, and
 * on a tenant whose accent is red it would otherwise be indistinguishable from
 * full.
 */
.avia-card-badge[data-kind="open"] { --avia-card-badge-bg: var(--avia-color-success); }
.avia-card-badge[data-kind="full"] { --avia-card-badge-bg: var(--avia-color-danger); }
.avia-card-badge[data-kind="waitlist"] { --avia-card-badge-bg: var(--avia-color-warning); }
.avia-card-badge[data-kind="enrolled"] { --avia-card-badge-bg: var(--avia-color-muted); }

/*
 * The body.
 *
 * The gap is the only thing that spaces these children, which is why the reset
 * below it exists: every child arrives carrying vendor block margins, and a
 * margin plus a gap is two spacings that have to be kept in agreement by hand.
 * Only the block axis is reset, and only on direct children, so a component
 * further in keeps its own margins.
 */
.avia-card-body {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--avia-space-3);
}

/*
 * The body's metrics are the design's: 12px over 16px sides and bottom. The
 * vendor sheet says 16px 20px 10px at one class of specificity on the surfaces
 * whose cascade block is not built, so the card's class rides along in the
 * selector; a tenant's details-padding control is #page-anchored and unlayered,
 * and still outranks this everywhere.
 *
 * The leading belongs here for a different reason: the theme declares it as a
 * length, `body { line-height: 1.6875rem }`, and a length inherits unchanged
 * however small the child's own type is -- so the 14px meta row sat in a 27px
 * line box, six pixels taller than the design. A ratio re-derives it per child,
 * which is why nothing below this needs its own except the title.
 */
.avia-card .avia-card-body {
	padding: var(--avia-space-4) var(--avia-space-5) var(--avia-space-5);
	line-height: var(--avia-leading-normal);
}

.avia-card-body > * {
	margin-block: 0;
}

/*
 * The meta row: the lesson count, and beside it the series indicator when a
 * course is one run of several. It was three utility classes in the markup
 * until it needed a position of its own for the tooltip below -- and once a
 * component rule exists, leaving half the row's type in the markup is two
 * authorities on one row.
 *
 * The body aligns its children to flex-start, so this row would otherwise be as
 * wide as the words in it -- 74px measured. It stretches because the panel
 * below takes its width from this box, and an invisible row of the body's full
 * width looks no different.
 *
 * It sits at the foot of the body -- with the progress bar under it, where a
 * course has one -- and the auto margin is what puts it there. The card is
 * `grid-template-rows: auto 1fr`, so the body already owns the height a taller
 * neighbour gives the row; without this the title held the top of that space and
 * the rest of it was blank. The slack collects above this row instead, so a
 * one-line title and a three-line one land their counts on the same line across
 * a row of cards.
 */
.avia-card-meta {
	position: relative;
	align-self: stretch;
	margin-block-start: auto;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--avia-space-2);
	/*
	 * The rule is what makes the space above it read as space. Whatever the auto
	 * margin leaves is at the top of a footer once there is a footer, where
	 * without one it was a hole between the title and a floating line of text.
	 */
	border-block-start: 1px solid var(--avia-color-border);
	padding-block-start: var(--avia-space-4);
	color: var(--avia-color-muted);
	font-size: var(--avia-text-base);
}

/*
 * The state, at the far end of the meta row it shares with the lesson count.
 *
 * It takes the row's own colour rather than one per state. A green "Lokið" was
 * a third signal on a card that already carries a filled pill on the cover and
 * a bar under the row, and the one it competed hardest with was the bar
 * directly beneath it saying 100% of the same thing. The state is a caption on
 * the count, not an alarm, so it takes no weight of its own either: the end of
 * the row is where it sits and that is the whole of the distinction.
 *
 * The state is still on the element as data, so a case that ever does need
 * colouring has somewhere to hang it.
 */
.avia-card-status {
	margin-inline-start: auto;
}

/*
 * The series indicator, and the panel of that series' other runs.
 *
 * The panel used to open rightward from the icon, which put it through the
 * right edge of the viewport on the last card in a row, and an inline script
 * measured getBoundingClientRect() on every mouseenter to decide whether to
 * flip it. Nothing needs measuring: anchored to the meta row rather than to the
 * icon, inset-inline: 0 gives the panel the card's own content width, so it has
 * no edge to cross and no side to choose. That is what the position on the row
 * above is for.
 *
 * Opening upward puts it over the cover, which is inside the card's own box, so
 * it is clipped by nothing until a series has enough runs to stand taller than
 * the cover -- the overflow rule below is for that case.
 */
.avia-series-indicator {
	display: inline-flex;
	align-items: center;
	color: var(--avia-color-accent);
	cursor: pointer;
}

.avia-series-tooltip {
	display: none;
	position: absolute;
	inset-inline: 0;
	inset-block-end: calc(100% + var(--avia-space-2));
	z-index: var(--avia-z-dropdown);
	padding: var(--avia-space-4);
	border: 1px solid var(--avia-color-border);
	border-radius: var(--avia-radius-md);
	background: var(--avia-color-surface);
	box-shadow: var(--avia-shadow-md);
	text-align: start;
}

.avia-series-indicator:hover .avia-series-tooltip {
	display: block;
}

/*
 * The gap between the row and the panel is a hover hole: the cursor leaves the
 * icon's own 16px box long before it reaches the panel, and the panel closes
 * under it. This bridges the gap and the few pixels of row above the icon, and
 * it is a pseudo on the panel so it exists only while the panel does.
 */
.avia-series-tooltip::after {
	content: '';
	position: absolute;
	inset-inline: 0;
	inset-block-start: 100%;
	height: var(--avia-space-4);
}

.avia-series-tooltip strong {
	display: block;
	margin-block-end: var(--avia-space-3);
	color: var(--avia-color-muted);
	font-size: var(--avia-text-xs);
	letter-spacing: 0.4px;
	text-transform: uppercase;
}

.avia-series-tooltip-item {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--avia-space-3);
	padding-block: var(--avia-space-2);
	border-block-end: 1px solid var(--avia-color-border);
	color: var(--avia-color-text);
	font-size: var(--avia-text-sm);
	text-decoration: none;
}

.avia-series-tooltip-item:last-child {
	border-block-end: 0;
}

.avia-series-tooltip-item:hover {
	color: var(--avia-color-accent);
}

/*
 * A panel taller than the cover leaves the card, and the theme clips the card:
 * `overflow: hidden` at .bb-course-items .bb-cover-list-item, which is (0,2,0)
 * -- the same specificity a rule of ours on .avia-card can reach, and this
 * sheet prints first, so a tie loses. The #page anchor is the trick the title
 * uses at the foot of this file: it outranks vendor on a page whose cascade
 * block is not built, and is redundant on one where it is.
 */
#page .avia-card:has(.avia-series-indicator) {
	overflow: visible;
}

/*
 * A card with no progress line still ends where its neighbours do.
 *
 * There is no line when the reader is not enrolled, or when the course has no
 * lessons for a percentage to be a fraction of. Without a reserve the meta row
 * becomes the body's last child, the auto margin above it drops it the whole
 * way, and one card in a row of four puts its lesson count 22px below the other
 * three -- which is the gap plus the line below.
 *
 * The height is that line's own, not a literal: the figure beside the bar is the
 * tallest thing in it, so it is one line of --avia-text-base at
 * --avia-leading-none, and the flex gap supplies the rest. Those are the two
 * declarations .avia-progress makes about its type in progress.css, and this
 * reserve is only right for as long as they are what that component says.
 *
 * There is nothing to line up with in a list where no card has a line at all,
 * and the reserve is then 22px of empty card on every one of them. A reader
 * enrolled in nothing sees a whole page of that. The list is as far as the
 * condition can reach: a row is not addressable in CSS, so a list holding one
 * enrolled course keeps the reserve on all of them, which is the point.
 *
 * Matching the list by element rather than by class covers all eight surfaces
 * the card renders on without naming a vendor hook, and a card rendered outside
 * a list keeps the reserve, which is the safe direction for a case that has
 * nothing beside it anyway.
 *
 * The toggle needs the same exemption for a different reason: it leaves the
 * line in the markup and hides it, so the list still counts as having one.
 */
.avia-card-body:not(:has(.avia-progress))::after {
	content: '';
	height: calc(var(--avia-text-base) * var(--avia-leading-none));
}

:where(ul, ol):not(:has(.avia-progress)) .avia-card-body::after,
[data-avia-hide~="progress"] .avia-card-body::after {
	content: none;
}

/*
 * The widget's progress toggle. The five tenant controls behind it stay dead:
 * they write heights and colours at .ld-progress-bar, which is markup this card
 * stopped rendering when it was rebuilt, and retargeting them would hand a
 * tenant control over a shared component's geometry from one of its callers.
 *
 * Where the bar itself goes needs no rule: it is the body's last child, and the
 * body is a flex column whose gap already spaces it off the meta row above.
 */
[data-avia-hide~="progress"] .avia-card-body > .avia-progress {
	display: none;
}

/*
 * The widget's media toggle. The theme hid the image and kept the frame, which
 * worked because the frame had a background of its own; without one it would
 * be an invisible gap the height of the cover, so the cover goes with it.
 */
[data-avia-hide~="media"] .avia-card-cover {
	display: none;
}

.avia-card-footer {
	width: 100%;
}

/* The deadline line on a mandatory card, directly under the title. */
.avia-mandatory-deadline {
	color: var(--avia-color-accent);
	font-size: var(--avia-text-xs);
	font-weight: var(--avia-weight-semibold);
}

.avia-mandatory-deadline[data-state="overdue"] {
	color: var(--avia-color-danger);
	font-weight: var(--avia-weight-bold);
}

/*
 * The title's own type: 22px semibold, the size the learning plan card's title
 * had when the CEO pointed at it and asked for course names to match. The link
 * inherits it rather than setting it again, which is what the theme does and
 * what the tenant colour control targets.
 *
 * It is the biggest thing on the page now, and larger than the section heading
 * above it -- see title.css, where the two crossed over rather than meeting. A
 * course name is what a member came to the page to find; "Mín námskeið" is a
 * label on the drawer it is in.
 *
 * Size is the whole of that emphasis and weight adds nothing to it: at 22px in a
 * grid of cards whose every other line is 14px, a course name is already the
 * only thing the eye lands on, and a bolded 22px in four cards abreast reads as
 * a page shouting rather than as a heading.
 *
 * The well is two lines whether the title fills them or not, and a short title
 * is centred in it. Measured on this page, 27 of 43 titles are one line and 16
 * are two, so the short title is the common case rather than the exception, and
 * the meta row's auto margin was dropping the whole of the spare line into one
 * gap under it -- 38px against 8px on a two-line card. Split above and below,
 * the same space reads as the title's own air. The count and the bar below it
 * do not move either way; the vendor clamps this to two lines, so the well is
 * never short of the title.
 */
/*
 * The size is a line through two measured points rather than a preference: 22px
 * is right at a 381px card and 18px is right at 220px, so 2.5cqi + 12.5px is
 * the line through them and the clamp holds it between 16 and 22.
 *
 * Counted across 43 courses, a 281px card clips three titles at 22px and none
 * at 20; a 220px card clips ten at 22 and three at 18; a 180px card clips
 * sixteen and eleven. Above 381px nothing clips at any size, which is where the
 * upper clamp sits -- a wide card gets the size the CEO asked for and no more.
 */
.avia-card-title {
	display: grid;
	align-content: center;
	min-height: calc(2em * var(--avia-leading-snug));
	font-size: clamp(16px, 2.5cqi + 12.5px, var(--avia-text-xl));
	font-weight: var(--avia-weight-normal);
	line-height: var(--avia-leading-snug);
}

/*
 * The one vendor rule this sheet has to outrank on its own. The title keeps
 * .bb-course-title because three tenant controls target it, and the parent
 * theme styles that class as `#page .bb-course-title` -- an ID's worth of
 * specificity this sheet's class selectors cannot tie. On a page whose sheets
 * are layered the layer settles it; on a page where no sheet is assigned and
 * the cascade block is not built, this sheet prints unlayered and would lose
 * exactly those declarations -- measured on the category archive as the only
 * difference between the two worlds. So the same declarations once more, at
 * `#page`-anchored specificity, the way global.css wins its report gap:
 * scoping instead of !important. A tenant's own margin control still wins --
 * it writes `#page {{WRAPPER}} .bb-course-title`, which outranks this.
 */
#page .avia-card .avia-card-title {
	margin-block: 0;
	font-size: clamp(16px, 2.5cqi + 12.5px, var(--avia-text-xl));
	line-height: var(--avia-leading-snug);
}


.avia-card-title a {
	font: inherit;
	transition: color var(--avia-duration-base) var(--avia-ease);
}

/*
 * Hover lift, on the theme's own value so a card of ours rises by the same
 * amount as everything else the theme lifts, and on the duration token so it
 * stops moving for a reader who asked for that.
 */
.avia-card {
	transition: box-shadow var(--avia-duration-base) var(--avia-ease);
}

.avia-card:hover {
	box-shadow: var(--avia-shadow-raised);
}

/*
 * The card is one target, so the title takes its hover colour from a hover
 * anywhere on the card rather than from its own two lines of text. The colour is
 * the theme's, reached through the accent token, so nothing about the state
 * changes -- only what triggers it. :focus-within is the keyboard's half of the
 * same thing: tabbing to the cover link is the same intent as pointing at it.
 */
.avia-card:hover .avia-card-title a,
.avia-card:focus-within .avia-card-title a {
	color: var(--avia-color-accent);
}

.avia-card-cover:focus-visible,
.avia-card-title a:focus-visible {
	outline: var(--avia-focus-width) solid var(--avia-focus-color);
	outline-offset: var(--avia-focus-offset);
}
