/**
 * Component: Tier Card  (patterns/tier-card.php)
 *
 * Reusable sponsorship-tier card — 480×620 @1440, r16, a vertical per-tier
 * gradient with the DuckBG3 skyline ghost at 16% opacity behind navy DynaPuff
 * type with a white contour. Figma: row 3 ("GRADIENT + SKYLINE GHOST") of
 * `2069:142`, five states `2075:2` (Headliner) / `2075:13` (Main Stage) /
 * `2075:24` (Rising Star) / `2075:35` (Festival Friend) / `2075:46` (Opening
 * Act) — read via get_design_context, not eyeballed. Decision log D26.
 *
 * BUILD-TRACKER SP1: this is the "partial" the tracker calls out as reused by
 * BOTH the sponsors-hero (now, cycling through all five) and sponsor-packages
 * (SP5, later, one per ticket). There is no shared PHP include layer for it —
 * `inc/*.php` files are hand-listed in functions.php (a shared file, off
 * limits to a single-item builder), so a new `inc/tier-card.php` helper
 * would need a functions.php edit this item isn't allowed to make. The
 * reusable contract is therefore CSS + markup convention, not a PHP
 * function: this partial is globbed and enqueued sitewide like every other
 * pattern CSS, so any pattern that emits the markup below (a `.dfd-tier-card`
 * root + the five `.dfd-tier-card--<slug>` modifiers + the class names
 * documented per element) gets the exact same visual component for free,
 * with zero coupling to this file's PHP. SP5, reuse this markup shape
 * directly rather than requiring `patterns/tier-card.php` — flagged in the
 * SP1 handoff for the orchestrator/SP5 builder to confirm.
 *
 * Markup contract (see patterns/tier-card.php for the literal example):
 *   <div class="dfd-tier-card-frame">
 *     <div class="dfd-tier-card dfd-tier-card--<slug>">
 *       <div class="dfd-tier-card__top">
 *         <p class="dfd-tier-card__kicker">SPONSORSHIP TIER 0X / 05</p>
 *         <p class="dfd-tier-card__year">2027</p>
 *       </div>
 *       <div class="dfd-tier-card__billing">
 *         <h3 class="dfd-tier-card__name">Name</h3> <!-- <p> in sponsors-hero.php's cycler — see .dfd-tier-card__name's own comment -->
 *         <p class="dfd-tier-card__amount">$X,000</p>
 *       </div>
 *       <div class="dfd-tier-card__teaser">
 *         <p class="dfd-tier-card__teaser-text">…</p>
 *         <a class="dfd-tier-card__link" href="#package-<slug>">See what's included →</a>
 *       </div>
 *     </div>
 *   </div>
 * `href="#package-<slug>"` (e.g. `#package-headliner`) targets that tier's
 * ticket in the not-yet-built packages section (SP5) — SP5 gives the
 * tickets those ids; same placeholder pattern as the hero's own
 * "Become a sponsor" button. The skyline ghost is a `.dfd-tier-card::before`
 * (CSS background-image), not a markup image — no alt text needed, it's
 * decorative, and it means every consumer gets it free without repeating
 * an <img>/<picture>.
 *
 * Gradient stops are tokens.css's `--dfd-tier-grad-<slug>-top/-bottom`
 * (gate round 1, pre-approved shared edit — promoted out of this partial
 * so SP5/SP6 read the same 5 values instead of each hand-copying the hex).
 * Text colours (kicker/teaser per tier) stay local custom props set per
 * `--modifier` below, since those aren't reused outside this component.
 *
 * One pattern = one CSS partial (auto-enqueued by inc/enqueue.php glob).
 * Zero shared-file edits.
 *
 * ── Sizing: a real CSS container query, not a viewport breakpoint (Alain,
 * live review round 2) ────────────────────────────────────────────────────
 * `.dfd-tier-card-frame` is the sizing/query box: it sets the card's actual
 * rendered WIDTH (a `clamp()` in split/two-column layouts, `min(30rem,100%)`
 * once stacked — both live in sponsors-hero.css, since only that pattern
 * knows which layout mode is active) and establishes
 * `container-type: inline-size`. Every internal measurement below (type,
 * padding, gaps, contour width) is in `cqi` — 1% of the FRAME's own inline
 * size — calibrated so 480px (30rem, the desktop/1440 exact-lock width)
 * reproduces the Figma desktop numbers exactly, and everything scales
 * continuously from there with no hard breakpoint at all: this replaced an
 * earlier two-state (desktop/"≤899.98px mobile") design that (a) didn't
 * actually track the card's real rendered width — in the 900–1216px
 * "generic tablet" band the card could shrink well below 480px while its
 * type stayed pinned at the 76px desktop size, overflowing the fixed
 * aspect-ratio box and clipping the teaser/link out of view — and (b) left
 * a visible jump exactly at 899.98px. `patterns/tier-card.php`'s standalone
 * example wraps itself in `.dfd-tier-card-frame` too, so it scales correctly
 * wherever an editor drops it, independent of viewport width — the
 * behaviour Alain asked for generalises to that consumer for free.
 *
 * `.dfd-tier-card` itself no longer sets its own width/aspect-ratio/radius as
 * literal desktop values — it just fills whatever `.dfd-tier-card-frame` gives
 * it (`.dfd-tier-card-frame .dfd-tier-card { position:absolute; inset:0; }` below).
 */

.dfd-tier-card-frame {
	container-type: inline-size;
	container-name: dfd-tier-card;
	position: relative;
	aspect-ratio: 480 / 620;
}

.dfd-tier-card-frame .dfd-tier-card {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}

.dfd-tier-card {
	/* cqi calibration base: 1cqi = 1% of the frame's inline size; at the
	   480px desktop frame width, 1cqi = 4.8px. Every value below states its
	   desktop px equivalent for the same reason the rest of the theme
	   comments a rem conversion. */
	--dfd-tier-card-radius: clamp(
		0.75rem,
		3.333cqi,
		1rem
	); /* 12px → 16px (Figma mobile card is 14px/342px ≈ 3.3cqi at that width anyway — this just extends it continuously). Gate round 1 (item 6): re-checked the --kd-radius-* scale for a 16px match — sm=4/md=12/lg=24/pill=999/panel=34/pillar=10, none is 16px — kept literal, flagged again here. */
	position: relative;
	overflow: hidden; /* clips the skyline ghost + gradient to the radius */
	display: flex;
	flex-direction: column;
	justify-content: space-between; /* matches Figma's own auto-layout: top row / billing / teaser distributed regardless of whether the name wraps 1 or 2 lines */
	box-sizing: border-box;
	border-radius: var(--dfd-tier-card-radius);
	padding: 7.5cqi; /* 36px @480 */
	background-image: linear-gradient(
		to bottom,
		var(--dfd-tier-grad-from),
		var(--dfd-tier-grad-to)
	);
}

/* Skyline ghost — bottom-anchored, 16% opacity, image-set() 1x/2x webp with
   a plain-png fallback (same two-layer technique as hero-campaign.css's
   raster background: a working image FIRST, then an @supports upgrade —
   never a later image-set() alone, which doesn't catch on a 404). Paints as
   the card's ::before so every `.dfd-tier-card` instance gets it with no <img>
   markup (purely decorative, no alt text needed). */
.dfd-tier-card::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	opacity: 0.16;
	pointer-events: none;
	background-repeat: no-repeat;
	background-position: center bottom; /* bottom-anchored per spec */
	background-size: cover; /* object-fit: cover equivalent for a CSS background */
	background-image: url("../../images/card-skyline-alpha.png");
}

@supports (background-image: image-set(url("a.jpg") 1x)) {
	.dfd-tier-card::before {
		background-image: image-set(
			url("../../images/card-skyline-alpha.webp") 1x type("image/webp"),
			url("../../images/card-skyline-alpha-2x.webp") 2x type("image/webp"),
			url("../../images/card-skyline-alpha.png") 1x type("image/png"),
			url("../../images/card-skyline-alpha-2x.png") 2x type("image/png")
		);
	}
}

/* Text sits above the ghost — no z-index needed, ::before paints first and
   these are its real DOM-order siblings, but set explicitly for clarity. */
.dfd-tier-card__top,
.dfd-tier-card__billing,
.dfd-tier-card__teaser {
	position: relative;
	z-index: 1;
	margin: 0;
	width: 100%;
}

.dfd-tier-card__top {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	font-family: var(--kd-font-body);
	font-weight: 700;
	font-size: clamp(
		0.6875rem,
		2.708cqi,
		0.8125rem
	); /* 11px floor (matches the old Figma mobile card) → 13px @480 ceiling */
	letter-spacing: 0.12em; /* 1.56/13 — Figma-verified, not the sitewide 3%/10%/-2% set; em, so it already scales with the cqi font-size above */
	text-transform: uppercase;
	white-space: nowrap;
}

.dfd-tier-card__kicker,
.dfd-tier-card__year {
	margin: 0;
	color: var(--dfd-tier-kicker-color);
}

.dfd-tier-card__billing {
	display: flex;
	flex-direction: column;
	gap: clamp(0.375rem, 1.667cqi, 0.5rem); /* 6px → 8px @480 */
}

.dfd-tier-card__name {
	/* Gate round 1 (item 11): the hero emits this as a <p> (five identical
	   "headings" cycling in and out reads oddly to a screen reader as
	   content headings; the standalone patterns/tier-card.php keeps a real
	   <h3>, since there it's the one genuine heading for that one card) —
	   so the DynaPuff `wdth` axis + drop-shadow suppression are declared
	   explicitly here instead of depending on the sitewide h1–h6 element
	   rule (global.css), which only fires on heading ELEMENTS. */
	font-variation-settings: "wdth" 100;
	font-family: var(--kd-font-heading);
	font-weight: 700;
	font-size: clamp(
		3.375rem,
		15.833cqi,
		4.75rem
	); /* 54px floor → 76px @480 ceiling — "stays legible at the 360 floor" per Alain: 15.833cqi of a 360-wide frame is already 57px, above the floor, so the floor only bites below ~341px (a true small-phone stacked width) */
	line-height: 0.9737; /* 74/76 — unitless so it tracks font-size automatically instead of needing its own cqi/clamp */
	letter-spacing: 0.02em; /* 1.52/76 — 2%, NOT the sitewide 3% heading tracking (Figma-verified); em, scales with font-size */
	color: var(
		--kd-color-contrast
	); /* navy fill, every tier — only the stroke/kicker/teaser colour changes */
	text-shadow: none; /* the sitewide h1-h6 rule's drop shadow is not part of this comp — explicit here since (in the hero) this isn't even a heading element for that rule to have applied to in the first place */
	-webkit-text-stroke: clamp(0.1875rem, 0.833cqi, 0.25rem)
		var(--kd-color-base); /* 3px → 4px @480 contour, per the SP1 brief */
	paint-order: stroke fill;
	margin: 0;
}

.dfd-tier-card__amount {
	font-family: var(--kd-font-heading);
	font-weight: 700;
	font-size: clamp(1.625rem, 7.5cqi, 2.25rem); /* 26px → 36px @480 */
	letter-spacing: 0.03em; /* 1.08/36 — 3%, matches --dfd-track-heading numerically but kept literal (component-local, not the shared token) */
	color: var(--kd-color-contrast);
	-webkit-text-stroke: clamp(0.125rem, 0.625cqi, 0.1875rem)
		var(--kd-color-base); /* 2px → 3px @480 contour */
	paint-order: stroke fill;
	margin: 0;
}

.dfd-tier-card__teaser {
	display: flex;
	flex-direction: column;
	gap: clamp(0.5rem, 2.5cqi, 0.75rem); /* 8px → 12px @480 */
}

.dfd-tier-card__teaser-text {
	font-family: var(--kd-font-body);
	font-weight: 400;
	font-size: clamp(0.875rem, 3.333cqi, 1rem); /* 14px → 16px @480 */
	line-height: 1.375; /* 22/16 — unitless */
	/* `color` is NOT set here — see the "Specificity fix" block below.
	   A single-class rule here is (0,1,0); on a real page this element
	   sits inside `.entry-content`, so it competes with global.css's
	   shared prose rules there. */
}

.dfd-tier-card__link {
	display: inline-block;
	font-family: var(--kd-font-body);
	font-weight: 700;
	font-size: clamp(0.8125rem, 3.125cqi, 0.9375rem); /* 13px → 15px @480 */
	/* `color` is NOT set here — same reason as `.dfd-tier-card__teaser-text`
	   above, except this one is an `a`, so it's the exact element
	   global.css's shared prose-link rule targets:
	   `.entry-content a:not(.wp-element-button):not([class*="kd-btn"])` is
	   (0,3,1) and always wins over this rule's (0,1,0) — see the
	   "Specificity fix" block below for the real fix. */
	text-decoration: none;
	border-radius: 0.1875rem;
}

.dfd-tier-card__link:hover {
	text-decoration: underline;
}

/* ── Specificity fix (SP8 gate finding, 2026-09-16) ───────────────────────
 * Confirmed live on `/sponsors/` (real `.entry-content` context, not a test
 * page): `.dfd-tier-card__link` computed navy on every card, including the
 * three dark ones (Headliner 1.37:1, Main Stage 3.00:1, Rising Star
 * 2.98:1 against their own gradient — invisible/failing). Same trap
 * logged in the tracker's F-section that already bit SP6/SP7:
 * global.css's `.entry-content a:not(.wp-element-button):not([class*=
 * "kd-btn"])` is (0,3,1) and beats this component's plain (0,1,0)
 * selectors regardless of source order. `.dfd-tier-card__teaser-text`
 * (a `<p>`, not an `a`) is not targeted by that specific rule and measured
 * correctly live at the same time — hardened anyway below, at the same
 * depth, purely so this component's own text colour never again depends
 * on winning a tie against whatever `.entry-content` rule exists when
 * some future page/pattern places it in a different wrapper.
 *
 * Fix: a real ancestor chain rooted in this component's OWN markup
 * contract (frame → card → teaser → element), not the consuming
 * pattern's wrapper — `sponsors-hero.php` is one consumer today, but the
 * file header's whole point is that ANY pattern emitting this markup gets
 * the same component "for free"; anchoring to `.sponsors-hero` would only
 * fix it there and leave `patterns/tier-card.php`'s own standalone example
 * (e.g. on the style-guide page, also inside `.entry-content`) broken.
 * Four real classes = (0,4,0), which beats (0,3,1) on class-count alone,
 * no `!important`, no `wp-element-button` (that class is theme.json's
 * button hook and would drag the yellow-pill treatment onto a text link —
 * the exact mistake the tracker already logs from the SP6 gate). Colour
 * stays the existing per-tier variable, so all five modifiers keep working
 * from this one rule. */
.dfd-tier-card-frame
	.dfd-tier-card
	.dfd-tier-card__teaser
	.dfd-tier-card__teaser-text,
.dfd-tier-card-frame
	.dfd-tier-card
	.dfd-tier-card__teaser
	.dfd-tier-card__link {
	color: var(--dfd-tier-teaser-color);
}

/* Hover/focus-visible colour, at the SAME (0,4,0) depth — the F-section
 * lesson bites here too: winning the resting colour above ALSO beats the
 * theme's low-specificity default `a:hover` (theme.json's
 * `elements.link.:hover`, effectively (0,0,1) via `:where()`), so without
 * this the link would silently stop changing colour on hover (underline
 * still would, since `text-decoration` isn't part of this fight — but the
 * colour wouldn't). Two groups, computed against each tier's own gradient
 * BOTTOM stop (where this element actually sits, per `justify-content:
 * space-between`):
 *   - Dark cards (Headliner #2e2466, Main Stage/Rising Star
 *     --dfd-color-purple-ink #5f58a3): yellow (`--kd-color-accent`)
 *     measures 10.07:1 / 4.61:1 / 4.61:1 — clears 4.5:1 on all three and
 *     matches the yellow SP6 already uses for its one dark highlighted
 *     card's hover state.
 *   - Light cards (Festival Friend/Opening Act, bottom #ffb5b5): no brand
 *     accent hue clears 4.5:1 against this specific pink (secondary
 *     purple 2.75:1, purple-ink 3.67:1 — both fail); the resting navy
 *     already measures 9.84:1, so hover/focus-visible re-assert the SAME
 *     `--dfd-tier-teaser-color` here rather than invent an ad-hoc colour
 *     that would fail contrast just to look "different" — underline +
 *     the focus ring below still carry the interaction feedback. */
.dfd-tier-card-frame
	.dfd-tier-card--headliner
	.dfd-tier-card__teaser
	.dfd-tier-card__link:hover,
.dfd-tier-card-frame
	.dfd-tier-card--headliner
	.dfd-tier-card__teaser
	.dfd-tier-card__link:focus-visible,
.dfd-tier-card-frame
	.dfd-tier-card--main-stage
	.dfd-tier-card__teaser
	.dfd-tier-card__link:hover,
.dfd-tier-card-frame
	.dfd-tier-card--main-stage
	.dfd-tier-card__teaser
	.dfd-tier-card__link:focus-visible,
.dfd-tier-card-frame
	.dfd-tier-card--rising-star
	.dfd-tier-card__teaser
	.dfd-tier-card__link:hover,
.dfd-tier-card-frame
	.dfd-tier-card--rising-star
	.dfd-tier-card__teaser
	.dfd-tier-card__link:focus-visible {
	color: var(
		--kd-color-accent
	); /* yellow — measured ≥4.5:1 on all three dark bottoms */
}

.dfd-tier-card-frame
	.dfd-tier-card--festival-friend
	.dfd-tier-card__teaser
	.dfd-tier-card__link:hover,
.dfd-tier-card-frame
	.dfd-tier-card--festival-friend
	.dfd-tier-card__teaser
	.dfd-tier-card__link:focus-visible,
.dfd-tier-card-frame
	.dfd-tier-card--opening-act
	.dfd-tier-card__teaser
	.dfd-tier-card__link:hover,
.dfd-tier-card-frame
	.dfd-tier-card--opening-act
	.dfd-tier-card__teaser
	.dfd-tier-card__link:focus-visible {
	color: var(
		--dfd-tier-teaser-color
	); /* navy, unchanged — see comment above */
}

/* Visible focus state (Alain, live review round 2) — every card's link is
   real and tabbable while its card is active (sponsors-hero.js manages
   `inert` on the inactive ones so tab order only ever reaches this one).
   Two-tone ring (navy outline, separated from the card by a white inner gap
   via box-shadow) rather than a single colour, because a single fixed
   colour can't read on both the light-bottomed cards (Festival Friend/
   Opening Act, pink) and the dark-bottomed ones (Headliner/Main Stage/
   Rising Star, navy/purple) — the white gap guarantees contrast against
   either. */
.dfd-tier-card__link:focus-visible {
	text-decoration: underline;
	outline: 2px solid var(--kd-color-contrast);
	outline-offset: 2px;
	box-shadow: 0 0 0 4px var(--kd-color-base);
}

/* ── Tier gradient + text-colour modifiers ────────────────────────────────
   Gradient stops now come from tokens.css's --dfd-tier-grad-* scale (gate
   round 1, pre-approved shared edit) — SP5/SP6 read the same tokens rather
   than each pattern hand-copying these hex values again.

   Kicker colour tracks the TOP gradient stop's contrast, teaser colour
   tracks the BOTTOM stop's — read directly off each Figma node (they are
   NOT both "purple = white / light = navy" as a single rule; Main Stage and
   Rising Star have a NAVY kicker over their light top stop but a WHITE
   teaser over their purple bottom stop). Billing text is navy on every
   card regardless (the white stroke is what keeps it legible on the dark
   tiers) — see figma get_design_context reads in the SP1 build notes.

   ⚠️ Headliner's kicker/year was WHITE on the Figma top stop (#8b82c7) —
   gate round 1 measured that at 3.43:1, below WCAG AA. Switched to navy
   (4.81:1 on the same top stop) — the only per-tier colour change made
   purely for contrast (Main Stage/Rising Star's fix lives in the gradient
   token itself, tokens.css, not here). ──────────────────────────────────── */
.dfd-tier-card--headliner {
	--dfd-tier-grad-from: var(--dfd-tier-grad-headliner-top);
	--dfd-tier-grad-to: var(--dfd-tier-grad-headliner-bottom);
	--dfd-tier-kicker-color: var(
		--kd-color-contrast
	); /* navy — was white, see comment above */
	--dfd-tier-teaser-color: var(
		--kd-color-base
	); /* white — bottom stop is navy #2e2466, unaffected */
}

.dfd-tier-card--main-stage {
	--dfd-tier-grad-from: var(--dfd-tier-grad-main-stage-top);
	--dfd-tier-grad-to: var(--dfd-tier-grad-main-stage-bottom);
	--dfd-tier-kicker-color: var(--kd-color-contrast); /* navy */
	--dfd-tier-teaser-color: var(--kd-color-base); /* white */
}

.dfd-tier-card--rising-star {
	--dfd-tier-grad-from: var(--dfd-tier-grad-rising-star-top);
	--dfd-tier-grad-to: var(--dfd-tier-grad-rising-star-bottom);
	--dfd-tier-kicker-color: var(--kd-color-contrast);
	--dfd-tier-teaser-color: var(--kd-color-base);
}

.dfd-tier-card--festival-friend {
	--dfd-tier-grad-from: var(--dfd-tier-grad-festival-friend-top);
	--dfd-tier-grad-to: var(--dfd-tier-grad-festival-friend-bottom);
	--dfd-tier-kicker-color: var(--kd-color-contrast);
	--dfd-tier-teaser-color: var(--kd-color-contrast);
}

.dfd-tier-card--opening-act {
	--dfd-tier-grad-from: var(--dfd-tier-grad-opening-act-top);
	--dfd-tier-grad-to: var(--dfd-tier-grad-opening-act-bottom);
	--dfd-tier-kicker-color: var(--kd-color-contrast);
	--dfd-tier-teaser-color: var(--kd-color-contrast);
}

/* No viewport media query left in this file — see the file header: every
   size above is cqi-driven off `.dfd-tier-card-frame`'s own rendered width, so
   the mobile scale (Figma `2082:2`, 342×442, r14, 26px padding) falls out
   of the same clamps rather than a second hand-authored copy. Verify: at a
   342px frame, radius clamps to 14.25px (≈14), name to ~57px (vs Figma's
   54 — the clamp floor is 54 but 15.833cqi of 342 is already 57.4, so the
   floor doesn't actually engage until ~341px; close enough at this exact
   width and flagged rather than hand-tuned further, since the point of
   this rework was continuous scaling, not re-matching one discrete frame).
   `prefers-reduced-motion` still has no role here — sponsors-hero.js never
   starts its cycler under it, so the PHP-default Headliner stays the only
   card ever un-hidden regardless of container size. */
