/* ═══════════════════════════════════════════════════════════════════════════════════════════
   CadPNG — design system
   ───────────────────────────────────────────────────────────────────────────────────────────
   One system for the public marketplace. Loaded LAST (after app.css and ui-refresh.css) so it can
   win ties, and namespaced `--c-*` so it never collides with the two older token sets it replaces.

   Phase ownership (a rule belongs here only when its phase has landed):
     UI.1  tokens · container · utilities · skeleton · focus  ← this file's first release
     UI.2  navigation shell          UI.3  mega-menu            UI.4  hero
     UI.7  image card                UI.8  gallery grid         UI.9  card hover
     UI.13 typography application    UI.14 spacing application  UI.15 search
     UI.16 category chips            UI.17 loading states       UI.18 image page
    UI.20 responsive pass (nine widths, touch targets)
    UI.21 font-loading stability (metric-matched fallback — §17)
    UI.22 accessibility (contrast fixes in §1 tokens and §14 spec labels)

   Rules for every later phase:
     • No new hard-coded colours, sizes or shadows in a Blade file — use a token from this file.
     • No `!important` unless a third-party rule is being defeated, and then with a comment saying which.
     • Every animation must be expressible with the motion tokens below and must collapse under
       `prefers-reduced-motion: reduce`.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */

/* ── 1. Tokens ────────────────────────────────────────────────────────────────────────────── */
:root {
    /* Brand — the CadPNG blue family. 600 is the brief's interactive blue; 700 is the blue blade
       measured from the logo (#085098). Links, focus and active states live in this ramp. */
    --c-brand-50:  #eff6fc;
    --c-brand-100: #dcedf8;
    --c-brand-200: #b9dcf2;
    --c-brand-300: #84c2e8;
    --c-brand-400: #47a3da;
    --c-brand-500: #1e85c8;
    --c-brand-600: #0067a8;
    --c-brand-700: #085098;
    --c-brand-800: #0c3f76;
    --c-brand-900: #123b63;   /* navy-light: pairs with ink-900 inside --c-band-dark */

    /* Accent — the logo's warm family (red/orange/yellow), used sparingly for attention:
       the Royalty-Free badge, highlight moments, never whole sections. */
    --c-accent-100: #fff3e8;   /* warm pill fill */
    --c-accent-300: #ffc4a3;
    --c-accent-500: #f84810;   /* the logo's orange */
    --c-accent-700: #d93408;
    --c-accent-800: #b44008;   /* the badge label: 5.70:1 on white, 5.24:1 over the pill fill
                                  (the brief's #c9480c measures 4.36:1 there — fails AA at 12px) */
    --c-star: #f59e0b;         /* rating stars stay semantic amber (Brand module §17) */

    /* Neutral ramp — cool navy-tinted greys from the logo and brief (§1–§2). */
    --c-ink-900: #0b1f33;   /* headline ink — the CadPNG navy */
    --c-ink-800: #21374b;   /* strong ink */
    --c-ink-700: #3d5265;   /* strong secondary */
    --c-ink-600: #526578;   /* secondary text */
    --c-ink-500: #667085;   /* body text (brief §2): 4.98:1 on white, 4.66:1 on the canvas,
                               4.55:1 on the band — WCAG AA at the 15px body step */
    --c-ink-400: #98a2b3;   /* placeholders/disabled hints only — too quiet for copy */
    --c-ink-300: #bec9d3;   /* disabled */
    --c-ink-200: #dce3ea;   /* borders */
    --c-ink-100: #eef3f8;   /* subtle fills — alternate band, table headers */
    --c-ink-50:  #f7f9fc;   /* page canvas */
    --c-white:   #ffffff;

    /* Semantic aliases — prefer these in new work. */
    --c-text: var(--c-ink-500);   /* body copy follows the brief's #667085 */
    --c-text-muted: var(--c-ink-500);
    --c-heading: var(--c-ink-900);
    --c-border: var(--c-ink-200);
    --c-border-strong: var(--c-ink-300);
    --c-surface: var(--c-white);
    --c-surface-sunken: var(--c-ink-50);
    --c-canvas: var(--c-ink-50);
    --c-focus: #159fe3;   /* brief §13: the bright-blue focus border */
    --c-danger: #c00d0d;   /* error text/borders — 7.4:1 on white */
    --c-success: #177a3d;   /* conventional green per §17 — 5.38:1 on white */
    --c-warning: #946200;   /* UI.22: #c47f12 measured 3.10:1 as text on --c-warning-soft (the "Extended"
                               licence flag) and failed WCAG AA; this measures 4.95:1 there, 5.24:1 on white.
                               Used only as text/icon colour on light warm fills. */
    --c-warning-soft: #fff8e6;   /* notice fills: warm, quiet, never alarming */
    --c-warning-line: #f0d9a8;   /* …with a line that reads as a warning at a glance */

    /* Type — one scale, one weight set (Phase UI.13).
       The fluid steps name the sizes the components already ship, measured rather than invented:
       the hero display is the largest size at which "Endless possibilities." still fits one line in
       the copy column at 1200px (UI.3); h2 is the section-head pattern the homepage was verified
       against (UI.12); h1 is the inner-page title. Naming them here means a page can no longer
       quietly invent a 21px heading — every level resolves to one of these steps. */
    --c-font-sans: 'Inter', 'Inter Fallback', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --c-fs-display: clamp(2.25rem, 4.6vw, 3.9rem);    /* 36 → 62.4px · hero headline */
    --c-fs-h1: clamp(2rem, 3.2vw, 2.875rem);          /* 32 → 46px  · page title */
    --c-fs-h2: clamp(1.5rem, 2.4vw, 2.05rem);         /* 24 → 32.8px · section title */
    --c-fs-h3: clamp(1.25rem, 1.6vw, 1.5rem);         /* 20 → 24px  · subsection */
    --c-fs-h4: 1.125rem;     /* 18px — card / item title */
    --c-fs-h5: 1rem;         /* 16px — minor heading */
    --c-fs-h6: 0.75rem;      /* 12px — label / eyebrow */
    --c-fs-lg: 1.0625rem;    /* 17px — lead paragraphs */
    /* The body step is 15px, deliberately and not by drift: every card, deck, grid and section
       measurement in UI.1–UI.12 was taken at 15px/1.65, the pages are card-dense rather than
       prose-dense, and 15px Inter at that leading is comfortably legible. Changing it would reflow
       twelve phases of verified layout for no readability gain, so the token adopts the shipped
       value and the shipped value adopts the token. */
    --c-fs-body: 0.9375rem;  /* 15px */
    --c-fs-sm: 0.875rem;     /* 14px */
    --c-fs-xs: 0.8125rem;    /* 13px */
    --c-fs-label: 0.75rem;   /* 12px — uppercase labels */
    --c-fw-regular: 400;
    --c-fw-medium: 500;
    --c-fw-semibold: 600;
    --c-fw-bold: 700;
    --c-fw-black: 800;       /* display, h1 and h2 only — 850/900 are not part of the system */
    --c-lh-tight: 1.08;
    --c-lh-display: 1.05;
    --c-lh-heading: 1.22;
    --c-lh-body: 1.65;
    --c-tracking-display: -0.02em;
    --c-tracking-heading: -0.02em;
    --c-tracking-label: 0.08em;

    /* Space — the only permitted gaps and paddings. 4 → 96. */
    --c-space-1: 4px;
    --c-space-2: 8px;
    --c-space-3: 12px;
    --c-space-4: 16px;
    --c-space-5: 24px;
    --c-space-6: 32px;
    --c-space-7: 48px;
    --c-space-8: 64px;
    --c-space-9: 96px;
    /* Two intermediate steps the UI.14 audit surfaced as heavily used (20px in 34 declarations, 40px in
       35) — both on the 4px grid, both inside 4→96. The numbered family above keeps its names: twelve
       verified phases reference it, so renumbering to make room would invalidate evidence for nothing. */
    --c-space-20: 20px;
    --c-space-40: 40px;
    --c-space-hairline: 2px;  /* optics only — 1px/2px nudges, borders, focus offsets */
    --c-section-y: clamp(3rem, 6vw, 6rem);      /* vertical rhythm between homepage sections */
    /* Section system (Phase UI.12): one rhythm, three bands, one head pattern. */
    --c-section-y-tight: clamp(2.5rem, 4.5vw, 4rem);   /* a band that continues the one above it */
    --c-section-head-gap: var(--c-space-6);           /* head block → the section's content */
    --c-section-title-gap: var(--c-space-2);          /* eyebrow / title / lede spacing */
    --c-band-soft: var(--c-ink-50);                    /* the tinted band, from the palette, not a new hex */
    --c-band-dark: linear-gradient(135deg, var(--c-ink-900), var(--c-brand-900));   /* navy → navy-light, self-cascades */
    /* CadPNG identity aliases + the two reusable gradients (Brand module §8) — decorative use only:
       hero objects, small promos, active accents. Never a page background. */
    --cad-navy: var(--c-ink-900);
    --cad-red: #f41717;         /* the blade red — white text on it measures 4.55:1 (AA) */
    --cad-red-dark: #c00d0d;
    --cad-gradient-warm: linear-gradient(120deg, #f00008 0%, #f84810 50%, #f8c810 100%);
    --cad-gradient-blue: linear-gradient(135deg, #0b1f33 0%, #085098 55%, #28b8ee 100%);
    --c-deck-gap: var(--c-space-4);                    /* category / creator / step decks */
    --c-gutter: var(--c-space-5);
    --c-gutter-sm: var(--c-space-4);

    /* Layout — the client asked for 1400–1600px of usable width on large screens. */
    --c-container: 1400px;
    --c-container-wide: 1560px;
    --c-container-narrow: 760px;
    --c-nav-h: 72px;
    --c-nav-h-sm: 64px;

    /* Radius */
    --c-radius-xs: 6px;
    --c-radius-sm: 8px;
    --c-radius-md: 12px;
    --c-radius-lg: 16px;
    --c-radius-xl: 20px;
    --c-radius-2xl: 28px;
    --c-radius-pill: 999px;

    /* Elevation — four steps, no more. */
    --c-shadow-xs: 0 1px 2px rgb(11 31 51 / 6%);
    --c-shadow-sm: 0 2px 8px rgb(11 31 51 / 6%);
    --c-shadow-md: 0 8px 24px rgb(11 31 51 / 8%);
    --c-shadow-lg: 0 18px 48px rgb(11 31 51 / 12%);
    --c-shadow-xl: 0 32px 80px rgb(11 31 51 / 18%);
    --c-shadow-focus: 0 0 0 3px rgb(21 159 227 / 22%);   /* brief §13 ring — bright enough to read at 3px */
    --c-ring: 3px;

    /* Motion — a marketplace must feel fast. 150–500ms for UI, longer only for hero sequences. */
    /* Hover — one tempo and one step for every hover state on the site (Phase UI.9). */
    --c-hover-dur: var(--c-dur-base);       /* 220ms: fast enough to feel instant, slow enough to read */
    --c-hover-lift: 2px;                    /* the most anything may move under the pointer */
    --c-hover-shadow: var(--c-shadow-md);   /* exactly one elevation step, never a glow */
    --c-hover-line: var(--c-brand-300);
    --c-hover-surface: var(--c-brand-50);

    --c-dur-instant: 90ms;
    --c-dur-fast: 150ms;
    --c-dur-base: 220ms;
    --c-dur-slow: 320ms;
    --c-dur-slower: 480ms;
    --c-ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
    --c-ease-entrance: cubic-bezier(0.16, 1, 0.3, 1);
    --c-ease-exit: cubic-bezier(0.4, 0, 1, 1);
    --c-motion-rise: 14px;   /* how far a revealing element travels; read by motion.js too */

    /* Z-index — one ladder, so nothing fights. */
    --c-z-base: 1;
    --c-z-sticky: 1020;
    --c-z-dropdown: 1030;
    --c-z-overlay: 1040;
    --c-z-modal: 1050;
    --c-z-toast: 1060;

    /* Bridge: keep the older token names pointing at the new values so existing pages inherit the
       system without being rewritten in one go. Removed once the last legacy rule is migrated. */
    --ui-violet: var(--c-brand-600);
    --ui-violet-soft: var(--c-brand-50);
    --ui-ink: var(--c-ink-800);
    --ui-muted: var(--c-ink-500);
    --ui-line: var(--c-border);
    --ui-canvas: var(--c-canvas);
    --ui-paper: var(--c-white);
    --ui-radius: var(--c-radius-lg);
    --ui-shadow: var(--c-shadow-sm);
    --brand: var(--c-brand-600);
    --brand-2: var(--c-accent-500);
    --ink: var(--c-ink-800);
    --muted: var(--c-ink-500);
    --line: var(--c-border);
    --surface: var(--c-canvas);
}

/* ── 2. Layout primitives ─────────────────────────────────────────────────────────────────── */
/* Bootstrap's .container tops out at 1320px; the marketplace needs the extra column.
   Only the public shell is widened — panels keep their denser measure.

   Specificity note: ui-refresh.css caps the public container with `.ui-refresh.public-body .container`
   (three classes) inside the same 1400px media query, so a two-class selector loses no matter how late
   it loads. This rule deliberately matches that weight and then wins on source order. */
@media (min-width: 1400px) {
    .public-body.ui-refresh .container {
        max-width: var(--c-container);
    }
}

@media (min-width: 1600px) {
    .public-body.ui-refresh .container {
        max-width: var(--c-container-wide);
    }
}

/* Bootstrap's own gutters (12px, and 24px at ≥1400) are not our scale. Both container systems must
   resolve identically, or the same page type sits at a different inset depending on which class its
   template happened to use — that was true for 39 templates vs 1. One gutter, two names. */
.public-body.ui-refresh .container,
.public-body.ui-refresh .container-fluid {
    padding-inline: var(--c-gutter-sm);
}

@media (min-width: 992px) {
    .public-body.ui-refresh .container,
    .public-body.ui-refresh .container-fluid {
        padding-inline: var(--c-gutter);
    }
}

/* One container system. `.c-container` is the design-system name and `.container` the Bootstrap one, but
   they must resolve to the same box at every width or a page's measure depends on which class its template
   happened to pick — the homepage hero (.c-container) was full-bleed at 1366 while every section around it
   (.container) sat at 1140. The ladder below mirrors Bootstrap's breakpoints exactly and then takes over at
   1400 where the system's own container steps live; the gutters are our scale, never Bootstrap's 12px. */
.c-container {
    width: 100%;
    margin-inline: auto;
    padding-inline: var(--c-gutter-sm);
}

@media (min-width: 576px) {
    .c-container { max-width: 540px; }
}

@media (min-width: 768px) {
    .c-container { max-width: 720px; }
}

@media (min-width: 992px) {
    .c-container { max-width: 960px; padding-inline: var(--c-gutter); }
}

@media (min-width: 1200px) {
    .c-container { max-width: 1140px; }
}

@media (min-width: 1400px) {
    .c-container { max-width: var(--c-container); }
}

@media (min-width: 1600px) {
    .c-container { max-width: var(--c-container-wide); }
}

/* The phone gutter is one step tighter than the 16px small gutter; it is a *scale* value, not the old
   15px/20px one-offs ui-refresh.css carried. It sits after the block above so it wins on source order. */
@media (max-width: 575.98px) {
    .c-container,
    .public-body.ui-refresh .container,
    .public-body.ui-refresh .container-fluid {
        padding-inline: var(--c-space-3);
    }
}

.c-section { padding-block: var(--c-section-y); }
.c-section--tight { padding-block: var(--c-space-7); }

/* A stacking utility with a single source of vertical rhythm. */
.c-stack > * + * { margin-top: var(--c-stack-gap, var(--c-space-4)); }
.c-stack--sm { --c-stack-gap: var(--c-space-2); }
.c-stack--lg { --c-stack-gap: var(--c-space-6); }

/* Horizontal cluster that wraps instead of overflowing — the fix for chip rows and toolbars. */
.c-cluster {
    display: flex;
    flex-wrap: wrap;
    gap: var(--c-cluster-gap, var(--c-space-3));
    align-items: center;
}

/* ── 3. Typography system (Phase UI.13) ────────────────────────────────────────────────────────

   One scale, one weight set, one wrapping policy.

   **The scale** lives in the tokens above. Before this phase the eight measured public pages used
   fifteen distinct text sizes, five heading weights and an h3 that rendered larger than its own h2;
   nothing was wrong with any single page, but there was no system to be wrong *against*. Now every
   heading level resolves to one step, so the drift has nowhere to happen.

   **The wrapping policy** is the client's original defect. A headline breaking inside words
   ("Sell you / r / wor / k") is what a blanket `overflow-wrap: anywhere` does: it lets a box shrink
   to one character wide and then cheerfully breaks the word to fit. Prose uses the normal rules
   instead — `word-break: normal`, `overflow-wrap: normal`, `white-space: normal` — so text breaks at
   spaces and the longest word sets the floor a container may not go below. The single exception is a
   token with no spaces in it at all (a 40-character id, an email): those get `break-word`, which
   breaks only a word that cannot fit on a line by itself and never shrinks a container.
   ───────────────────────────────────────────────────────────────────────────────────────────── */

.c-display, .c-h1, .c-h2, .c-h3, .c-h4, .c-h5, .c-h6 {
    font-family: var(--c-font-sans);
    color: var(--c-heading);
    word-break: normal;
    overflow-wrap: normal;
    hyphens: none;
}

.c-display { font-size: var(--c-fs-display); font-weight: var(--c-fw-black); line-height: var(--c-lh-display); letter-spacing: var(--c-tracking-display); }
.c-h1 { font-size: var(--c-fs-h1); font-weight: var(--c-fw-black); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
.c-h2 { font-size: var(--c-fs-h2); font-weight: var(--c-fw-black); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
.c-h3 { font-size: var(--c-fs-h3); font-weight: var(--c-fw-bold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
.c-h4 { font-size: var(--c-fs-h4); font-weight: var(--c-fw-bold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
.c-h5 { font-size: var(--c-fs-h5); font-weight: var(--c-fw-semibold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
.c-h6 { font-size: var(--c-fs-h6); font-weight: var(--c-fw-semibold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-label); text-transform: uppercase; }

/* Bootstrap ships a complete second heading scale on the bare elements — `h3` is
   `calc(1.3rem + .6vw)` and `h4` is `1.5rem` above 1200px, with `font-weight: 500` on all six
   levels. So a heading written without a class fell into Bootstrap's scale instead of ours: the
   eight measured pages were rendering 21, 22, 23, 24, 25, 27 and 28px headings that matched
   Bootstrap's fluid steps exactly. The rules below carry the same specificity as Bootstrap's
   element selectors and load later, so a bare heading lands on our scale — while every component
   class still wins, because a class beats an element selector. That is the whole trick: our
   scale becomes the floor, not another layer fighting for the top. */
:where(body.ui-refresh) h1,
:where(body.ui-refresh) .h1 { font-size: var(--c-fs-h1); font-weight: var(--c-fw-black); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
:where(body.ui-refresh) h2,
:where(body.ui-refresh) .h2 { font-size: var(--c-fs-h2); font-weight: var(--c-fw-black); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
:where(body.ui-refresh) h3,
:where(body.ui-refresh) .h3 { font-size: var(--c-fs-h3); font-weight: var(--c-fw-bold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
:where(body.ui-refresh) h4,
:where(body.ui-refresh) .h4 { font-size: var(--c-fs-h4); font-weight: var(--c-fw-bold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
:where(body.ui-refresh) h5,
:where(body.ui-refresh) .h5 { font-size: var(--c-fs-h5); font-weight: var(--c-fw-semibold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-heading); }
:where(body.ui-refresh) h6,
:where(body.ui-refresh) .h6 { font-size: var(--c-fs-h6); font-weight: var(--c-fw-semibold); line-height: var(--c-lh-heading); letter-spacing: var(--c-tracking-label); }

/* Bootstrap's font-size utilities are a set of sizes *outside* the scale — `.fs-1` is 2.5rem, `.fs-5`
   is 1.25rem (20px, a step the scale does not have), and the templates use them 59 times. They are
   remapped onto the steps rather than deleted from the markup: one rule per utility means the scale
   is enforced in one place, and a page that reaches for `fs-4` gets the step the system owns instead
   of a fourth opinion. Each rule ties Bootstrap's specificity and wins on order. */
:where(body.ui-refresh) .fs-1, :where(body.ui-refresh) .display-6 { font-size: var(--c-fs-h1); }
:where(body.ui-refresh) .fs-2, :where(body.ui-refresh) .display-5 { font-size: var(--c-fs-h2); }
:where(body.ui-refresh) .fs-3, :where(body.ui-refresh) .display-4 { font-size: var(--c-fs-h3); }
:where(body.ui-refresh) .fs-4 { font-size: var(--c-fs-h3); }
:where(body.ui-refresh) .fs-5 { font-size: var(--c-fs-h4); }
:where(body.ui-refresh) .fs-6 { font-size: var(--c-fs-h5); }

/* The baseline for every public page: the raw element carries the level's meaning even where no
   class is present, so a legacy template cannot reintroduce a fifth heading weight. Components
   declared later in this file (the hero, the section head) override deliberately — they are not
   exceptions to the scale, they are named steps of it. */
.ui-refresh :where(h1, h2, h3, h4, h5, h6) {
    word-break: normal;
    overflow-wrap: normal;
    white-space: normal;
}

.ui-refresh :where(p, li, dd, dt, label, figcaption, blockquote, td, th, summary, small, legend) {
    word-break: normal;
    overflow-wrap: normal;
    white-space: normal;
}

/* The honest exception: strings that contain no space at all. `break-word` only engages when the
   word cannot fit on a line of its own, and unlike `anywhere` it does not shrink the box. */
.ui-refresh :where(code, kbd, samp, .c-token-break, [data-break-token]) {
    overflow-wrap: break-word;
}

.c-lead { font-size: var(--c-fs-lg); line-height: var(--c-lh-body); color: var(--c-text-muted); }
.c-muted { color: var(--c-text-muted); }
.c-label {
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    letter-spacing: var(--c-tracking-label);
    text-transform: uppercase;
    color: var(--c-ink-500);
}

/* ══ PHASE UI.2 — Navigation shell ══════════════════════════════════════════════════════════
   The header is the first thing a visitor reads, so it carries the design system's weight: one
   height token, one hover language, one focus treatment, and no inline styles anywhere.

   Ownership: this file owns every `#mainNavbar` / public `.nav-link` rule. The matching legacy
   rules were deleted from ui-refresh.css in this phase rather than overridden — fighting an
   `!important` with another `!important` is how the previous three-layer mess was built.
   ══════════════════════════════════════════════════════════════════════════════════════════ */

.c-navbar {
    position: sticky;
    top: 0;
    z-index: var(--c-z-sticky);
    background: rgb(255 255 255 / 88%);
    border-bottom: 1px solid transparent;
    backdrop-filter: saturate(180%) blur(18px);
    -webkit-backdrop-filter: saturate(180%) blur(18px);
    transition: background var(--c-dur-base) var(--c-ease-standard),
                border-color var(--c-dur-base) var(--c-ease-standard),
                box-shadow var(--c-dur-base) var(--c-ease-standard);
}

/* Set by navbar.js once the page is scrolled: the bar firms up instead of the old inline-style
   mutation, so the appearance is decided in CSS and can be tested in the stylesheet. */
.c-navbar.is-scrolled {
    background: rgb(255 255 255 / 97%);
    border-bottom-color: var(--c-border);
    box-shadow: var(--c-shadow-sm);
}

.c-navbar__inner {
    display: flex;
    align-items: center;
    gap: var(--c-space-4);
    min-height: var(--c-nav-h);
}

/* ── Brand ── */
.c-navbar__brand {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-3);
    flex: 0 0 auto;
    font-size: var(--c-fs-h3);
    font-weight: var(--c-fw-black);
    letter-spacing: -0.04em;
    color: var(--c-heading);
    text-decoration: none;
    white-space: nowrap;
}

.c-navbar__brand:hover { color: var(--c-brand-700); }

.c-navbar__mark {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: var(--c-radius-md);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    color: #fff;
    font-size: var(--c-fs-h5);
    box-shadow: 0 6px 16px rgb(93 69 212 / 28%);
    flex: 0 0 auto;
}

/* Admin-uploaded logo keeps its own proportions. */
.c-navbar__brand img { max-height: 40px; width: auto; }

/* ── Desktop links ── */
/* Bootstrap hides `.collapse:not(.show)` and only re-shows it for `.navbar-expand-*` markup. This
   navbar deliberately carries no `navbar-expand-*` class (the old bar did, which is also what gave it
   its stock Bootstrap look), so the desktop state is declared here. `!important` matches the
   specificity Bootstrap's own `.navbar-expand-lg .navbar-collapse { display:flex !important }` uses —
   nothing is being out-shouted, the same declaration is simply owned by the design system. */
@media (min-width: 992px) {
    .c-navbar__collapse {
        display: flex !important;
        flex-basis: auto;
        flex-grow: 1;
        align-items: center;
        gap: var(--c-space-4);
    }

}

.c-navbar__links {
    display: flex;
    align-items: center;
    gap: var(--c-space-1);
    /* Centred in the leftover space between the brand and the account cluster — see the desktop
       layout note at the end of this section. */
    margin: 0 auto;
    padding: 0;
    list-style: none;
}

.c-nav-link {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-1);
    padding: var(--c-space-2) var(--c-space-3);
    border: 0;
    background: transparent;   /* the More toggle is a <button>; it must not inherit the UA button look */
    font-family: inherit;
    border-radius: var(--c-radius-sm);
    color: var(--c-ink-600);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
    /* The defect from the client's screenshot: nav and menu text must never break inside a word. */
    word-break: normal;
    overflow-wrap: normal;
    hyphens: none;
    transition: color var(--c-dur-fast) var(--c-ease-standard),
                background-color var(--c-dur-fast) var(--c-ease-standard);
}

.c-nav-link:hover,
.c-nav-link:focus-visible {
    color: var(--c-brand-700);
    background: var(--c-brand-50);
}

.c-nav-link.is-active {
    color: var(--c-brand-700);
    background: var(--c-brand-50);
}

.c-nav-link__caret {
    font-size: var(--c-fs-label);
    line-height: 1;
    transition: transform var(--c-dur-fast) var(--c-ease-standard);
}

/* Bootstrap draws a caret with `.dropdown-toggle::after`. The navbar ships its own icon (which can
   rotate and can be seen by the `aria-expanded` state), so the pseudo-element is removed here —
   without this the collapsed panel showed two carets side by side. */
.c-navbar .dropdown-toggle::after { display: none; }

.c-nav-link[aria-expanded="true"] .c-nav-link__caret { transform: rotate(180deg); }

/* ── Menu panel positioning ────────────────────────────────────────────────────────────────────
   `data-bs-display="static"` on the toggle means Bootstrap applies `.dropdown-menu[data-bs-popper]`
   (`top:100%; left:0`) instead of letting Popper write inline coordinates on every scroll. Both
   halves of that contract are restated here so the component does not depend on Bootstrap's
   stylesheet having loaded. */
.c-navbar__links .dropdown { position: relative; }

.c-menu[data-bs-popper] {
    top: calc(100% + var(--c-space-2));
    /* Aligned to the right edge of its trigger: the trigger sits mid-bar, so a left-aligned 320px
       panel runs off the viewport between 992 and ~1150px. */
    left: auto;
    right: 0;
    margin-top: 0;
}

/* ── Right-hand actions ── */
.c-navbar__actions {
    display: flex;
    align-items: center;
    gap: var(--c-space-2);
    flex: 0 0 auto;
}

.c-navbar__action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--c-space-1);
    min-height: 40px;
    padding: 0 var(--c-space-4);
    border: 1px solid transparent;
    border-radius: var(--c-radius-pill);
    background: transparent;
    color: var(--c-ink-700);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--c-dur-fast) var(--c-ease-standard),
                background-color var(--c-dur-fast) var(--c-ease-standard),
                border-color var(--c-dur-fast) var(--c-ease-standard),
                box-shadow var(--c-dur-fast) var(--c-ease-standard);
}

.c-navbar__action:hover { color: var(--c-brand-700); background: var(--c-brand-50); }

.c-navbar__action--primary {
    background: var(--c-brand-600);
    color: #fff;
    box-shadow: 0 6px 16px rgb(93 69 212 / 24%);
}

.c-navbar__action--primary:hover {
    background: var(--c-brand-700);
    color: #fff;
    transform: translateY(-1px);
}

/* ── Mobile toggler ── */
.c-navbar__toggler {
    display: none;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-left: auto;
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-md);
    background: var(--c-surface);
    color: var(--c-ink-700);
    font-size: var(--c-fs-h4);
    cursor: pointer;
    transition: border-color var(--c-dur-fast) var(--c-ease-standard),
                background-color var(--c-dur-fast) var(--c-ease-standard);
}

.c-navbar__toggler:hover { border-color: var(--c-brand-300); background: var(--c-brand-50); }

.c-navbar__toggler .c-navbar__toggler-open,
.c-navbar__toggler .c-navbar__toggler-close { display: block; line-height: 1; }

.c-navbar__toggler .c-navbar__toggler-close { display: none; }
.c-navbar__toggler[aria-expanded="true"] .c-navbar__toggler-open { display: none; }
.c-navbar__toggler[aria-expanded="true"] .c-navbar__toggler-close { display: block; }

/* ── Mobile panel (≤ lg) ── */
@media (max-width: 991.98px) {
    .c-navbar__inner {
        min-height: var(--c-nav-h-sm);
        /* Brand + toggler on the first row, the panel on the second: without this the collapsed
           panel is squeezed into whatever space is left beside the brand (156px at 390px wide). */
        flex-wrap: wrap;
    }

    .c-navbar__toggler { display: inline-flex; }

    .c-navbar__collapse {
        flex-basis: 100%;
        padding: var(--c-space-3) 0 var(--c-space-5);
        border-top: 1px solid var(--c-border);
        margin-top: var(--c-space-3);
    }

    .c-navbar__links {
        flex-direction: column;
        align-items: stretch;
        gap: var(--c-space-hairline);
        margin: 0 0 var(--c-space-3);
    }

    /* Each row is one tappable target spanning the panel: the <li> stretches, so the link inside it
       has to be told to fill it (an inline-flex anchor sizes to its text). */
    .c-navbar__links > li { width: 100%; }

    .c-nav-link {
        justify-content: space-between;
        width: 100%;
        padding: 14px var(--c-space-3);
        border-radius: var(--c-radius-md);
        font-size: var(--c-fs-h5);
    }

    .c-navbar__actions {
        flex-direction: column;
        align-items: stretch;
        gap: var(--c-space-2);
        padding-top: var(--c-space-3);
        border-top: 1px solid var(--c-border);
    }

    .c-navbar__action { min-height: 48px; border-radius: var(--c-radius-md); }

    .c-navbar__action--ghost { border-color: var(--c-border); }

    /* Inside the collapsed panel the menu is part of the flow: it can never overlap the viewport
       edge, and it reads as a nested list rather than a floating card. */
    .c-navbar__links .dropdown { position: static; }

    .c-menu[data-bs-popper] {
        position: static;
        width: auto;
        max-width: none;
        min-width: 0;
        margin: var(--c-space-2) 0 var(--c-space-2);
        border-color: var(--c-border);
        box-shadow: none;
    }

    .c-menu__item { padding: var(--c-space-3) var(--c-space-3); }

    .c-menu__title { font-size: var(--c-fs-body); white-space: normal; }

    /* Mega-menu inside the collapsed panel: full width, same rows, no chevron (there is nothing to
       the right of it) and a two-line description limit that the narrow screen needs most. */
    .c-mega { width: auto; }
    .c-mega__item { grid-template-columns: 36px 1fr; }
    .c-mega__arrow { display: none; }
    .c-mega__foot { flex-wrap: wrap; row-gap: var(--c-space-2); }}

/* ── Desktop layout note ───────────────────────────────────────────────────────────────────────
   The link group is centred in the space the brand and the account cluster leave over. True
   container-centring was implemented and rejected during UI.2 verification: it needs the collapsed
   wrapper to be `display: contents` (so the actions can occupy the third grid track) and strictly
   equal tracks, and between 992 and ~1280px the two account pills are wider than an equal track —
   the tracks then grow unevenly and the links drift, or the pills overlap the links. Centring on the
   leftover space is stable from 992px to 1920px, keeps every group in its own column, and is what the
   large stock marketplaces do. */

/* ── 4b. Menu panel baseline (Phase UI.2) ──────────────────────────────────────────────────────
   The panel itself is rebuilt as the full mega-menu in UI.3. What lands here is the *defect*:
   ui-refresh.css applies `overflow-wrap: anywhere` to every `li`, and Bootstrap menu items are
   `li`s, so titles broke character-by-character ("Sell / you / r / wor / k"). The menu now
   declares its own wrapping and a deliberate width so no future phase can regress it.
   ────────────────────────────────────────────────────────────────────────────────────────────── */
.c-menu {
    min-width: 320px;
    max-width: min(420px, calc(100vw - 2 * var(--c-space-4)));
    margin-top: var(--c-space-2);
    padding: var(--c-space-2);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-lg);
    box-shadow: var(--c-shadow-lg);
    background: var(--c-surface);
    /* The fix, stated once, scoped to menus: text may wrap between words, never inside one. */
    word-break: normal;
    overflow-wrap: normal;
    hyphens: none;
}

.c-menu__item {
    display: block;
    padding: var(--c-space-3) var(--c-space-4);
    border-radius: var(--c-radius-md);
    color: var(--c-ink-700);
    white-space: normal;
    word-break: normal;
    overflow-wrap: normal;
    hyphens: none;
}

.c-menu__item + .c-menu__item { margin-top: var(--c-space-hairline); }

.c-menu__item:hover,
.c-menu__item:focus,          /* keyboard.js moves focus here when the panel opens from the keyboard */
.c-menu__item:focus-visible {
    background: var(--c-brand-50);
    color: var(--c-brand-700);
}

.c-menu__item:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: -2px; }

.c-menu__title {
    display: block;
    font-size: var(--c-fs-body);
    font-weight: var(--c-fw-semibold);
    line-height: 1.35;
    white-space: nowrap;
}

.c-menu__hint {
    display: block;
    margin-top: var(--c-space-hairline);
    color: var(--c-text-muted);
    font-size: var(--c-fs-xs);
    font-weight: var(--c-fw-regular);
    line-height: 1.45;
}

/* ── 4c. Mega-menu (Phase UI.3) ──────────────────────────────────────────────────────────────
   The "More" panel as a real mega-menu: an eyebrow, five rows of icon + title + description, and a
   compact support footer. Width is deliberate (380px, inside the 320–450px the brief asks for) so
   every description has room to flow between words and nobody has to read text broken mid-word.
   One owner: these rules are the only ones that style the panel; nothing here uses `!important`.
   ────────────────────────────────────────────────────────────────────────────────────────────── */
.c-mega {
    width: 380px;
    max-width: min(420px, calc(100vw - 2 * var(--c-space-4)));
    padding: var(--c-space-2);
    z-index: var(--c-z-dropdown);
}

/* The eyebrow is a label, not a link — it is referenced by the list's aria-labelledby. */
.c-mega__eyebrow {
    margin: 0;
    padding: var(--c-space-2) var(--c-space-3) var(--c-space-1);
    color: var(--c-text-muted);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.c-mega__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.c-mega__item {
    display: grid;
    grid-template-columns: 36px 1fr 16px;
    align-items: center;
    column-gap: var(--c-space-3);
    padding: var(--c-space-2) var(--c-space-3);
    border-radius: var(--c-radius-md);
    min-height: 56px;
}

.c-mega__item:hover,
.c-mega__item:focus,
.c-mega__item:focus-visible { background: var(--c-brand-50); }

.c-mega__item:focus-visible { outline: 2px solid var(--c-brand-500); outline-offset: -2px; }

/* Icon tile — the visual anchor of every row. */
.c-menu__icon {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: var(--c-radius-sm);
    background: var(--c-brand-50);
    color: var(--c-brand-600);
    font-size: var(--c-fs-h5);
    transition: background-color var(--c-dur-fast) var(--c-ease-standard),
                color var(--c-dur-fast) var(--c-ease-standard);
}

.c-mega__item:hover .c-menu__icon,
.c-mega__item:focus-visible .c-menu__icon {
    background: var(--c-brand-600);
    color: var(--c-white);
}

.c-mega__text { min-width: 0; }

.c-mega__item .c-menu__title { white-space: nowrap; }

/* Hard limit of two lines: the panel must never grow into a wall of text. */
.c-mega__item .c-menu__hint {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}

.c-mega__arrow {
    color: var(--c-ink-300);
    font-size: var(--c-fs-xs);
    transition: transform var(--c-dur-fast) var(--c-ease-standard),
                color var(--c-dur-fast) var(--c-ease-standard);
}

.c-mega__item:hover .c-mega__arrow,
.c-mega__item:focus-visible .c-mega__arrow {
    color: var(--c-brand-600);
    /* a chevron nudges, it does not travel: the same 2px as everything else (Phase UI.9) */
    transform: translateX(var(--c-hover-lift));
}

/* Footer strip: quieter than the rows, separated by a hairline. */
.c-mega__foot {
    display: flex;
    align-items: center;
    gap: var(--c-space-4);
    margin-top: var(--c-space-2);
    padding: var(--c-space-3) var(--c-space-3) var(--c-space-1);
    border-top: 1px solid var(--c-border);
}

.c-mega__foot-link {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-1);
    color: var(--c-text-muted);
    font-size: var(--c-fs-xs);
    font-weight: var(--c-fw-medium);
    text-decoration: none;
    transition: color var(--c-dur-fast) var(--c-ease-standard);
}

.c-mega__foot-link:hover,
.c-mega__foot-link:focus-visible { color: var(--c-brand-700); }

/* ── Entrance ────────────────────────────────────────────────────────────────────────────────
   The CSS path is the baseline: navbar.js adds `.is-entering` when the panel opens, the rows rise
   in a short stagger, and the class is removed afterwards so nothing is left mid-animation. When
   GSAP is already on the page navbar.js animates the rows itself and never adds the class — the two
   paths cannot run at once. Reduced motion is handled by the system-wide guard in section 6. */
@keyframes c-mega-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

.c-mega.is-entering .c-mega__item,
.c-mega.is-entering .c-mega__eyebrow,
.c-mega.is-entering .c-mega__foot {
    animation: c-mega-in var(--c-dur-slow) var(--c-ease-entrance) both;
}

.c-mega.is-entering .c-mega__eyebrow { animation-delay: 0ms; }
.c-mega.is-entering .c-mega__list > li:nth-child(1) .c-mega__item { animation-delay: 20ms; }
.c-mega.is-entering .c-mega__list > li:nth-child(2) .c-mega__item { animation-delay: 45ms; }
.c-mega.is-entering .c-mega__list > li:nth-child(3) .c-mega__item { animation-delay: 70ms; }
.c-mega.is-entering .c-mega__list > li:nth-child(4) .c-mega__item { animation-delay: 95ms; }
.c-mega.is-entering .c-mega__list > li:nth-child(5) .c-mega__item { animation-delay: 120ms; }
.c-mega.is-entering .c-mega__foot { animation-delay: 150ms; }

/* ── 4d. Premium hero (Phase UI.4) ───────────────────────────────────────────────────────────
   The first screen. Image-first: the right-hand stage shows real catalogue thumbnails (watermarked
   derivatives, never originals) instead of drawn decoration, and the left column carries the promise,
   the search and the trust cues. Everything here reads from the token block above — no literal
   colours, no magic numbers that the spacing scale does not already define.
   The hero's entrance is a UI.6 motion sequence (`data-motion-sequence="hero"`); the retired
   `.hero-cadpng` / `.hero-copy` / `.hero-scene` hooks were removed with it.
   ────────────────────────────────────────────────────────────────────────────────────────────── */
.c-hero {
    position: relative;
    overflow: hidden;
    border-bottom: 1px solid var(--c-border);
    background:
        radial-gradient(120% 120% at 88% 8%, var(--c-brand-100) 0%, transparent 55%),
        radial-gradient(90% 90% at 4% 92%, var(--c-accent-100) 0%, transparent 48%),
        var(--c-canvas);
}

.c-hero__inner {
    position: relative;
    z-index: var(--c-z-base);
    display: grid;
    gap: var(--c-space-8);
    padding-block: var(--c-space-9);
}

.c-hero__copy { max-width: 40rem; }

/* ── Eyebrow ── */
.c-hero__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    margin: 0 0 var(--c-space-5);
    padding: 6px var(--c-space-4);
    border: 1px solid var(--c-brand-200);
    border-radius: var(--c-radius-pill);
    background: var(--c-brand-50);
    color: var(--c-brand-700);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    letter-spacing: var(--c-tracking-label);
    text-transform: uppercase;
    white-space: nowrap;
}

/* ── Headline ── */
.c-hero__title {
    margin: 0;
    color: var(--c-heading);
    /* 3.9rem is the largest size at which "Endless possibilities." still fits on one line inside the
       copy column at 1200px — measured, not guessed. The accent is held on one line from 1200px up
       (below that the copy is full-width and a normal wrap is correct). */
    font-size: var(--c-fs-display)   /* identical value — pure de-duplication */;
    font-weight: var(--c-fw-black);
    line-height: var(--c-lh-tight);
    letter-spacing: var(--c-tracking-display);
    /* the client's wrapping defect: break between words, never inside one */
    word-break: normal;
    overflow-wrap: normal;
    hyphens: none;
    text-wrap: balance;
}

.c-hero__title-accent {
    display: block;
    background: linear-gradient(96deg, var(--c-brand-600) 0%, var(--c-brand-800) 62%, var(--c-accent-700) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}

.c-hero__lede {
    max-width: 34rem;
    margin: var(--c-space-5) 0 0;
    color: var(--c-text-muted);
    font-size: var(--c-fs-lg);
    line-height: var(--c-lh-body);
    word-break: normal;
    overflow-wrap: normal;
}

/* ── Search ───────────────────────────────────────────────────────────────────────────────────
   One field, one button. The format/licence selectors belong to Phase UI.15, which owns the unified
   search component; this is the hero's entry point into it. */
/* UI.19 — retired: the `.c-hero__search*` family (14 rules, all dead). It predates the unified search
   bar (UI.15) and nothing has carried the class since — no view, no script, and no served page at any
   width renders it; the hero draws the same `.c-search__bar` component the results page does. One of
   these rules was also one of the sites where `box-shadow: var(--c-ring)` silently dropped the
   declaration, which is how the dead family was found. The two members that shared a selector group
   with live rules were removed from that group only, leaving its other twenty selectors untouched. */

/* ── Quick topics ──
   The row is the hero's; the chip itself is the shared component in §12 (the metrics are the ones
   that used to live here, so the hero renders identically). */

/* ── Trust cues ── */
.c-hero__trust {
    display: flex;
    flex-wrap: wrap;
    gap: var(--c-space-3) var(--c-space-5);
    margin: var(--c-space-6) 0 0;
    padding: var(--c-space-5) 0 0;
    border-top: 1px solid var(--c-border);
    list-style: none;
}

.c-hero__trust li {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    color: var(--c-text-muted);
    font-size: var(--c-fs-xs);
    font-weight: var(--c-fw-medium);
    white-space: nowrap;
}

.c-hero__trust i { color: var(--c-brand-600); font-size: var(--c-fs-body); }

/* ── Image stage ──────────────────────────────────────────────────────────────────────────────
   A deliberately stable collage: one tall image beside two squares. Nothing is absolutely
   positioned, so it cannot overlap its neighbours at any width; the depth comes from shadow and a
   hover lift. Phase UI.5 replaces the *inside* of `.c-hero__stage` with the floating 3D gallery —
   the geometry of the container (grid, sizing, responsive steps) is what it should keep. */
.c-hero__stage {
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: var(--c-space-4);
    align-self: center;
}

.c-hero__tile {
    position: relative;
    overflow: hidden;
    border-radius: var(--c-radius-xl);
    background: var(--c-surface);
    box-shadow: var(--c-shadow-lg);
    transition: transform var(--c-dur-base) var(--c-ease-standard),
                box-shadow var(--c-dur-base) var(--c-ease-standard);
}

.c-hero__tile--tall { grid-row: span 2; }

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

.c-hero__tile--tall img { aspect-ratio: 3 / 4; }
.c-hero__stage .c-hero__tile:not(.c-hero__tile--tall) img { aspect-ratio: 4 / 3; }

/* The protected-preview mark: these are watermarked derivatives, and saying so is part of the
   promise (the licensed original is delivered after purchase). */
.c-hero__tile-tag {
    position: absolute;
    left: var(--c-space-3);
    bottom: var(--c-space-3);
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-1);
    padding: 5px var(--c-space-3);
    border-radius: var(--c-radius-pill);
    background: rgb(17 16 32 / 62%);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: var(--c-white);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    letter-spacing: 0.02em;
    white-space: nowrap;
}

@media (hover: hover) {
    /* Phase UI.9 — the collage answers the pointer with one step and one elevation, no more. */
    .c-hero__tile:hover {
        transform: translateY(calc(-1 * var(--c-hover-lift)));
        box-shadow: var(--c-hover-shadow);
    }
}

/* ── Responsive ladder ────────────────────────────────────────────────────────────────────────
   Measured, not assumed. The collage needs ~420px per column to be worth its size, so:
     · <992px  — one column; the stage is a compact strip of three tiles (a full-screen hero on a
                 phone is a first screen, not a gallery: 390px went 1167px → 837px tall).
     · ≥992px  — copy beside the tall-beside-two collage.
     · ≥1200px — the same, with the roomier vertical rhythm. */
.c-hero__stage {
    position: relative;          /* the canvas is positioned against this box */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: auto;
    gap: var(--c-space-3);
}

/* Phase UI.5 — the floating gallery paints into this canvas, which exactly overlays the collage the
   server rendered. The collage keeps its layout box (`visibility`, not `display`), so revealing the
   canvas cannot shift anything, and a client without WebGL simply never sees it. */
.c-hero__scene {
    display: none;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.c-hero__stage.is-3d .c-hero__scene { display: block; }
.c-hero__stage.is-3d .c-hero__tile { visibility: hidden; }

.c-hero__tile img,
.c-hero__stage .c-hero__tile:not(.c-hero__tile--tall) img { aspect-ratio: 3 / 4; height: auto; }

.c-hero__tile--tall { grid-row: auto; }

@media (min-width: 992px) {
    .c-hero__inner {
        grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
        align-items: center;
        padding-block: var(--c-space-8);
    }

    /* 700px — UI.4's stated ceiling for the copy measure. The old 44rem (704px) only stayed under it
       while the container was 1400; once the container ladder widened the hero column at ≥1600 the cap
       started binding at 704 and the hero probe caught it. The contract wins over the round number. */
    .c-hero__copy { max-width: 43.75rem; }

    .c-hero__stage {
        grid-template-columns: 1.15fr 1fr;
        grid-template-rows: repeat(2, minmax(0, 1fr));
        gap: var(--c-space-4);
    }

    .c-hero__tile--tall { grid-row: span 2; }

    .c-hero__tile img { aspect-ratio: 3 / 4; }
    .c-hero__stage .c-hero__tile--tall img { aspect-ratio: 3 / 4; height: 100%; }
    .c-hero__stage .c-hero__tile:not(.c-hero__tile--tall) img { aspect-ratio: 4 / 3; }
}

@media (min-width: 1200px) {
    .c-hero__inner { padding-block: calc(var(--c-space-9) + var(--c-space-4)); }

    .c-hero__stage { gap: var(--c-space-4); }

    /* The promise reads as two lines: the statement, then the accent. */
    .c-hero__title-accent { white-space: nowrap; }
}

@media (max-width: 991.98px) {
    .c-hero__inner { gap: var(--c-space-6); padding-block: var(--c-space-7); }
    .c-hero__copy { max-width: none; }
    .c-hero__stage { max-width: none; }
    .c-hero__lede { margin-top: var(--c-space-4); font-size: var(--c-fs-body); }
    .c-hero__trust { margin-top: var(--c-space-5); padding-top: var(--c-space-4); gap: var(--c-space-2) var(--c-space-4); }
}

@media (max-width: 575.98px) {
    .c-hero__inner { padding-block: var(--c-space-6); }

    .c-hero__eyebrow { white-space: normal; }
    .c-hero__tile { border-radius: var(--c-radius-lg); }
    /* On a phone the tiles are ~110px wide: the label would truncate, so only the shield remains
       (the whole stage is aria-hidden decoration; the promise is stated twice in the trust row). */
    .c-hero__tile-tag { left: var(--c-space-2); bottom: var(--c-space-2); padding: var(--c-space-1) var(--c-space-2); font-size: var(--c-fs-label); }
    .c-hero__tile-tag-text { display: none; }
}

/* ── 7. Image card (Phase UI.7) ─────────────────────────────────────────────────────────────────
   The public card is the picture. Everything the old card carried that was not the image is gone: the
   white panel and its bordered footer, the "Preview" badge that sat on every tile, four permanently
   visible action buttons, a grey letterbox band under each image, and a separate "View" link beside a
   linked title. What is left is one link covering the whole card, a two-line caption, a badge only
   when the image genuinely holds a Royalty-Free licence, and actions that stay out of the way until
   the card is hovered or focused.

   Prices, DPI, resolution and file size are not part of a card and never were — they belong to the
   detail page, where they stay.

   The `card-badge` / `card-badge--rf` names are deliberately NOT renamed: Module 6 and Module 10 count
   `card-badge--rf` in the rendered HTML to prove the badge follows the licence, so the handle is
   load-bearing rather than stylistic.
   ───────────────────────────────────────────────────────────────────────────────────────────── */
.c-card {
    position: relative;
    height: 100%;
    border-radius: var(--c-radius-lg);
}

/* One link, the whole card: the frame and the caption both live inside it, so every pixel of the card
   is clickable and a screen reader announces a single, named target instead of two links to one place. */
.c-card__link {
    display: block;
    height: 100%;
    border-radius: inherit;
    color: inherit;
    text-decoration: none;
}

.c-card__frame {
    position: relative;
    display: block;
    overflow: hidden;
    aspect-ratio: 1.2;
    border-radius: var(--c-radius-lg);
    background: var(--c-ink-100);
    box-shadow: inset 0 0 0 1px var(--c-ink-200);
    transition: box-shadow var(--c-dur-base) var(--c-ease-standard);
}

/* Fill the frame. `contain` left a grey band below every image whose ratio was not exactly the tile's. */
.c-card__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--c-dur-slow) var(--c-ease-standard);
}

.c-card__caption {
    display: block;
    margin-top: var(--c-space-3);
}

/* Two lines, then an ellipsis — a title is the only text on the card, so a long one gets a second
   line instead of being cut off at one. `white-space: normal` with normal breaking keeps words whole:
   nothing here ever splits mid-word. Most titles fit the first line anyway, so desktop is unaffected. */
.c-card__title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    color: var(--c-ink-900);
    font-size: var(--c-fs-sm);
    font-weight: 600;
    line-height: 1.35;
    white-space: normal;
    word-break: normal;
    overflow-wrap: normal;
}

.c-card__by {
    display: block;
    margin-top: var(--c-space-1);
    color: var(--c-ink-500);
    font-size: var(--c-fs-xs);
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
}

/* The image ID is one unbreakable token and it is a searchable handle, so it is never truncated or
   split: it takes its own line under the contributor at every width. That also keeps every caption in
   a row the same height, and it removes the separator that otherwise ended up dangling at a line end
   whenever a caption wrapped (which it does at almost every grid width). */
.c-card__id {
    display: block;
    white-space: nowrap;
}

/* Restrained: the image warms toward the viewer and the tile gains a hairline. Nothing here moves the
   layout. Phase UI.9 owns the site's hover language and will refine this. */
@media (hover: hover) {
    .c-card:hover .c-card__img,
    .c-card:focus-within .c-card__img {
        transform: scale(1.035);
    }

    .c-card:hover .c-card__frame,
    .c-card:focus-within .c-card__frame {
        box-shadow: inset 0 0 0 1px var(--c-ink-300), var(--c-shadow-md);
    }
}

/* ── Actions ── quiet at rest, present the moment the card is touched. A card in a 20-tile grid with
   four permanent buttons on every tile reads as chrome, which is exactly what the redesign removes. */
.c-card__actions {
    position: absolute;
    top: var(--c-space-3);
    right: var(--c-space-3);
    display: flex;
    gap: var(--c-space-2);
    opacity: 0;
    /* One step, not four: the actions fade in and rise by the same 2px everything else is allowed
       (Phase UI.9 — the reveal used to travel twice that, which the contract does not permit even for
       an element that is invisible at rest). */
    transform: translateY(calc(-1 * var(--c-hover-lift)));
    transition: opacity var(--c-dur-base) var(--c-ease-standard),
                transform var(--c-dur-base) var(--c-ease-standard);
}

.c-card:hover .c-card__actions,
.c-card:focus-within .c-card__actions {
    opacity: 1;
    transform: none;
}

/* A touch device has no hover state, so there the actions are simply always there. */
@media (hover: none) {
    .c-card__actions { opacity: 1; transform: none; }
}

.c-card__action {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgb(255 255 255 / 92%);
    color: var(--c-ink-900);
    font-size: var(--c-fs-body);
    line-height: 1;
    box-shadow: var(--c-shadow-sm);
    backdrop-filter: blur(6px);
    transition: background var(--c-dur-fast) var(--c-ease-standard),
                color var(--c-dur-fast) var(--c-ease-standard),
                transform var(--c-dur-fast) var(--c-ease-standard);
}

.c-card__action:hover {
    background: var(--c-brand-600);
    color: var(--c-white);
    transform: translateY(-1px);
}

/* §5's ring sets `border-radius: var(--c-radius-xs)` on whatever it focuses, which would square off a
   circular button mid-focus. The ring itself is inherited; only the radius needs holding. */
.c-card__action:focus-visible {
    border-radius: 50%;
}

/* Saved state: a filled heart in the danger colour, so "saved" reads without a tooltip. */
.c-card__action.is-saved {
    color: var(--c-danger);
}

.c-card__action.is-saved:hover {
    background: var(--c-danger);
    color: var(--c-white);
}

/* ── The Royalty-Free badge: only rendered when the licence genuinely exists (UI.11), and it must not
   steal the click — the whole card is the link. ── */
.c-card .card-badge {
    position: absolute;
    top: var(--c-space-3);
    left: var(--c-space-3);
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-1);
    padding: 3px var(--c-space-2);
    border-radius: var(--c-radius-xs);
    background: rgb(255 255 255 / 94%);
    color: var(--c-ink-700);
    font-size: var(--c-fs-label);
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1.5;
    box-shadow: var(--c-shadow-sm);
    pointer-events: none;
}

.c-card .card-badge--rf {
    /* Measured, not chosen by eye (UI.11): the pill is white at 94%, so the label has to hold contrast
       over any photograph underneath it. At accent-700 (#1d8f79) it read 3.99:1 — under the 4.5:1 small
       text needs. accent-800 gives 5.97:1 on white and 5.24:1 in the worst case (pill over black). */
    color: var(--c-accent-800);
}

.c-card .card-badge .bi {
    font-size: var(--c-fs-xs);
}

/* Small screens: the caption steps down a notch and the tile keeps its radius. */
@media (max-width: 575.98px) {
    .c-card__caption { margin-top: var(--c-space-2); }
    .c-card__title { font-size: var(--c-fs-xs); }
    .c-card__by { font-size: var(--c-fs-label); }
    .c-card__actions { top: var(--c-space-2); right: var(--c-space-2); }
}

/* ── 9. Hover language (Phase UI.9) ─────────────────────────────────────────────────────────────

   Hover is feedback, not decoration. One tempo, one step, and never a layout event:

     1. Something changes — colour, background, border, shadow, or at most 2px of travel. A hoverable
        thing that does nothing tells the visitor it is not hoverable.
     2. Nothing moves layout. No size, padding, margin or border-width changes, and no descendant may
        move its own siblings (an avatar that grows on hover moves the text under it: banned).
     3. At most 2px of travel (--c-hover-lift), at most 4% of scale on imagery, never a rotation,
        never a bounce, never a sheen that sweeps across a button.
     4. Hover and keyboard focus show the same thing, so the mouse and the tab key describe the same
        interface.
     5. Nothing animates while idle. Motion happens in response to the pointer or the keyboard or not
        at all.
     6. A device without hover gets no hover state (a tap must not leave a card lifted), and a
        reduced-motion visitor keeps the colour feedback and loses all of the movement.

   The components below follow it here; the legacy surfaces that predate the design system are brought
   into line in app.css, and the probe (`_tools/shot/hover_probe.js`) measures the result from the
   live CSSOM and a real pointer, because the cascade does not care which sheet states the rule. */

@media (hover: hover) {
    /* Navigation and the panels behind it: colour only, no movement. */
    .c-menu__item:hover,
    .c-mega__item:hover,
    .c-mega__foot-link:hover {
        transition: background var(--c-hover-dur) var(--c-ease-standard),
                    color var(--c-hover-dur) var(--c-ease-standard);
    }

    /* Chips and the hero's own button: colour, plus the single allowed step. */
    .c-chip:hover {
        transition: background var(--c-hover-dur) var(--c-ease-standard),
                    border-color var(--c-hover-dur) var(--c-ease-standard),
                    color var(--c-hover-dur) var(--c-ease-standard);
    }

    /* The image card: the tile itself never moves. The picture warms and the frame takes a hairline,
       which is the whole treatment — see §7 for the states. */
    .c-card__action:hover {
        transform: translateY(calc(-1 * var(--c-hover-lift) / 2));
    }
}

/* Keyboard parity. The three legacy decks are an anchor wrapping a div, so the hover treatment sits on
   the inner element while focus lands on the link: a visitor tabbing through the home page used to get
   nothing but a generic outline while the mouse got a whole card treatment. The mouse and the tab key
   must describe the same interface, so a focused card gets the focused version of its own hover. */
a:focus-visible .category-card-modern,
a:focus-visible .contributor-card-modern,
a:focus-visible .pricing-card-modern {
    transform: translateY(calc(-1 * var(--c-hover-lift)));
    box-shadow: var(--c-hover-shadow);
}

a:focus-visible .contributor-card-modern {
    border-color: rgba(93, 69, 212, 0.28);
}

/* The pricing deck is not an anchor: the focusable thing is the button inside it, so `:focus-within` is
   the relationship that matters there. */
.pricing-card-modern:focus-within {
    transform: translateY(calc(-1 * var(--c-hover-lift)));
    box-shadow: var(--c-hover-shadow);
}

/* A device with no hover gets no hover state. app.css's lifts are older than this design system and are
   not wrapped in a capability query, so on a touch screen a tap could leave a card stuck 2px up until
   something else was tapped. The state is switched off for surfaces that cannot be hovered; taps still
   get the colour and shadow response, because those are not a stuck state. */
@media (hover: none) {
    .c-hero__tile:hover,
    .category-card-modern:hover,
    .contributor-card-modern:hover,
    .pricing-card-modern:hover,
    /* a tap gives :focus-within on some browsers: that must not leave a card stuck either */
    .pricing-card-modern:focus-within,
    .glass-card:hover,
    .sell-card:hover,
    .hover-lift-sm:hover,
    .hover-lift:hover,
    .hover-lift-lg:hover,
    .hover-lift-xl:hover,
    .btn-cadpng:hover,
    .btn-cadpng-outline:hover,
    .btn-primary:hover,
    .public-body .navbar-brand:hover,
    .public-body .hero .search-shell:focus-within {
        transform: none !important;
    }
}

/* The Bootstrap-button sheen. `@keyframes sv-sheen` is not even needed for it: app.css paints a white
   stripe across the button and slides it 245% of its width on hover, 650ms, on every public page that
   carries a .btn-primary. ui-refresh already disarms the same flourish on .btn-cadpng
   (`.ui-refresh .btn-cadpng::before { display: none }`); this finishes the job. Nothing about the
   button's colour, radius or shadow changes — only the stripe that sweeps over the label. */
.public-body .btn-primary::before {
    display: none;
}

/* The hero's search shell lifts 4px when the field inside it takes focus. Halved to the one allowed
   step and moved onto the shared tempo, so the focus state reads the same as every hover state. */
.public-body .hero .search-shell:focus-within {
    transform: translateY(calc(-1 * var(--c-hover-lift)));
    transition: transform var(--c-hover-dur) var(--c-ease-standard),
                box-shadow var(--c-hover-dur) var(--c-ease-standard);
}

/* The language holds without hover too: colours stay, movement does not exist.
   §6 already takes the durations to zero for reduced-motion visitors, which is not enough on its own —
   a zero-length transform is still a 2px jump. These selectors remove the movement itself, so the
   colour, border and shadow feedback survives and nothing moves. `!important` is deliberate: this is
   an off-switch, and it has to beat a plain `:hover` rule in any earlier sheet (app.css and
   ui-refresh.css both set these transforms, ui-refresh at a higher specificity than a bare :hover). */
@media (prefers-reduced-motion: reduce) {
    .c-hero__tile:hover,
    .c-card__action:hover,
    .c-mega__item:hover .c-mega__arrow,
    .c-mega__item:focus-visible .c-mega__arrow,
    .pricing-card-modern:focus-within,
    a:focus-visible .category-card-modern,
    a:focus-visible .contributor-card-modern,
    .public-body .hero .search-shell:focus-within,
    .public-body .navbar-brand:hover,
    .category-card-modern:hover,
    .contributor-card-modern:hover,
    .pricing-card-modern:hover,
    .glass-card:hover,
    .sell-card:hover,
    .hover-lift-sm:hover,
    .hover-lift:hover,
    .hover-lift-lg:hover,
    .hover-lift-xl:hover,
    .btn-cadpng:hover,
    .btn-cadpng-outline:hover,
    .btn-primary:hover {
        transform: none !important;
    }

    .c-card:hover .c-card__img,
    .c-card:focus-within .c-card__img {
        transform: none !important;
    }
}

/* ── 8. Image gallery layout (Phase UI.8) ───────────────────────────────────────────────────────

   One grid for every image gallery on the public site — the catalogue, collections, a contributor's
   portfolio, the wishlist, the detail page's related strips (all through the same partial) and the home
   page's two image sections.

   Uniform, not masonry. Every tile in a row is the same size, so no image is accidentally privileged
   over its neighbour, every caption sits on the same line, and paging or "load more" cannot reflow the
   page as batches arrive. It is also what the stock marketplaces this has to sit beside do.

   The steps are device classes rather than arbitrary widths, and the gaps come from the spacing scale,
   so a tile is a comfortable size at every width instead of being crammed to fit one more column:

       viewport     columns   gap
       <  576px        2       12px
       >= 576px        3       12px
       >= 768px        3       16px
       >= 992px        4       16px
       >= 1200px       4       24px
       >= 1440px       5       24px

   768 -> 992 and 1366 -> 1440 hold the tile size steady (226px and ~259px), so a window resized across
   those widths gains a column without the artwork jumping. */
.c-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--c-space-3);
    margin: 0;
    padding: 0;
    list-style: none;
    /* No stretch: a tile is exactly as tall as its picture and its caption. */
    align-items: start;
}

/* A grid item has to be allowed to be narrower than its content, or one long title widens a column. */
.c-grid > * {
    min-width: 0;
}

@media (min-width: 576px) {
    .c-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 768px) {
    .c-grid { gap: var(--c-space-4); }
}

@media (min-width: 992px) {
    .c-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (min-width: 1200px) {
    .c-grid { gap: var(--c-space-5); }
}

@media (min-width: 1440px) {
    .c-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* Uniform rows need uniform tiles. A title that fits on one line would make its card 18px shorter than
   its neighbours, and the difference shows up as uneven space under that card — the row's gap is fixed,
   but the eye reads the distance from the caption to the next row. Reserving the second line for every
   title in a grid keeps each tile exactly as tall as the next one, so the rhythm is even at every width
   and for every title length. (The clamp itself is UI.7's: two lines, never more.) */
.c-grid .c-card__title {
    min-block-size: calc(var(--c-fs-sm) * 1.35 * 2);
}

@media (max-width: 575.98px) {
    .c-grid .c-card__title {
        min-block-size: calc(var(--c-fs-xs) * 1.35 * 2);
    }
}

/* Category and creator decks keep their own denser schedule (Phase UI.12). */

/* ── 5. Focus & accessibility ─────────────────────────────────────────────────────────────── */
.public-body :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: var(--c-ring) solid var(--c-focus);
    outline-offset: 3px;
    /* No border-radius reset here (UI.16): an outline already follows the element's own radius, and
       forcing a fixed one visibly straightened pills — the search submit and every chip — the moment
       they were focused by keyboard. */
}

/* A focus ring that is visible on dark hero panels too. */
.c-on-dark :focus-visible { outline-color: #fff; }

.c-visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── 5. Loading skeleton (used from UI.17; the primitive belongs to the system) ───────────── */
.c-skeleton {
    position: relative;
    overflow: hidden;
    background: var(--c-ink-100);
    border-radius: var(--c-radius-md);
}

.c-skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, rgb(255 255 255 / 55%), transparent);
    animation: c-skeleton-sweep 1.4s var(--c-ease-standard) infinite;
}

/* Off-screen placeholders do not animate: the tint stays, the sweep goes. `app.js` marks the ones the
   visitor can see (a screen and a half either way) with `is-inview`, and the rule is scoped to `.js` so
   that a browser without scripting keeps the effect it always had. */
.js .c-skeleton:not(.is-inview)::after { display: none; }

@keyframes c-skeleton-sweep {
    100% { transform: translateX(100%); }
}

/* ── 6. Motion system (Phase UI.6) ───────────────────────────────────────────────────────────
   The CSS half of `assets/js/motion.js`. Markup opts in with `data-motion`,
   `data-motion-group`, `data-motion-sequence`/`data-motion-step`; this file holds the start state
   and nothing else, so that the JS owns the movement and the stylesheet stays declarative.

   Two rules make this safe, and both matter more than the animation:

   1. The start state is scoped to `.js`, a class the <head> bootstrap adds only while scripting is
      running. With JavaScript off, no rule here matches and every element renders normally — motion
      can never be why a visitor cannot read the page.
   2. `.is-revealed` ends the rule. The element's final appearance is whatever its own stylesheet
      says, not a value left behind by an animation, so a half-finished tween cannot strand content
      between two states. `motion.js` adds the class and clears its inline styles in the same frame.

   `data-motion="fade-in"` skips the rise for elements that should not travel (inline chips, hero
   text inside a line box), and children of a sequence are held until their turn.
   ────────────────────────────────────────────────────────────────────────────────────────────── */
.js [data-motion]:not(.is-revealed),
.js [data-motion-group]:not(.is-revealed) > *,
.js [data-motion-sequence] [data-motion-step]:not(.is-revealed) {
    opacity: 0;
    transform: translate3d(0, var(--c-motion-rise), 0);
}

.js [data-motion="fade-in"]:not(.is-revealed),
.js [data-motion-group][data-motion="fade-in"]:not(.is-revealed) > * {
    transform: none;
}

/* A revealing element should not be clipped or scrolled into place by its own transform. */
.js [data-motion-group] > * {
    will-change: opacity, transform;
}

/* ── The CSS path ───────────────────────────────────────────────────────────────────────────────
   When GSAP cannot be reached (blocked CDN, offline, a CSP that does not allow the host) the page is
   not left unanimated: motion.js hands the sequences to the stylesheet, exactly as navbar.js does for
   the mega-menu, and records which path ran on `data-entrance`. The two cannot run together — the
   class is only ever added on the branch where GSAP was unavailable.
   ------------------------------------------------------------------------------------------------ */
@keyframes c-motion-rise {
    from { opacity: 0; transform: translate3d(0, var(--c-motion-rise), 0); }
    to   { opacity: 1; transform: none; }
}

.js [data-motion-sequence].is-entering [data-motion-step] {
    animation: c-motion-rise var(--c-dur-slower) var(--c-ease-entrance) both;
}

/* The stagger, in the same 70ms rhythm the GSAP path uses. Seven steps is what the hero has; a
   longer sequence simply falls back to the shared duration beyond the last rule. */
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(1) { animation-delay: 0ms; }
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(2) { animation-delay: 70ms; }
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(3) { animation-delay: 140ms; }
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(4) { animation-delay: 210ms; }
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(5) { animation-delay: 280ms; }
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(6) { animation-delay: 350ms; }
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(7) { animation-delay: 420ms; }
.js [data-motion-sequence].is-entering [data-motion-step]:nth-child(n+8) { animation-delay: 490ms; }

/* A group revealed on the CSS path: children rise in a short stagger, containers never move. */
.js [data-motion-group].is-entering > * {
    animation: c-motion-rise var(--c-dur-base) var(--c-ease-entrance) both;
}

.js [data-motion-group].is-entering > *:nth-child(1) { animation-delay: 0ms; }
.js [data-motion-group].is-entering > *:nth-child(2) { animation-delay: 45ms; }
.js [data-motion-group].is-entering > *:nth-child(3) { animation-delay: 90ms; }
.js [data-motion-group].is-entering > *:nth-child(4) { animation-delay: 135ms; }
.js [data-motion-group].is-entering > *:nth-child(n+5) { animation-delay: 180ms; }

/* Once everything has arrived, stop paying for compositor layers. */
.js [data-motion].is-revealed,
.js [data-motion-group].is-revealed > *,
.js [data-motion-step].is-revealed {
    will-change: auto;
}

/* ── 6. Motion safety ─────────────────────────────────────────────────────────────────────── */
/* One guard for the whole system: everything collapses when the visitor asks for less motion.
   Phases must not re-implement this per component. */
@media (prefers-reduced-motion: reduce) {
    :root {
        --c-dur-instant: 0ms;
        --c-dur-fast: 0ms;
        --c-dur-base: 0ms;
        --c-dur-slow: 0ms;
        --c-dur-slower: 0ms;
    }

    .public-body *,
    .public-body *::before,
    .public-body *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }

    .c-skeleton::after { display: none; }

    /* UI.6: a reduced-motion visitor is never shown the hidden start state, even for the moment
       before motion.js runs. The animation is not sped up here — it does not happen. */
    .js [data-motion]:not(.is-revealed),
    .js [data-motion-group]:not(.is-revealed) > *,
    .js [data-motion-sequence] [data-motion-step]:not(.is-revealed) {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* ── 10. Section system (Phase UI.12) ────────────────────────────────────────────────────────────

   The homepage was nine bands of `py-5` with a `.section-header` that centred every heading and a
   handful of ad-hoc background hexes (#fafbff, #f8f9ff, two gradients). Nothing was wrong with any one
   band; what was missing was a *system*: a section that says what it is, one rhythm between sections, a
   band treatment that repeats deliberately, and a head pattern that reads the same every time.

   The rules, in the order they matter:

   1. **Every section announces itself.** A band carries a heading (h2) and, where it helps, a one-line
      lede and a trailing action. No band is a bare pile of cards.
   2. **One rhythm.** `--c-section-y` between sections, `--c-section-y-tight` when a band continues the
      one above it (the pricing band under the creator band, for instance). Never a one-off padding.
   3. **Three bands, not nine.** Plain (the page canvas), `--band` (a tinted band, `--c-band-soft`) and
      `--band-dark` (the testimonial band). Bands alternate only when the alternation means something.
   4. **The head is a pattern, not a slogan.** Eyebrow (optional, small caps), title, lede (optional,
      one sentence, capped measure), action (optional, right-aligned on desktop, beneath the lede on
      mobile). The title is left-aligned: a centred heading under a centred heading is how a page starts
      to feel like a brochure rather than a catalogue.
   5. **Decks use the gallery's grid.** Category, creator and step decks share the collection's gaps and
      breakpoint schedule so the page has one column language, not two (`row-cols-*` had its own).

   `.section-header` (app.css, UI.1) stays for the pages this phase did not touch; the homepage and the
   other public galleries move to `.c-section__head`. Both are listed in the census so the migration can
   be finished page by page. ── */

.c-section {
    padding-block: var(--c-section-y);
}

.c-section--tight {
    padding-block: var(--c-section-y-tight);
}

.c-section--band {
    background: var(--c-band-soft);
}

.c-section--dark {
    background: var(--c-band-dark);
    color: var(--c-white);
}

/* The first band after the hero starts at the top rhythm: the hero already owns its own bottom space. */
.c-hero + .c-section {
    padding-block-start: var(--c-section-y);
}

/* ── The head ── */
.c-section__head {
    margin-bottom: var(--c-section-head-gap);
}

.c-section__head-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: var(--c-space-4) var(--c-space-5);
}

.c-section__eyebrow {
    margin: 0 0 var(--c-space-2);
    font-size: var(--c-fs-label);
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--c-brand-600);
}

.c-section__title {
    margin: 0;
    font-size: var(--c-fs-h2)   /* identical value — pure de-duplication */;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.15;
    color: var(--c-ink-900);
}

.c-section__lede {
    margin: var(--c-section-title-gap) 0 0;
    max-width: 62ch;
    font-size: var(--c-fs-lg);
    line-height: 1.6;
    color: var(--c-text-muted);
}

.c-section__action {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    padding-block: var(--c-space-1);
    font-size: var(--c-fs-sm);
    font-weight: 700;
    color: var(--c-brand-700);
    text-decoration: none;
    white-space: nowrap;
    border-bottom: 2px solid transparent;
    transition: color var(--c-hover-dur) var(--c-ease-standard),
                border-color var(--c-hover-dur) var(--c-ease-standard);
}

.c-section__action .bi {
    transition: transform var(--c-hover-dur) var(--c-ease-standard);
}

@media (hover: hover) {
    .c-section__action:hover {
        color: var(--c-brand-800);
        border-bottom-color: currentColor;
    }

    .c-section__action:hover .bi {
        transform: translateX(var(--c-hover-lift));
    }
}

.c-section__action:focus-visible {
    /* UI.12 fix: `--c-shadow-focus` is a box-shadow *value* (0 0 0 3px …), so it is not a valid outline
       colour and the ring never rendered — the probe caught the link being tabbed to with no visible
       answer. The brand ink is a colour, and the underline mirrors what the pointer does. */
    color: var(--c-brand-800);
    border-bottom-color: currentColor;
    outline: var(--c-ring) solid var(--c-brand-600);
    outline-offset: 3px;
}

.c-section__action:focus-visible .bi {
    transform: translateX(var(--c-hover-lift));
}

.c-section__head.is-dark .c-section__title {
    color: var(--c-white);
}

.c-section__head.is-dark .c-section__lede {
    color: rgb(255 255 255 / 72%);
}

.c-section__head.is-dark .c-section__eyebrow {
    color: var(--c-accent-300);
}

.c-section__head.is-dark .c-section__action {
    color: var(--c-white);
}

/* A head with no action keeps its text column full width: no empty second column. */
.c-section__head:not(:has(.c-section__action)) .c-section__head-row {
    grid-template-columns: minmax(0, 1fr);
}

@media (max-width: 767.98px) {
    .c-section__head-row {
        grid-template-columns: minmax(0, 1fr);
        align-items: start;
    }

    .c-section__action {
        justify-self: start;
    }
}

/* ── Decks: categories, creators, steps. One column language with the galleries (UI.8). ── */
.c-deck {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--c-deck-gap);
    margin: 0;
    padding: 0;
    list-style: none;
    align-items: stretch;
}

.c-deck > * {
    min-width: 0;
}

@media (min-width: 576px) {
    .c-deck { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 992px) {
    .c-deck { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* The step deck is a three-up reading order, not a gallery: it never becomes four columns. */
.c-deck--steps {
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 768px) {
    .c-deck--steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ── Step card: the "how it works" tiles, previously three stacks of inline gradients ── */
.c-step {
    display: grid;
    gap: var(--c-space-3);
    height: 100%;
    padding: var(--c-space-6) var(--c-space-5);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-lg);
    background: var(--c-white);
    text-align: center;
    box-shadow: var(--c-shadow-xs);
}

.c-step__icon {
    display: grid;
    place-items: center;
    width: 64px;
    height: 64px;
    margin: 0 auto var(--c-space-2);
    border-radius: var(--c-radius-lg);
    background: var(--c-brand-50);
    color: var(--c-brand-600);
    font-size: var(--c-fs-h3);
}

.c-step--accent .c-step__icon {
    background: var(--c-accent-100);
    color: var(--c-accent-800);
}

.c-step--warm .c-step__icon {
    background: var(--c-ink-100);
    color: var(--c-warning);
}

.c-step__title {
    margin: 0;
    font-size: var(--c-fs-h4);
    font-weight: 700;
    color: var(--c-ink-900);
}

.c-step__text {
    margin: 0;
    font-size: var(--c-fs-sm);
    line-height: 1.6;
    color: var(--c-text-muted);
}

/* ── Testimonial card: the dark band's content, previously a stack of inline rgba/blur ── */
.c-quote {
    height: 100%;
    padding: var(--c-space-6);
    border: 1px solid rgb(255 255 255 / 12%);
    border-radius: var(--c-radius-xl);
    background: rgb(255 255 255 / 6%);
}

.c-quote__stars {
    margin-bottom: var(--c-space-3);
    color: var(--c-star);
    font-size: var(--c-fs-sm);
}

.c-quote__text {
    margin: 0 0 var(--c-space-5);
    font-size: var(--c-fs-lg);
    font-style: italic;
    line-height: 1.65;
    color: rgb(255 255 255 / 82%);
}

.c-quote__who {
    display: flex;
    align-items: center;
    gap: var(--c-space-3);
}

.c-quote__avatar {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: var(--c-radius-pill);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    color: var(--c-white);
    font-weight: 800;
}

.c-quote__name {
    font-weight: 700;
    color: var(--c-white);
}

.c-quote__role {
    font-size: var(--c-fs-label);
    color: rgb(255 255 255 / 55%);
}

/* ── Category tile: the tinted tile the deck used to build out of a background div plus an overlay ── */
.c-cat-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 148px;
    height: 100%;
    padding: var(--c-space-5);
    border-radius: var(--c-radius-lg);
    overflow: hidden;
    color: var(--c-white);
    text-decoration: none;
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-brand-800));
    isolation: isolate;
}

.c-cat-tile__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgb(22 22 42 / 10%), rgb(22 22 42 / 62%));
    z-index: -1;
}

.c-cat-tile__name {
    margin: 0;
    /* Phase UI.13 — this is an h3 and it is a card title, so it takes the card step rather than the
       17px body-large step it was borrowing. One pixel, and the tile stops being the one heading on
       the home page that sat between two levels. */
    font-size: var(--c-fs-h4);
    font-weight: var(--c-fw-bold);
    line-height: var(--c-lh-heading);
    letter-spacing: var(--c-tracking-heading);
}

.c-cat-tile__count {
    margin-top: var(--c-space-1);
    font-size: var(--c-fs-label);
    color: rgb(255 255 255 / 78%);
}

/* UI.12: the tile is a link, so it owes the visitor an answer (UI.9 contract rule 1). The answer is the
   scrim deepening and one elevation step — no travel, so nothing can shift the grid. */
.c-cat-tile__scrim {
    transition: background var(--c-hover-dur) var(--c-ease-standard);
}

.c-cat-tile {
    transition: box-shadow var(--c-hover-dur) var(--c-ease-standard);
}

.c-cat-tile:focus-visible {
    outline: var(--c-ring) solid var(--c-white);
    outline-offset: 3px;
}

@media (hover: hover) {
    .c-cat-tile:hover {
        box-shadow: var(--c-hover-shadow);
    }

    .c-cat-tile:hover .c-cat-tile__scrim {
        background: linear-gradient(180deg, rgb(22 22 42 / 4%), rgb(22 22 42 / 74%));
    }
}

/* Keyboard parity (UI.9 rule 4): focus shows what hover shows. */
.c-cat-tile:focus-visible {
    box-shadow: var(--c-hover-shadow);
}

.c-cat-tile:focus-visible .c-cat-tile__scrim {
    background: linear-gradient(180deg, rgb(22 22 42 / 4%), rgb(22 22 42 / 74%));
}

/* ── Creator card ── */
.c-creator {
    display: grid;
    justify-items: center;
    gap: var(--c-space-2);
    height: 100%;
    padding: var(--c-space-6) var(--c-space-5);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-lg);
    background: var(--c-white);
    text-align: center;
    text-decoration: none;
    box-shadow: var(--c-shadow-xs);
}

.c-creator__avatar {
    display: grid;
    place-items: center;
    width: 64px;
    height: 64px;
    border-radius: var(--c-radius-pill);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    color: var(--c-white);
    font-size: var(--c-fs-h3);
    font-weight: 800;
}

.c-creator__name {
    margin: 0;
    font-size: var(--c-fs-h4);
    font-weight: 700;
    color: var(--c-ink-900);
}

.c-creator__count {
    font-size: var(--c-fs-label);
    color: var(--c-text-muted);
}

.c-creator__cta {
    margin-top: var(--c-space-2);
    font-size: var(--c-fs-label);
    font-weight: 700;
    color: var(--c-brand-700);
}

/* UI.12: border and shadow answer, the avatar stays still (UI.9 measured the old deck rotating its
   avatar 5° and called it unrestrained). */
.c-creator {
    transition: border-color var(--c-hover-dur) var(--c-ease-standard),
                box-shadow var(--c-hover-dur) var(--c-ease-standard);
}

@media (hover: hover) {
    .c-creator:hover {
        border-color: var(--c-hover-line);
        box-shadow: var(--c-hover-shadow);
    }
}

.c-creator:focus-visible {
    border-color: var(--c-hover-line);
    box-shadow: var(--c-hover-shadow);
    outline: var(--c-ring) solid var(--c-brand-600);
    outline-offset: 3px;
}

/* ── Footer link groups: one heading per group, whichever source provides it (UI.12) ── */
.marketplace-footer .c-footer__heading {
    margin: 0 0 var(--c-space-3);
    font-size: var(--c-fs-label);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--c-white);
}

.marketplace-footer .c-footer__links a {
    color: rgb(255 255 255 / 62%);
    text-decoration: none;
    transition: color var(--c-dur-fast) var(--c-ease-standard);
}

.marketplace-footer .c-footer__links a:hover,
.marketplace-footer .c-footer__links a:focus-visible {
    color: #28b8ee;   /* Brand module §14: footer links hover to the logo's cyan */
}

/* ── Closing call to action (UI.12) ── */
.c-cta {
    padding: clamp(2.5rem, 5vw, 4rem) var(--c-space-5);
    border-radius: var(--c-radius-2xl);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    color: var(--c-white);
    text-align: center;
}

.c-cta__title {
    /* UI.12 defect: this heading inherited the global heading ink (rgb(36,36,56)) and sat at 2.36:1 on
       the purple→teal gradient — the washed-out headline the phase's probe caught. Explicit white. */
    color: var(--c-white);
    margin: 0 0 var(--c-space-4);
    font-size: var(--c-fs-h2);
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.2;
}

.c-cta__text {
    max-width: 52ch;
    margin: 0 auto var(--c-space-5);
    font-size: var(--c-fs-lg);
    line-height: 1.6;
    color: rgb(255 255 255 / 85%);
}

.c-cta__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--c-space-3);
}

.c-cta__button {
    display: inline-flex;
    align-items: center;
    padding: var(--c-space-3) var(--c-space-6);
    border: 2px solid var(--c-white);
    border-radius: var(--c-radius-pill);
    background: var(--c-white);
    color: var(--c-brand-700);
    font-weight: 700;
    font-size: var(--c-fs-sm);
    text-decoration: none;
    transition: background-color var(--c-hover-dur) var(--c-ease-standard),
                color var(--c-hover-dur) var(--c-ease-standard);
}

.c-cta__button--ghost {
    background: transparent;
    color: var(--c-white);
}

@media (hover: hover) {
    .c-cta__button:hover { background: var(--c-brand-50); }

    .c-cta__button--ghost:hover {
        background: rgb(255 255 255 / 14%);
        color: var(--c-white);
    }
}

.c-cta__button:focus-visible {
    /* same surface change as hover, plus the ring — parity, not two different languages */
    background: var(--c-brand-50);
    outline: var(--c-ring) solid var(--c-white);
    outline-offset: 3px;
}

.c-cta__button--ghost:focus-visible {
    background: rgb(255 255 255 / 14%);
    color: var(--c-white);
}

/* ── 11. Unified search box (Phase UI.15) ─────────────────────────────────────────────────────────
   One component for every search surface: the home hero and the search page render the same markup with
   a variant class, so a change to the control is a change everywhere. Three parts — a labelled field, a
   scope dropdown and a submit button — and the scope dropdown is the *only* control for what kind of
   asset is wanted (format and licence), which is what stops those two facets from ever disagreeing.

   Sized to be the largest control on any page it appears on: a 56px bar, a 17px query. At phone widths
   the bar re-flows to two rows so nothing is squeezed into an unusable width. */
.c-search {
    position: relative;
    width: 100%;
}

.c-search__bar {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    grid-template-areas: "icon scope input submit";
    align-items: center;
    gap: var(--c-space-2);
    min-height: 56px;
    padding: var(--c-space-1) var(--c-space-1) var(--c-space-1) var(--c-space-5);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--c-radius-pill);
    background: var(--c-surface);
    box-shadow: var(--c-shadow-md);
    transition: border-color var(--c-dur-fast) var(--c-ease-standard),
                box-shadow var(--c-dur-fast) var(--c-ease-standard);
}

/* UI.19 defect: same dropped declaration as the hero search above. */
.c-search__bar:focus-within {
    border-color: var(--c-brand-500);
    box-shadow: var(--c-shadow-md), var(--c-shadow-focus);
}

.c-search__icon {
    grid-area: icon;
    display: grid;
    place-items: center;
    color: var(--c-ink-400);
    font-size: var(--c-fs-h5);
}

/* ── the query field ─────────────────────────────────────────────────────────────────────────── */
.c-search__input {
    grid-area: input;
    min-width: 0;
    height: 44px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--c-text);
    font-family: inherit;
    font-size: var(--c-fs-lg);
    line-height: 1.4;
    /* long placeholders clip mid-word at 320–430px otherwise ("…or Imag"); this keeps the cut clean */
    text-overflow: ellipsis;
}

.c-search__input::placeholder { color: var(--c-ink-400); }
.c-search__input:focus { outline: none; }

/* ── the scope dropdown ──────────────────────────────────────────────────────────────────────── */
.c-search__scope-wrap { grid-area: scope; }

.c-search__scope {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    min-height: 44px;
    padding: 0 var(--c-space-3);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-pill);
    background: var(--c-canvas);
    color: var(--c-ink-700);
    font-family: inherit;
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--c-dur-fast) var(--c-ease-standard),
                border-color var(--c-dur-fast) var(--c-ease-standard);
}

.c-search__scope:hover { background: var(--c-brand-50); border-color: var(--c-brand-200); }
.c-search__scope:focus-visible { outline: none; box-shadow: var(--c-shadow-focus); }
.c-search__scope[aria-expanded="true"] { background: var(--c-brand-50); border-color: var(--c-brand-300); }

.c-search__scope-label { max-width: 12ch; overflow: hidden; text-overflow: ellipsis; }

.c-search__scopes {
    min-width: 15rem;
    max-height: min(60vh, 26rem);
    overflow-y: auto;
    padding: var(--c-space-2);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-lg);
    background: var(--c-surface);
    box-shadow: var(--c-shadow-lg);
}

.c-search__scope-item {
    display: flex;
    align-items: center;
    gap: var(--c-space-2);
    min-height: 44px;
    padding: var(--c-space-2) var(--c-space-3);
    border-radius: var(--c-radius-md);
    color: var(--c-ink-700);
    font-size: var(--c-fs-body);
    font-weight: var(--c-fw-medium);
    text-align: left;
    white-space: nowrap;
}

.c-search__scope-item:hover,
.c-search__scope-item:focus-visible {
    background: var(--c-brand-50);
    color: var(--c-brand-700);
}

/* the applied scope is marked, so the menu states what is already on */
.c-search__scope-item.is-current {
    color: var(--c-brand-700);
    font-weight: var(--c-fw-bold);
}

.c-search__scope-item.is-current::after {
    content: "✓";
    margin-left: auto;
    font-size: var(--c-fs-sm);
}

.c-search__scope-heading {
    margin: var(--c-space-1) 0 0;
    padding: var(--c-space-2) var(--c-space-3) 0;
    color: var(--c-ink-500);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    letter-spacing: var(--c-tracking-label);
    text-transform: uppercase;
}

/* ── the submit button ───────────────────────────────────────────────────────────────────────── */
.c-search__submit {
    grid-area: submit;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--c-space-1);
    min-height: 48px;
    padding: 0 var(--c-space-6);
    border: 0;
    border-radius: var(--c-radius-pill);
    background: var(--c-brand-600);
    color: var(--c-white);
    font-family: inherit;
    font-size: var(--c-fs-h5);
    font-weight: var(--c-fw-bold);
    cursor: pointer;
    transition: background var(--c-dur-fast) var(--c-ease-standard),
                transform var(--c-dur-fast) var(--c-ease-standard);
}

.c-search__submit:hover { background: var(--c-brand-700); transform: translateY(-1px); }
.c-search__submit:active { transform: none; }
.c-search__submit:focus-visible { outline: none; box-shadow: var(--c-shadow-focus); }

/* /search carries a second submit inside the refinements panel ("Apply filters"). It is that page's
   secondary action — the bar's Search stays the primary one — so it wears the quiet treatment rather
   than a second filled purple pill competing with the first. */
.c-search__submit--secondary {
    background: var(--c-surface);
    color: var(--c-brand-700);
    border: 1px solid var(--c-brand-200);
}

.c-search__submit--secondary:hover {
    background: var(--c-brand-50);
    border-color: var(--c-brand-300);
}

.c-search__submit--secondary:focus-visible { box-shadow: var(--c-shadow-focus); }

/* ── variants ────────────────────────────────────────────────────────────────────────────────── */
.c-search--hero { margin-top: var(--c-space-6); }

.c-search--panel { max-width: none; }

@media (max-width: 767.98px) {
    /* Two rows: the field keeps the full width, the scope and the button share the second row. */
    .c-search__bar {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "icon  input  input"
            "scope scope  submit";
        row-gap: var(--c-space-2);
        padding: var(--c-space-2) var(--c-space-2) var(--c-space-2) var(--c-space-4);
        border-radius: var(--c-radius-xl);
    }

    .c-search__scope-wrap { justify-self: start; }
    .c-search__submit { padding: 0 var(--c-space-5); }
}

@media (max-width: 359.98px) {
    .c-search__scope-label { max-width: 9ch; }
}

/* the search page's refinement controls sit on their own panel: they are on a dark band and a bare
   white select floating on a gradient reads as broken */
.c-search-panel {
    margin-top: var(--c-space-5);
    padding: var(--c-space-5);
    border-radius: var(--c-radius-xl);
    background: rgb(255 255 255 / 95%);
    backdrop-filter: blur(10px);
}

.c-filter-bar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--c-space-4);
    align-items: end;
}

.c-filter-bar__field { display: grid; gap: var(--c-space-2); min-width: 0; }

.c-filter-bar__label {
    color: var(--c-ink-600);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    letter-spacing: var(--c-tracking-label);
    text-transform: uppercase;
}

.c-filter-bar .form-control,
.c-filter-bar .form-select {
    min-height: 44px;
    border-color: var(--c-border);
    border-radius: var(--c-radius-md);
    font-size: var(--c-fs-body);
}

.c-filter-bar__hint {
    margin: var(--c-space-1) 0 0;
    color: var(--c-ink-500);
    font-size: var(--c-fs-label);
    line-height: 1.4;
}

.c-filter-bar__submit { grid-column: 1 / -1; }

@media (min-width: 1200px) {
    .c-filter-bar__submit { grid-column: auto; justify-self: end; }
}

/* ── 12. Chips (Phase UI.16) ──────────────────────────────────────────────────────────────────────
   One component for every pill-shaped filter, topic and tag control: the hero's popular searches, the
   category row on the results page, the tags under an image, and the subcategory pills on a category
   card. Rest, hover, focus-visible and selected are defined once here — before this phase each surface
   carried its own recipe (the hero chip a tokenised one, the categories card an inline literal one) and
   the same visual object behaved differently on each page.

   The base metrics are the hero chip's, unchanged: 36px, 16px padding, pill, one border, 14px/500.
   Variants: `--sm` (28px, for dense metadata rows), `--static` (not interactive — a chip sitting inside
   a larger link must not light up when that link is hovered), and the selected state, which is driven
   by `aria-current` so the markup, the styling and the announcement cannot disagree. */
.c-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--c-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.c-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    min-height: 36px;
    padding: 0 var(--c-space-4);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-pill);
    background: var(--c-surface);
    color: var(--c-ink-600);
    font-family: inherit;
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-medium);
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--c-dur-fast) var(--c-ease-standard),
                border-color var(--c-dur-fast) var(--c-ease-standard),
                background-color var(--c-dur-fast) var(--c-ease-standard);
}

.c-chip__count {
    color: var(--c-text-muted);
    font-size: var(--c-fs-xs);
    font-weight: var(--c-fw-semibold);
    font-variant-numeric: tabular-nums;
}

/* Hover and keyboard focus are one affordance (UI.9): the brand tint, no movement, no shadow. */
.c-chip:hover,
.c-chip:focus-visible {
    border-color: var(--c-brand-300);
    background: var(--c-brand-50);
    color: var(--c-brand-700);
}

/* Selected: the chip the visitor is currently filtering by. Driven by aria-current so the state a
   screen reader announces is the state the eye sees. */
.c-chip[aria-current="true"],
.c-chip[aria-current="page"] {
    border-color: var(--c-brand-600);
    background: var(--c-brand-600);
    color: var(--c-white);
}

.c-chip[aria-current="true"]:hover,
.c-chip[aria-current="true"]:focus-visible,
.c-chip[aria-current="page"]:hover,
.c-chip[aria-current="page"]:focus-visible {
    border-color: var(--c-brand-700);
    background: var(--c-brand-700);
    color: var(--c-white);
}

.c-chip[aria-current] .c-chip__count { color: var(--c-brand-100); }

/* Dense rows: subcategory pills on a category card, tags under an image. */
.c-chip--sm {
    min-height: 28px;
    padding: 0 var(--c-space-3);
    gap: var(--c-space-1);
    font-size: var(--c-fs-xs);
}

/* Not a control: it labels, it does not act. Declared after the hover rule on purpose — equal
   specificity means the later state wins, so a static chip inside a hovered card stays quiet. */
.c-chip--static {
    background: var(--c-surface-sunken);
    border-color: transparent;
    color: var(--c-text-muted);
    cursor: default;
}

.c-chip--static:hover,
.c-chip--static:focus-visible {
    background: var(--c-surface-sunken);
    border-color: transparent;
    color: var(--c-text-muted);
}

/* The results page's category row: a full-width field inside the refinement grid, with the same
   label treatment as the selects beside it. */
.c-filter-bar__field--chips { grid-column: 1 / -1; }

.c-filter-bar__field--chips .c-chips { margin-top: var(--c-space-hairline); }

/* The hero row's rhythm — declared here, after `.c-chips`' own reset, so the cascade cannot undo it
   (the two are single-class selectors and the later one wins). */
.c-hero__chips { margin-top: var(--c-space-5); }

@media (max-width: 767.98px) {
    .c-hero__chips { margin-top: var(--c-space-4); }
}

/* ── 13. Loading states (Phase UI.17) ─────────────────────────────────────────────────────────────
   Nothing on the site may look blank, broken or half-drawn while it is fetching. Three shapes of the
   `c-skeleton` primitive (defined in §5) cover every placeholder the site needs, and one contract
   covers every image: a frame starts as a skeleton, the picture fades in on top of it, and a picture
   that never arrives is replaced by a controlled fallback instead of the browser's broken-image glyph.

   The fade is opt-in through `.js` — the bootstrap adds that class only while scripting is on — so a
   visitor without JavaScript sees the images immediately rather than a page of empty frames. */

/* ── shapes ── */
/* These parts are written as `<span>` in the markup that draws them (the placeholder a card becomes during
   an in-place search), and an inline box ignores `width`, `height` and `aspect-ratio` — which collapsed
   every placeholder in that grid to nothing until it was given a block box. The card's own frame is a
   `div` and was never affected; this is what the two share. */
.c-skeleton--media,
.c-skeleton--line { display: block; }

.c-skeleton--media {
    aspect-ratio: 1.2;
    border-radius: var(--c-radius-lg);
}

.c-skeleton--line {
    height: 12px;
    border-radius: var(--c-radius-pill);
}

.c-skeleton--line.is-short { width: 45%; }
.c-skeleton--line.is-medium { width: 70%; }

/* A card-shaped placeholder: the same media box and two caption lines the real card draws, so the grid
   it replaces has the same rhythm and the swap moves nothing. */
.c-skeleton--card {
    display: block;
}

.c-skeleton--card .c-skeleton--media { width: 100%; }

/* The caption block's own schedule: the block starts where the card's caption starts, the title's second
   line is tight under its first, and the byline and id line follow at the card's caption spacing. */
.c-skeleton--card .c-skeleton--line:first-of-type { margin-top: var(--c-space-3); }

.c-skeleton--card .c-skeleton--line + .c-skeleton--line { margin-top: var(--c-space-1); }

/* ── the card's own contract ─────────────────────────────────────────────────────────────────────
   `.c-card__frame` already reserves the tile's box (aspect-ratio 1.2), which is what keeps the fade and
   the failure state from moving anything. It carries `c-skeleton` while the picture is in flight. */
/* The start state is applied by the script — not by the stylesheet — and only to pictures that are
   genuinely still in flight. An image the browser already has (a cached tile, a back-navigation) is
   therefore painted at its first frame with no empty-frame flash at all, which a blanket opacity rule
   under `.js` could not do: it hid the picture until the script had run.

   One pair of states, one fade, for both kinds of picture on the site: a card's and a media box's (the
   image page's preview, the quick-view panel). The card's transition also covers the hover scale
   (UI.9), which is why this rule sits here rather than in either section. */
.c-card__img {
    transition: opacity var(--c-dur-slow) var(--c-ease-standard),
                transform var(--c-dur-slow) var(--c-ease-standard);
}

.c-media img {
    transition: opacity var(--c-dur-slow) var(--c-ease-standard);
}

.c-card__img.is-pending,
.c-media img.is-pending { opacity: 0; }

.c-card__img.is-loaded,
.c-media img.is-loaded { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
    .c-card__img,
    .c-media img { transition: none; }
}

/* The picture never arrived (offline, a purged derivative, a signed URL that expired): hide the image
   element — the browser would otherwise draw its own broken-image icon inside the frame — and show the
   fallback that ships with the card. The card itself stays usable: link, badge and actions are intact. */
.c-card__frame.is-failed .c-card__img { visibility: hidden; }

.c-card__fallback {
    position: absolute;
    inset: 0;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--c-space-2);
    padding: var(--c-space-4);
    color: var(--c-ink-500);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    text-align: center;
}

.c-card__frame.is-failed .c-card__fallback { display: flex; }

.c-card__fallback .bi { font-size: var(--c-fs-h3); color: var(--c-ink-400); }

/* A failed frame drops the inset ring the loading state uses: nothing is coming, so it must not read as
   a live placeholder. */
.c-card__frame.is-failed { box-shadow: inset 0 0 0 1px var(--c-ink-200); background: var(--c-surface-sunken); }

/* ── results-region placeholders (the AJAX search) ────────────────────────────────────────────── */
.c-skeleton-grid { pointer-events: none; }

.c-skeleton-grid .c-skeleton--card { min-width: 0; }

/* ── a failure the visitor can act on ────────────────────────────────────────────────────────── */
.c-results-notice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--c-space-2) var(--c-space-3);
    margin-bottom: var(--c-space-4);
    padding: var(--c-space-3) var(--c-space-4);
    border: 1px solid var(--c-warning-line, var(--c-border-strong));
    border-radius: var(--c-radius-md);
    background: var(--c-warning-soft, var(--c-surface-sunken));
    color: var(--c-ink-700);
    font-size: var(--c-fs-sm);
}

.c-results-notice .bi { color: var(--c-warning); }

.c-results-notice__retry {
    margin-left: auto;
    padding: var(--c-space-1) var(--c-space-3);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--c-radius-pill);
    background: var(--c-surface);
    color: var(--c-brand-700);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-bold);
    cursor: pointer;
}

.c-results-notice__retry:hover { border-color: var(--c-brand-300); background: var(--c-brand-50); }

/* ── media boxes outside the grid (the detail page's preview, the quick-view panel) ───────────── */
.c-media { position: relative; }

.c-media__fallback {
    display: none;
    align-items: center;
    justify-content: center;
    gap: var(--c-space-2);
    min-height: 12rem;
    color: var(--c-ink-500);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
}

.c-media.is-failed .c-media__fallback { display: flex; }

.c-media.is-failed img { display: none; }

/* The dialog's media box has one shape wherever it appears — the live panel and the placeholder that
   stands in for it while the request is in flight — declared here rather than repeated as inline
   `min-height`/`max-height` on each element. */
.c-quickview-media,
.c-quickview-skeleton .c-skeleton--media { min-height: 300px; max-height: 520px; }

.c-quickview-media img { min-height: 300px; max-height: 520px; }

/* The quick-view panel, before its content arrives. */
.c-quickview-skeleton {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--c-space-4);
    padding: var(--c-space-4);
}

@media (min-width: 992px) {
    .c-quickview-skeleton { grid-template-columns: 7fr 5fr; }
}

.c-quickview-skeleton__lines { display: grid; align-content: start; gap: var(--c-space-3); }

@media (prefers-reduced-motion: reduce) {
    /* A reduced-motion visitor keeps the placeholder (a frame has to look like something) and loses the
       sweep and the fade (the global rule already collapses durations; this states it for the shimmer). */
    .c-skeleton::after { display: none; }

    .c-card__img { transition: none; }
}

/* ── 14. Image page (Phase UI.18) ──────────────────────────────────────────────────────────────

   The page a purchase decision is made on, rebuilt on the system instead of beside it. Before this
   phase the page carried 142 inline-styled elements and 131 literal colours; the preview itself was a
   dark box whose own skeleton contract had been switched off inline (`aspect-ratio: auto`), so the
   page jumped 302px when the picture landed.

   The pieces:

   1. **The preview keeps its promise.** Its box is reserved from the record's own dimensions
      (`--c-preview-ratio`, computed server-side from width/height), so the picture fades into a space
      that was already the right size — the UI.17 contract, with the aspect ratio this page actually
      has instead of the card's 1.2. On desktop the preview sticks while the licence panel is read.
   2. **Two columns, one rhythm.** Left: the asset (preview, protected-preview line, share). Right:
      identity (tags, title, id, contributor, description), the licence chooser, the specs, the tags.
      Every gap is a spacing token; the sections below use the UI.12 head pattern.
   3. **The licence chooser is a set of radios, styled once.** Selection is a class on the label
      (`is-selected`), not four inline style writes from a script, and the price, the hidden licence id
      and the cart form all follow the same source of truth.
   4. **A failure is still a page.** The preview's skeleton, its fallback and its zoom control all
      survive a missing derivative (see §13), and nothing in this section depends on an image having
      loaded. ── */

/* ── the breadcrumb bar ─────────────────────────────────────────────────────────────────────── */
.c-breadcrumb {
    padding-block: var(--c-space-3);
    background: var(--c-surface-sunken);
    border-bottom: 1px solid var(--c-border);
}

.c-breadcrumb__list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--c-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--c-fs-sm);
    color: var(--c-ink-500);
}

.c-breadcrumb__item + .c-breadcrumb__item::before {
    content: "/";
    margin-inline-end: var(--c-space-2);
    color: var(--c-ink-300);
}

.c-breadcrumb__link {
    color: var(--c-brand-600);
    text-decoration: none;
}

.c-breadcrumb__link:hover,
.c-breadcrumb__link:focus-visible {
    text-decoration: underline;
}

/* ── the page block ─────────────────────────────────────────────────────────────────────────── */
.c-detail {
    padding-block: var(--c-space-6) var(--c-section-y-tight);
}

.c-detail__media {
    min-width: 0;
}

/* A visitor who scrolls to read the licence terms should not lose the picture they are buying. The
   preview alone sticks — the caption and share bar below it scroll — so the pinned block always fits
   a normal viewport. */
@media (min-width: 992px) {
    .c-detail__preview {
        position: sticky;
        top: var(--c-space-6);
    }
}

/* ── the preview ────────────────────────────────────────────────────────────────────────────── */
.asset-preview {
    position: relative;
    display: grid;
    place-items: center;
    overflow: hidden;
    border-radius: var(--c-radius-xl);
    background: var(--c-ink-900);
    /* The record's own shape, passed in as `--c-preview-ratio` (a computed value, not a design
       decision — no token can know that this asset is 1600×1200). A missing record falls back to the
       catalogue's card ratio, so the box is always reserved and the page never shifts on load. */
    aspect-ratio: var(--c-preview-ratio, 1.2);
}

.asset-preview img {
    display: block;
    width: 100%;
    height: auto;
}

.c-preview__zoom {
    position: absolute;
    inset-block-end: var(--c-space-4);
    inset-inline-end: var(--c-space-4);
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    padding: var(--c-space-2) var(--c-space-4);
    border: 0;
    border-radius: var(--c-radius-md);
    background: rgb(255 255 255 / 92%);
    color: var(--c-ink-800);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
    box-shadow: var(--c-shadow-sm);
    backdrop-filter: blur(10px);
    transition: background var(--c-dur-fast) var(--c-ease-standard), transform var(--c-dur-fast) var(--c-ease-standard);
}

.c-preview__zoom:hover {
    background: var(--c-white);
    transform: translateY(-1px);
}

.c-detail__caption {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--c-space-3);
    margin-top: var(--c-space-3);
}

.c-detail__note {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    margin: 0;
    color: var(--c-ink-500);
    font-size: var(--c-fs-sm);
}

/* The quiet, secondary control: present when looked for, never competing with a purchase action. */
.c-link-button {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    padding: var(--c-space-2) var(--c-space-3);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-sm);
    background: var(--c-surface);
    color: var(--c-ink-600);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
    transition: border-color var(--c-dur-fast) var(--c-ease-standard), color var(--c-dur-fast) var(--c-ease-standard);
}

.c-link-button:hover {
    border-color: var(--c-brand-300);
    color: var(--c-brand-700);
}

/* ── identity ───────────────────────────────────────────────────────────────────────────────── */
.c-detail__title {
    margin: var(--c-space-3) 0 var(--c-space-2);
}

.c-idpill {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-1);
    padding: 2px var(--c-space-2);
    border-radius: var(--c-radius-sm);
    background: var(--c-ink-100);
    color: var(--c-ink-600);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
    letter-spacing: var(--c-tracking-label);
}

.c-idpill strong {
    color: var(--c-ink-900);
    font-variant-numeric: tabular-nums;
}

.c-idcopy {
    display: inline-flex;
    align-items: center;
    padding: var(--c-space-1);
    border: 0;
    background: none;
    color: var(--c-ink-500);
    font-size: var(--c-fs-xs);
    cursor: pointer;
    border-radius: var(--c-radius-xs);
}

.c-idcopy:hover { color: var(--c-brand-600); }

.c-detail__identity {
    display: flex;
    align-items: center;
    gap: var(--c-space-2);
    margin-bottom: var(--c-space-2);
}

.c-detail__byline {
    color: var(--c-brand-600);
    font-weight: var(--c-fw-semibold);
    text-decoration: none;
}

.c-detail__byline:hover { text-decoration: underline; }

.c-detail__description {
    margin: var(--c-space-4) 0 0;
    color: var(--c-ink-600);
    line-height: var(--c-lh-body);
}

/* ── the licence panel ──────────────────────────────────────────────────────────────────────── */
.purchase-panel {
    margin-top: var(--c-space-5);
    padding: var(--c-space-5);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-xl);
    background: var(--c-surface);
    box-shadow: var(--c-shadow-sm);
}

.c-purchase__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--c-space-3);
    margin-bottom: var(--c-space-4);
}

.c-purchase__label {
    display: block;
    /* UI.22: 13px text, not a placeholder — `--c-ink-400` measured 2.98:1 here and failed WCAG AA. */
    color: var(--c-ink-500);
    font-size: var(--c-fs-xs);
}

.c-purchase__price {
    font-size: var(--c-fs-display);
    font-weight: var(--c-fw-black);
    line-height: var(--c-lh-display);
    letter-spacing: var(--c-tracking-display);
    color: var(--c-ink-900);
    font-variant-numeric: tabular-nums;
}

.c-wishlist {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    flex: 0 0 auto;
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-pill);
    background: var(--c-brand-50);
    color: var(--c-danger);
    font-size: var(--c-fs-h3);
    cursor: pointer;
    transition: background var(--c-dur-fast) var(--c-ease-standard), border-color var(--c-dur-fast) var(--c-ease-standard);
}

.c-wishlist:hover {
    background: var(--c-brand-100);
    border-color: var(--c-brand-300);
}

/* One row per licence: a real radio inside a label, so the choice is announced by the browser and
   keyboard-selectable with the arrow keys, with the visual state driven by the class the script sets
   (and by `:has()` where the browser supports it — the chosen row is styled even before any script
   runs, because the server marks the first option selected). */
.c-licence {
    display: flex;
    gap: var(--c-space-3);
    padding: var(--c-space-3);
    margin-bottom: var(--c-space-2);
    border: 2px solid var(--c-border);
    border-radius: var(--c-radius-lg);
    background: var(--c-surface);
    cursor: pointer;
    transition: border-color var(--c-dur-fast) var(--c-ease-standard), background var(--c-dur-fast) var(--c-ease-standard);
}

.c-licence:hover { border-color: var(--c-border-strong); }

.c-licence.is-selected {
    border-color: var(--c-brand-600);
    background: var(--c-brand-50);
}

.c-licence:has(input:checked) {
    border-color: var(--c-brand-600);
    background: var(--c-brand-50);
}

.c-licence input[type="radio"] {
    margin-top: 2px;
    flex: 0 0 auto;
    accent-color: var(--c-brand-600);
}

.c-licence__body { flex: 1 1 auto; min-width: 0; }

.c-licence__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--c-space-3);
}

.c-licence__name {
    color: var(--c-ink-900);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-bold);
}

.c-licence__price {
    color: var(--c-brand-600);
    font-weight: var(--c-fw-black);
    font-variant-numeric: tabular-nums;
}

.c-licence__desc {
    margin: var(--c-space-1) 0 0;
    color: var(--c-ink-600);
    font-size: var(--c-fs-xs);
    line-height: var(--c-lh-body);
}

.c-licence__flags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--c-space-1);
    margin-top: var(--c-space-2);
}

.c-flag {
    padding: 1px var(--c-space-2);
    border-radius: var(--c-radius-sm);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-semibold);
}

.c-flag--commercial { background: var(--c-accent-100); color: var(--c-accent-800); }
.c-flag--extended { background: var(--c-warning-soft); color: var(--c-warning); }
.c-flag--attribution { background: var(--c-brand-100); color: var(--c-brand-700); }

.c-link-quiet {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-1);
    padding: 0;
    border: 0;
    background: none;
    color: var(--c-brand-600);
    font-size: var(--c-fs-xs);
    font-weight: var(--c-fw-semibold);
    cursor: pointer;
}

.c-link-quiet:hover { text-decoration: underline; }

/* An owned licence: a fact, not an advertisement. */
.c-owned {
    display: flex;
    align-items: center;
    gap: var(--c-space-2);
    padding: var(--c-space-2) var(--c-space-3);
    margin-bottom: var(--c-space-2);
    border-radius: var(--c-radius-md);
    background: var(--c-accent-100);
    color: var(--c-accent-800);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
}

.c-purchase__field-label {
    display: block;
    margin-bottom: var(--c-space-1);
    color: var(--c-ink-600);
    font-size: var(--c-fs-xs);
    font-weight: var(--c-fw-semibold);
}

.c-purchase .form-control,
.c-purchase .form-select {
    border-color: var(--c-border);
    font-size: var(--c-fs-sm);
}

.c-purchase .form-control:focus,
.c-purchase .form-select:focus {
    border-color: var(--c-brand-400);
    box-shadow: var(--c-shadow-focus);
}

.c-purchase__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--c-space-2);
    margin-bottom: var(--c-space-2);
}

.c-btn-buy {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--c-space-2);
    padding: var(--c-space-3) var(--c-space-5);
    border: 1px solid var(--c-brand-600);
    border-radius: var(--c-radius-md);
    background: var(--c-brand-600);
    color: var(--c-white);
    font-weight: var(--c-fw-bold);
    transition: background var(--c-dur-fast) var(--c-ease-standard);
}

.c-btn-buy:hover { background: var(--c-brand-700); border-color: var(--c-brand-700); }
.c-btn-buy:disabled { background: var(--c-ink-300); border-color: var(--c-ink-300); }

.c-btn-outline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--c-space-2);
    width: 100%;
    padding: var(--c-space-2) var(--c-space-4);
    border: 2px solid var(--c-brand-600);
    border-radius: var(--c-radius-md);
    background: var(--c-surface);
    color: var(--c-brand-600);
    font-weight: var(--c-fw-bold);
    transition: background var(--c-dur-fast) var(--c-ease-standard);
}

.c-btn-outline:hover { background: var(--c-brand-50); }

.c-purchase__cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--c-space-2);
    width: 100%;
    margin-top: var(--c-space-2);
    padding: var(--c-space-3) var(--c-space-5);
    border: 1px solid var(--c-brand-600);
    border-radius: var(--c-radius-md);
    background: var(--c-brand-600);
    color: var(--c-white);
    font-weight: var(--c-fw-bold);
    text-decoration: none;
}

.c-purchase__cta:hover { background: var(--c-brand-700); }

.c-purchase__tax {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    gap: var(--c-space-2);
    margin-top: var(--c-space-2);
}

/* ── the recorded facts ─────────────────────────────────────────────────────────────────────── */
/* The recorded facts, as a data row under the asset. Two columns on a phone, as many as fit from the
   tablet width up — the same breakpoint schedule the gallery uses, so the page keeps one column
   language. */
.c-specs {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--c-space-3);
    margin: 0;
}

@media (min-width: 576px)  { .c-specs { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 992px)  { .c-specs { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1200px) { .c-specs { grid-template-columns: repeat(7, minmax(0, 1fr)); } }

.c-spec {
    padding: var(--c-space-4);
    border-radius: var(--c-radius-lg);
    background: var(--c-surface-sunken);
    text-align: center;
}

.c-spec__icon {
    color: var(--c-brand-600);
    font-size: var(--c-fs-h4);
}

.c-spec__value {
    margin-top: var(--c-space-1);
    color: var(--c-ink-900);
    font-weight: var(--c-fw-black);
    font-variant-numeric: tabular-nums;
}

.c-spec__label {
    /* UI.22: this is 12px text, not a placeholder — `--c-ink-400` measured 2.72:1 against its panel
       and failed WCAG AA. The muted token measures 5.0:1 here. */
    color: var(--c-ink-500);
    font-size: var(--c-fs-label);
}

.c-detail__tags { margin-top: var(--c-space-5); }

/* ── the terms dialog ───────────────────────────────────────────────────────────────────────── */
.c-terms__heading {
    margin: 0 0 var(--c-space-2);
    font-size: var(--c-fs-h6);
    font-weight: var(--c-fw-semibold);
    letter-spacing: var(--c-tracking-label);
    line-height: var(--c-lh-heading);
    color: var(--c-ink-900);
}

.c-terms__section + .c-terms__section {
    margin-top: var(--c-space-5);
    padding-top: var(--c-space-5);
    border-top: 1px solid var(--c-border);
}

.c-terms__text {
    margin-top: var(--c-space-2);
    color: var(--c-ink-700);
    font-size: var(--c-fs-sm);
    line-height: var(--c-lh-body);
}

.c-terms__label {
    font-size: var(--c-fs-sm);
}

.c-terms__label--ok { color: var(--c-success); }
.c-terms__label--no { color: var(--c-danger); }

.c-terms__note {
    margin-top: var(--c-space-4);
    padding: var(--c-space-4);
    border-radius: var(--c-radius-md);
    background: var(--c-surface-sunken);
    color: var(--c-ink-600);
    font-size: var(--c-fs-xs);
}

.c-terms__note i { margin-inline-end: var(--c-space-1); }

/* ── the zoom overlay ───────────────────────────────────────────────────────────────────────── */
#imageZoomModal .modal-content { border: 0; background: var(--c-ink-900); }
#imageZoomModal .modal-body img { border-radius: var(--c-radius-md); }
#imageZoomModal .c-zoom__caption {
    display: flex;
    align-items: center;
    gap: var(--c-space-2);
    color: var(--c-white);
    font-size: var(--c-fs-sm);
}

/* On a phone the control becomes an icon: at that size the labelled pill reaches across the image's
   own id band, and the label adds nothing a magnifier does not already say. The accessible name stays. */
@media (max-width: 575.98px) {
    .c-preview__zoom { padding: var(--c-space-2); }
    .c-preview__zoom-label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .c-preview__zoom { transition: none; }
    .c-licence { transition: none; }
}

/* Small pieces the image page needs that are not worth their own block. */
.c-purchase__options { margin-bottom: var(--c-space-4); }

.c-link-quiet--accent { color: var(--c-accent-800); }

.c-terms { border: 0; border-radius: var(--c-radius-xl); }

.c-terms .modal-header { padding: var(--c-space-5) var(--c-space-5) 0; border-bottom: 0; }

.c-terms .modal-body { padding: var(--c-space-5); }

/* The identity row's chips sit above the title, where the title's own margin takes over. */
.c-detail__title + .c-detail__identity,
.c-chips + .c-detail__title { margin-top: var(--c-space-3); }

/* ── the share bar (UI.18) ──────────────────────────────────────────────────────────────────────
   The bar was seven inline style attributes with their own greys; it now draws from the palette. The
   markup that carries the behaviour (channels, endpoints, the recorded count) is untouched — this is
   the same component, painted by the system. */
.c-share {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--c-space-2);
}

.c-share__label {
    margin-inline-end: var(--c-space-1);
    color: var(--c-ink-600);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
}

.c-share__channel {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-1);
    padding: var(--c-space-1) var(--c-space-3);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-sm);
    background: var(--c-surface);
    color: var(--c-ink-600);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
    text-decoration: none;
    transition: border-color var(--c-dur-fast) var(--c-ease-standard), color var(--c-dur-fast) var(--c-ease-standard);
}

.c-share__channel:hover,
.c-share__channel:focus-visible {
    border-color: var(--c-brand-300);
    color: var(--c-brand-700);
}

.c-share__count,
.c-share__note {
    /* UI.22: both are 13px body text — `--c-ink-400` measured 2.98:1 against the page and failed AA. */
    color: var(--c-ink-500);
    font-size: var(--c-fs-xs);
}

.c-share__count { align-self: center; }

.c-share__note { margin: var(--c-space-2) 0 0; }

@media (prefers-reduced-motion: reduce) {
    .c-share__channel { transition: none; }
}

/* The mandatory policy acceptance inside a checkout form (Module 11.6's component, painted by UI.18).
   It appears on the image page, in the cart and on the pricing page, so the box is a design-system
   citizen — one set of colours, in the palette, instead of four literals per surface. */
.c-policy {
    margin: var(--c-space-2) 0;
    padding: var(--c-space-3);
    border: 1px solid var(--c-brand-200);
    border-radius: var(--c-radius-md);
    background: var(--c-surface-sunken);
}

.c-policy__check {
    display: flex;
    align-items: flex-start;
    gap: var(--c-space-2);
    margin: 0;
}

.c-policy__label {
    margin: 0;
    color: var(--c-ink-700);
    font-size: var(--c-fs-xs);
    line-height: var(--c-lh-body);
}

.c-policy__link {
    color: var(--c-brand-600);
    font-weight: var(--c-fw-semibold);
}

.c-policy__note {
    margin: var(--c-space-1) 0 0;
    color: var(--c-ink-400);
    font-size: var(--c-fs-label);
    line-height: 1.5;
}

.c-policy__error {
    margin: var(--c-space-1) 0 0;
    color: var(--c-danger);
    font-size: var(--c-fs-label);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   PHASE UI.19 — interaction timing: the system owns the primitives

   Bootstrap ships its own timing (`transition: .15s ease-in-out` on controls and accordions, a 300ms
   ease-out on the modal dialog, a 350ms ease on a collapsing panel), and those numbers reach the page
   whenever a Bootstrap primitive is used directly. Measured on the running site: five off-ladder
   recipes were live on every public page — 160ms from the legacy button recipe, 150ms ease-in-out and
   300ms ease-out from Bootstrap, 300ms ease from the legacy cards. Each is now the design system's own
   step and easing, so "short, fast, purposeful" is one ladder with one vocabulary rather than four
   sheets disagreeing.

   Two of these are deliberate layout animations and are the only exception the probe allows: a
   disclosure panel has to animate its height, because that is what opening one means. They are still
   on the ladder.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

.form-control,
.form-select {
    transition: border-color var(--c-dur-fast) var(--c-ease-standard),
                box-shadow var(--c-dur-fast) var(--c-ease-standard),
                background-color var(--c-dur-fast) var(--c-ease-standard);
}

.accordion-button {
    transition: color var(--c-dur-fast) var(--c-ease-standard),
                background-color var(--c-dur-fast) var(--c-ease-standard),
                border-color var(--c-dur-fast) var(--c-ease-standard),
                box-shadow var(--c-dur-fast) var(--c-ease-standard),
                border-radius var(--c-dur-fast) var(--c-ease-standard);
}

/* the accordion's chevron turns on the same step as everything else */
.accordion-button::after {
    transition: transform var(--c-dur-fast) var(--c-ease-standard);
}

/* A dialog arrives, it does not slide in: the entrance easing, one base step. */
.modal.fade .modal-dialog {
    transition: transform var(--c-dur-base) var(--c-ease-entrance);
}

/* Bootstrap's own fade, on the ladder rather than a stray 150ms linear. */
.fade {
    transition: opacity var(--c-dur-fast) var(--c-ease-standard);
}

/* The one place a layout property is allowed to move: a panel opening has to reveal its height.
   Both the mobile navigation and the FAQ accordions are this element. */
.collapsing {
    transition: height var(--c-dur-base) var(--c-ease-standard);
}

@media (prefers-reduced-motion: reduce) {
    .form-control,
    .form-select,
    .accordion-button,
    .accordion-button::after,
    .modal.fade .modal-dialog,
    .fade,
    .collapsing { transition: none; }
}

/* ── 16. Responsive pass (Phase UI.20) ─────────────────────────────────────────────────────────
   Verified by `_tools/shot/responsive_probe.js` at 320 / 375 / 390 / 430 / 768 / 1024 / 1366 /
   1440 / 1920. Each rule below closes a defect that probe reproduced; nothing here changes how
   the page looks at ≥1200, where the earlier phases were pinned. */

/* The desktop bar needs 1056px (brand 126 + links 716 + actions 182 + two 16px gaps) but Bootstrap's
   lg container stops at 960px, so between 992 and ~1104 the account cluster ran past the container
   and the shell's `overflow-x: clip` cut "Join free". In this one band the bar goes fluid and its
   own spacing tightens by 128px — enough at 992, the tightest point, and invisible above 1104. */
@media (min-width: 992px) and (max-width: 1199.98px) {
    .c-navbar > .container { max-width: 100%; }
    .c-navbar__inner { gap: var(--c-space-2); }             /* 16 → 8  */
    .c-navbar__collapse { gap: var(--c-space-2); }          /* 16 → 8  */
    .c-nav-link { padding-inline: var(--c-space-2); }       /* 12 → 8  */
    .c-navbar__links { gap: 0; }                            /* 4 → 0   */
    .c-navbar__actions { gap: var(--c-space-1); }           /* 8 → 4   */
    .c-navbar__action { padding-inline: var(--c-space-3); } /* 16 → 12 */
}

/* Footer newsletter: the e-mail field collapsed to 0px and the Subscribe label was cut in the
   1024–1366 band — both flex children were shrinking. The input yields; the button never does. */
.c-newsletter .form-control { flex: 1 1 auto; min-width: 0; }
.c-newsletter .btn { flex: 0 0 auto; min-width: max-content; }
@media (max-width: 575.98px) {
    .c-newsletter { flex-wrap: wrap; }
    .c-newsletter .form-control { flex: 1 1 100%; }
    .c-newsletter .btn { flex: 1 1 100%; justify-content: center; }
}

/* Touch widths: every standalone target reaches the 24×24 minimum (WCAG 2.5.8). */
@media (max-width: 430px) {
    .c-link-quiet { min-height: 24px; }
    .auth-page .form-check { min-height: 24px; }
    .auth-page .form-check-label { display: inline-flex; align-items: center; min-height: 24px; }
    .auth-page a.small.fw-semibold { display: inline-flex; align-items: center; min-height: 24px; }
}

/* UI.22 — the banner wrapper around the top bar. Making the wrapper sticky (rather than only the nav
   inside it) keeps the whole bar pinned: a sticky child is confined to its parent's box, and this
   wrapper spans only the bar's own height. The nav keeps its own sticky rule for layouts that do not
   use this wrapper. */
.c-site-header {
    position: sticky;
    top: 0;
    z-index: var(--c-z-sticky);
}

/* ── 17. Font-loading stability (Phase UI.21) ─────────────────────────────────────────────────────
   Inter is served by Google Fonts with `display=swap`: the browser paints the page in the fallback
   sans, then re-lays it out when Inter lands. Measured on a cold visit, that fallback draws 6–13%
   wider than Inter, and the reflow was a real layout shift — on a 390px phone the search heading
   rewrapped from one line to two (78px → 39px) and dragged the panel up 64px (CLS 0.059); at 1440
   the share buttons narrowed 8–15px and slid their row sideways (CLS 0.058).

   These two faces scale whichever local sans the device has to Inter's own width, so the swap no
   longer changes where lines break. Widths measured in the verification environment (Inter width ÷
   fallback width): regular weights 0.925–0.951 → 94%; 600–900 weights 0.853–0.906 → 87.5%. The
   vertical metrics are Inter's own, so `line-height: normal` boxes line up as well. The faces are
   used only while Inter is loading or unavailable — once Inter is present, nothing here applies. */
@font-face {
    font-family: 'Inter Fallback';
    src: local('Arial'), local('Helvetica Neue'), local('Helvetica'), local('Segoe UI'), local('Roboto'),
         local('Liberation Sans'), local('DejaVu Sans'), local('Verdana');
    font-weight: 100 500;
    size-adjust: 94%;
    ascent-override: 96.875%;
    descent-override: 24.121%;
    line-gap-override: 0%;
}
@font-face {
    font-family: 'Inter Fallback';
    src: local('Arial'), local('Helvetica Neue'), local('Helvetica'), local('Segoe UI'), local('Roboto'),
         local('Liberation Sans'), local('DejaVu Sans'), local('Verdana');
    font-weight: 600 900;
    size-adjust: 87.5%;
    ascent-override: 96.875%;
    descent-override: 24.121%;
    line-gap-override: 0%;
}

/* Icon glyphs are a second webfont with exactly the same problem, and a worse one: before
   `bootstrap-icons` lands, the glyph is drawn by whatever sans the element falls back to and measures
   ~10px at 16px type; afterwards the real icon measures ~15.2px. That 5px per icon re-wrapped the
   share row on the image page and nudged the zoom control (measured: CLS 0.027 at 320px, the last
   shift anywhere on a cold visit). The glyph box is pinned to 1em instead, so the advance is the same
   whichever font draws it — the icon can then arrive whenever it likes without moving anything. */
[class^="bi-"]::before,
[class*=" bi-"]::before,
.bi::before {
    width: 1em;
    text-align: center;
}

/* ── 18. Bootstrap variable remap (Phase UI.23) ───────────────────────────────────────────────────
   Bootstrap is the substrate, never the look. Its components pin their colours, radii and type to
   their own private variables, so a page can wear the design system everywhere *except* inside a
   dropdown, a modal or an accordion — measured, not guessed: the mega menu, the terms dialog and the
   FAQ all resolved their text to Bootstrap's stock #212529 while the rest of the page rendered the
   system ink, and `.form-select` still painted Bootstrap's own chevron. Remapping the variables the
   components actually read keeps Bootstrap's behaviour (menus, focus management, collapse) and
   replaces its appearance everywhere at once, instead of chasing components one by one. */
:root,
[data-bs-theme="light"] {
    --bs-body-color: var(--c-ink-900);
    --bs-body-bg: var(--c-white);
    --bs-body-font-family: var(--c-font-sans);
    --bs-border-color: var(--c-border);
    --bs-border-color-translucent: var(--c-border);
    --bs-border-radius: var(--c-radius-md);
    --bs-border-radius-sm: var(--c-radius-sm);
    --bs-border-radius-lg: var(--c-radius-lg);
    --bs-border-radius-xl: var(--c-radius-lg);
    --bs-border-radius-xxl: var(--c-radius-lg);
    /* Bootstrap reads link and theme colours as RGB triples; these mirror --c-brand-600 (#0067a8)
       and the §18 hover red exactly. */
    --bs-link-color-rgb: 0, 103, 168;
    --bs-link-hover-color-rgb: 244, 23, 23;
    --bs-primary-rgb: 0, 103, 168;
    --bs-secondary-rgb: 82, 101, 120;
    /* Bootstrap's semantic utilities (.text-success and friends) read these triples, so remapping them
       makes any such utility render the design token — no `!important` fights with Bootstrap's own
       utility rules. Mirrors: --c-success #177a3d, --c-danger #c00d0d, --c-warning #946200. */
    --bs-success-rgb: 23, 122, 61;
    --bs-danger-rgb: 192, 13, 13;
    --bs-warning-rgb: 148, 98, 0;
    --bs-dropdown-color: var(--c-ink-900);
    --bs-dropdown-link-color: var(--c-ink-700);
    --bs-dropdown-link-hover-color: var(--c-ink-900);
    --bs-dropdown-link-hover-bg: var(--c-surface-sunken);
    --bs-dropdown-border-color: var(--c-border);
    --bs-modal-color: var(--c-ink-900);
    --bs-modal-border-color: var(--c-border);
    --bs-accordion-color: var(--c-ink-900);
    --bs-accordion-btn-color: var(--c-ink-900);
    --bs-accordion-btn-bg: var(--c-white);
    --bs-accordion-active-color: var(--c-brand-700);
    --bs-accordion-active-bg: var(--c-brand-50);
    --bs-accordion-border-color: var(--c-border);
    --bs-accordion-border-radius: var(--c-radius-md);
}

/* The public accordion (the FAQ page and the Sell-your-work page), wearing the system. The FAQ markup
   used to patch Bootstrap's variables with an inline style attribute; these rules replace that patch
   and apply to every accordion on the public site. */
.c-faq .accordion-item,
.sell-faq .accordion-item {
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-md);
    overflow: hidden;
    background: var(--c-white);
    margin-bottom: var(--c-space-3);
}

.c-faq .accordion-button,
.sell-faq .accordion-button {
    padding: var(--c-space-4) var(--c-space-5);
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
    background: var(--c-white);
}

.c-faq .accordion-body,
.sell-faq .accordion-body {
    color: var(--c-ink-500);
    line-height: var(--c-lh-body);
}

/* Our own chevron on every select on the public site — Bootstrap's stock arrow (stroke #343a40) is one
   of the clearest "stock Bootstrap" tells. The stroke mirrors --c-ink-500 (#67677f). */
.form-select {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3e%3cpath stroke='%2367677f' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
    background-size: 16px 12px;
    background-position: right var(--c-space-3) center;
    background-repeat: no-repeat;
    padding-right: var(--c-space-6);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════
   §19  UI.26 — one page head, one empty state, one page body

   UI.18 moved the image page fully into the system; the template pages still carried the pre-UI
   markup: twenty hand-rolled `<section class="collection-header" style="background:linear-gradient(…">`
   heads with raw hex, ~350 inline styles and a centred/left split that depended on which page you were
   looking at. This section replaces all of it with one component per job:

     .c-page-head            every public page's opening band (light, tokenised, hairline)
     .c-page-head--dark      the same band on a landing page that opens dark (contributor studio)
     .c-page-head--center    aligns the head with a centred body (pricing's card grid)
     .c-empty                the professional empty state a fresh install shows (blog, collections, …)
     .c-page-body            the content column under a page head (rhythm, width, first/last-child gaps)

   The rule the probe enforces: a page head aligns with the content beneath it, and every page head on
   the site is the same component with the same internal spacing.
   ═══════════════════════════════════════════════════════════════════════════════════════════════════ */

.c-page-head {
    position: relative;
    isolation: isolate;
    padding-block: var(--c-space-7) var(--c-space-6);
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-border);
}

/* The wash is the same tinted-band language the redesigned sections use — brand at 4%, never a raw
   gradient, and it never touches the text contrast (verified by a11y_probe on every page it ships on). */
.c-page-head::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(120% 140% at 12% -20%, color-mix(in srgb, var(--c-brand-500) 7%, transparent) 0%, transparent 58%),
        radial-gradient(90% 130% at 88% 0%, color-mix(in srgb, var(--c-accent-500) 6%, transparent) 0%, transparent 60%);
}

.c-page-head__inner {
    max-width: 72ch;
}

.c-page-head__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--c-space-2);
    margin: 0 0 var(--c-space-3);
    padding: var(--c-space-1) var(--c-space-3);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-bold);
    letter-spacing: var(--c-tracking-label);
    text-transform: uppercase;
    color: var(--c-brand-700);
    background: color-mix(in srgb, var(--c-brand-500) 9%, transparent);
    border: 1px solid color-mix(in srgb, var(--c-brand-500) 18%, transparent);
    border-radius: var(--c-radius-pill);
}

.c-page-head__title {
    margin: 0;
    font-size: var(--c-fs-h1);
    font-weight: var(--c-fw-black);
    letter-spacing: var(--c-tracking-heading);
    line-height: var(--c-lh-heading);
    color: var(--c-ink-900);
    text-wrap: balance;
}

.c-page-head__lede {
    margin: var(--c-space-4) 0 0;
    max-width: 62ch;
    font-size: var(--c-fs-lg);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

.c-page-head__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--c-space-3);
    margin-top: var(--c-space-5);
}

/* Centred variant: only where the body beneath is itself centred (the pricing grid).
   The inner column is wide enough that the shortest page titles still fit one line — the lede keeps its
   own 62ch measure, so the reading width is unchanged. */
.c-page-head--center .c-page-head__inner {
    max-width: 46rem;
    margin-inline: auto;
    text-align: center;
}

.c-page-head--center .c-page-head__lede {
    margin-inline: auto;
}

.c-page-head--center .c-page-head__actions {
    justify-content: center;
}

/* Dark variant: a landing page that opens dark keeps its dark opening, but built from tokens and with
   the same internal spacing as every other head, so the two are recognisably one component. */
.c-page-head--dark {
    background:
        radial-gradient(120% 160% at 8% -30%, color-mix(in srgb, var(--c-brand-600) 55%, transparent) 0%, transparent 62%),
        linear-gradient(150deg, var(--c-ink-900) 0%, var(--c-brand-900) 78%);
    border-bottom-color: color-mix(in srgb, var(--c-white) 14%, transparent);
}

.c-page-head--dark::before {
    background:
        radial-gradient(80% 120% at 92% 8%, color-mix(in srgb, var(--c-accent-500) 22%, transparent) 0%, transparent 58%);
}

.c-page-head--dark .c-page-head__title { color: var(--c-white); }
.c-page-head--dark .c-page-head__lede  { color: rgb(255 255 255 / 74%); }

.c-page-head--dark .c-page-head__eyebrow {
    color: var(--c-accent-300);
    background: color-mix(in srgb, var(--c-white) 10%, transparent);
    border-color: color-mix(in srgb, var(--c-white) 22%, transparent);
}

/* ── the page body under a head ─────────────────────────────────────────────────────────────────── */

.c-page-body {
    padding-block: var(--c-space-8) var(--c-space-9);
}

.c-page-body > * + * {
    margin-top: var(--c-space-8);
}

.c-page-body > :first-child { margin-top: 0; }

/* ── the empty state ─────────────────────────────────────────────────────────────────────────────
   A fresh install genuinely has no blog posts, no public collections, no press quotes. That is a
   normal state and it must look deliberate rather than broken: one panel, one glyph, one sentence,
   one action — instead of the bare icon + two floating lines the template pages shipped. */

.c-empty {
    display: grid;
    justify-items: center;
    gap: var(--c-space-3);
    padding: var(--c-space-8) var(--c-space-6);
    text-align: center;
    background: var(--c-surface);
    border: 1px dashed var(--c-border-strong);
    border-radius: var(--c-radius-xl);
}

.c-empty__icon {
    display: grid;
    place-items: center;
    width: 56px;
    height: 56px;
    font-size: 24px;
    color: var(--c-brand-600);
    background: color-mix(in srgb, var(--c-brand-500) 10%, transparent);
    border-radius: var(--c-radius-pill);
}

.c-empty__title {
    margin: 0;
    font-size: var(--c-fs-h4);
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
}

.c-empty__text {
    margin: 0;
    max-width: 46ch;
    font-size: var(--c-fs-sm);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

.c-empty__action {
    margin-top: var(--c-space-2);
}

/* A grid of cards that has nothing to show keeps its section padding, not a collapsed band. */
.c-grid-empty {
    grid-column: 1 / -1;
}

/* ── §19 (cont.) — the patterns the template pages had written inline ─────────────────────────────
   Every rule below replaces a repeated inline style found across the public views: a white panel, a
   tinted panel, an icon list, a long-form prose column, a tile grid, a closing call to action and the
   pricing plan card. The names are the ones the redesigned pages already speak. */

.c-panel {
    padding: var(--c-space-6);
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-xl);
}

.c-panel--tinted { background: var(--c-surface-sunken); }

.c-panel--accent {
    background: color-mix(in srgb, var(--c-brand-500) 5%, var(--c-surface));
    border-color: color-mix(in srgb, var(--c-brand-500) 16%, var(--c-border));
}

.c-panel__title {
    margin: 0 0 var(--c-space-2);
    font-size: var(--c-fs-h4);
    font-weight: var(--c-fw-black);   /* h2-carrying panel titles must read as headings (probe-enforced) */
    color: var(--c-ink-900);
}

.c-panel__text {
    margin: 0;
    font-size: var(--c-fs-sm);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

/* An icon-led row: the glyph in a soft brand chip, then a title and a line of copy. Used by the
   “how it works” bands (licensing guide, usage guide) and the feature tiles. */
.c-tile-grid {
    display: grid;
    gap: var(--c-space-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

.c-tile {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--c-space-4);
    align-items: start;
    padding: var(--c-space-5);
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-lg);
}

.c-tile__icon {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    font-size: 20px;
    color: var(--c-brand-700);
    background: color-mix(in srgb, var(--c-brand-500) 10%, transparent);
    border-radius: var(--c-radius-md);
}

.c-tile__icon--accent {
    color: var(--c-accent-800);
    background: color-mix(in srgb, var(--c-accent-500) 14%, transparent);
}

.c-tile__title {
    margin: 0 0 var(--c-space-1);
    font-size: var(--c-fs-h4);
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
}

.c-tile__text {
    margin: 0;
    font-size: var(--c-fs-sm);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

/* A checklist: the brand tick, then the line. Replaces “<i style="color:#059669">” repeated by hand. */
.c-checklist {
    display: grid;
    gap: var(--c-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.c-checklist li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--c-space-3);
    align-items: start;
    font-size: var(--c-fs-sm);
    line-height: var(--c-lh-body);
    color: var(--c-text);
}

.c-checklist .bi {
    margin-top: 0.15em;
    color: var(--c-accent-700);
}

.c-checklist--brand .bi { color: var(--c-brand-600); }

/* Long-form copy (about, legal, policy pages): one column, measured width, real paragraph rhythm. */
.c-prose {
    max-width: 72ch;
    font-size: var(--c-fs-body);
    line-height: var(--c-lh-body);
    color: var(--c-text);
}

.c-prose > * + * { margin-top: var(--c-space-4); }

.c-prose :where(h2) {
    margin: var(--c-space-7) 0 0;
    font-size: var(--c-fs-h3);
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
}

.c-prose :where(h3) {
    margin: var(--c-space-6) 0 0;
    font-size: var(--c-fs-h4);
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
}

.c-prose :where(ul, ol) { padding-left: var(--c-space-5); }
.c-prose :where(ul, ol) > li + li { margin-top: var(--c-space-2); }
.c-prose :where(a) { color: var(--c-brand-700); text-underline-offset: 2px; }
.c-prose :where(strong) { color: var(--c-ink-900); }

/* The closing call to action a long page ends with (careers, help centre, success stories, for-*). */
.c-cta-band {
    display: grid;
    gap: var(--c-space-4);
    justify-items: center;
    padding: var(--c-space-8) var(--c-space-6);
    text-align: center;
    background: var(--c-surface-sunken);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-xl);
}

.c-cta-band__title {
    margin: 0;
    font-size: var(--c-fs-h3);
    font-weight: var(--c-fw-black);
    letter-spacing: var(--c-tracking-heading);
    color: var(--c-ink-900);
}

.c-cta-band__text {
    margin: 0;
    max-width: 54ch;
    font-size: var(--c-fs-sm);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

.c-cta-band__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--c-space-3);
    justify-content: center;
}

/* ── the pricing plan card (pricing page + the homepage credit packs) ───────────────────────────── */

.c-plan {
    position: relative;
    display: grid;
    align-content: start;
    gap: var(--c-space-3);
    height: 100%;
    padding: var(--c-space-6);
    text-align: center;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-2xl);
    overflow: hidden;
}

.c-plan--featured {
    border-color: var(--c-brand-600);
    box-shadow: var(--c-shadow-lg);
}

.c-plan__ribbon {
    position: absolute;
    top: 18px;
    right: -34px;
    padding: var(--c-space-1) var(--c-space-40);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-bold);
    color: var(--c-white);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-brand-500));
    transform: rotate(45deg);
}

.c-plan__credits {
    justify-self: center;
    padding: var(--c-space-1) var(--c-space-3);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-bold);
    color: var(--c-brand-700);
    background: var(--c-brand-100);
    border-radius: var(--c-radius-pill);
}

.c-plan__name {
    margin: 0;
    font-size: var(--c-fs-h4);
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
}

.c-plan__price {
    margin: 0;
    font-size: var(--c-fs-display);
    font-weight: var(--c-fw-black);
    letter-spacing: var(--c-tracking-display);
    line-height: 1;
    color: var(--c-ink-900);
}

.c-plan__price-note {
    margin: 0;
    font-size: var(--c-fs-sm);
    color: var(--c-text-muted);
}

.c-plan__list {
    display: grid;
    gap: var(--c-space-2);
    margin: var(--c-space-2) 0 var(--c-space-3);
    padding: 0;
    text-align: left;
    list-style: none;
}

.c-plan__list li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--c-space-2);
    font-size: var(--c-fs-sm);
    color: var(--c-text);
}

.c-plan__list .bi { margin-top: 0.15em; color: var(--c-accent-700); }
.c-plan__form { display: grid; gap: var(--c-space-2); }

/* ── the contributor studio's dark opening (UI.26: the upload tile used to overlap the headline) ── */

.c-split-hero {
    display: grid;
    gap: var(--c-space-7);
    align-items: center;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
}

@media (max-width: 991.98px) {
    .c-split-hero { grid-template-columns: minmax(0, 1fr); }
}

.c-split-hero__panel {
    display: grid;
    gap: var(--c-space-3);
    padding: var(--c-space-6);
    background: rgb(255 255 255 / 8%);
    border: 1px solid rgb(255 255 255 / 16%);
    border-radius: var(--c-radius-xl);
    backdrop-filter: blur(6px);
}

.c-split-hero__panel-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--c-space-3);
    align-items: center;
    font-size: var(--c-fs-sm);
    color: rgb(255 255 255 / 82%);
}

.c-split-hero__panel-row .bi { font-size: 18px; color: var(--c-accent-300); }

.c-stat-row {
    display: grid;
    gap: var(--c-space-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
}

.c-stat {
    display: grid;
    gap: var(--c-space-1);
    justify-items: center;
    text-align: center;
}

.c-stat__value {
    font-size: var(--c-fs-h2);
    font-weight: var(--c-fw-black);
    line-height: 1;
    color: var(--c-ink-900);
}

.c-stat__label {
    font-size: var(--c-fs-sm);
    color: var(--c-text-muted);
}

/* The page head's optional mark (careers, help centre, success stories, the two landing pages used a
   hand-built colour chip here — blue, green, amber, each a raw hex outside the system). */
.c-page-head__mark {
    display: grid;
    place-items: center;
    width: 64px;
    height: 64px;
    margin-bottom: var(--c-space-4);
    font-size: 26px;
    color: var(--c-white);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    border-radius: var(--c-radius-lg);
}

.c-page-head--center .c-page-head__mark { margin-inline: auto; }

/* A brand-filled closing call to action (careers, the contributor studio): the same band as .c-cta-band,
   on the brand gradient instead of the sunken surface. Contrast on this fill is ≥ 4.6:1 for body copy. */
.c-cta-band--brand {
    background: linear-gradient(135deg, var(--c-brand-700) 0%, var(--c-brand-600) 58%, var(--c-accent-700) 100%);
    border-color: transparent;
}

.c-cta-band--brand .c-cta-band__title,
.c-cta-band--brand .c-cta-band__text { color: var(--c-white); }

.c-cta-band--brand .c-cta-band__text { color: rgb(255 255 255 / 84%); }

/* A quiet row of benefits / supports: tick or glyph, then the label (used by careers and the help pages). */
.c-support-grid {
    display: grid;
    gap: var(--c-space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
    margin: 0;
    padding: 0;
    list-style: none;
}

.c-support-grid li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: var(--c-space-3);
    padding: var(--c-space-4);
    font-weight: var(--c-fw-semibold);
    color: var(--c-ink-900);
    background: var(--c-surface-sunken);
    border-radius: var(--c-radius-md);
}

.c-support-grid .bi { font-size: 1.2rem; color: var(--c-brand-600); }

/* A tile that is itself a link: the whole card is the hit area, and the affordance is the same
   `.c-section__action` arrow the redesigned sections use (UI.9's one hover contract). */
.c-tile--link {
    color: inherit;
    text-decoration: none;
    transition: border-color var(--c-hover-dur) var(--c-ease-standard),
                box-shadow var(--c-hover-dur) var(--c-ease-standard),
                transform var(--c-hover-dur) var(--c-ease-standard);
}

.c-tile--link:focus-visible {
    outline: var(--c-ring) solid var(--c-brand-600);
    outline-offset: 3px;
}

@media (hover: hover) {
    .c-tile--link:hover {
        border-color: color-mix(in srgb, var(--c-brand-500) 34%, var(--c-border));
        box-shadow: var(--c-hover-shadow);
    }
}

/* A customer quote (success stories, the homepage's testimonial band). */
.c-quote__who {
    display: flex;
    align-items: center;
    gap: var(--c-space-3);
    margin-bottom: var(--c-space-4);
}

.c-avatar {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    flex: 0 0 auto;
    font-weight: var(--c-fw-black);
    color: var(--c-white);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    border-radius: var(--c-radius-pill);
}

.c-quote__name {
    margin: 0;
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
}

.c-quote__role {
    margin: 0;
    font-size: var(--c-fs-xs);
    color: var(--c-text-muted);
}

.c-quote__text {
    margin: 0;
    font-size: var(--c-fs-body);
    font-style: italic;
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

/* The “commitment / platform / privacy” blocks the About page and the legal intros use: an icon tile,
   a heading and a paragraph — one component, so the three read as siblings rather than three designs. */
.c-feature {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--c-space-4);
    align-items: start;
}

.c-feature + .c-feature { margin-top: var(--c-space-7); }

.c-feature--panel {
    padding: var(--c-space-6);
    background: var(--c-surface-sunken);
    border-radius: var(--c-radius-xl);
}

.c-feature__icon {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    font-size: 20px;
    color: var(--c-brand-700);
    background: color-mix(in srgb, var(--c-brand-500) 12%, transparent);
    border-radius: var(--c-radius-md);
}

.c-feature__icon--accent {
    color: var(--c-accent-800);
    background: color-mix(in srgb, var(--c-accent-500) 16%, transparent);
}

.c-feature__title {
    margin: 0 0 var(--c-space-2);
    font-size: var(--c-fs-h3);
    font-weight: var(--c-fw-black);
    letter-spacing: var(--c-tracking-heading);
    line-height: var(--c-lh-heading);
    color: var(--c-ink-900);
}

.c-feature__text {
    margin: 0;
    font-size: var(--c-fs-body);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

/* A tile that stacks its glyph above its text (the two landing pages' feature cards). */
.c-tile--stack {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--c-space-4);
    text-align: left;
}

/* A tile whose glyph says “allowed” or “not allowed” (the licensing guide's rule list). */
.c-tile__icon--no {
    color: var(--c-danger);
    background: color-mix(in srgb, var(--c-danger) 12%, transparent);
}

/* The “what you cannot do” glyph — the same tile chip, on the danger token. */
.c-feature__icon--danger {
    color: var(--c-danger);
    background: color-mix(in srgb, var(--c-danger) 12%, transparent);
}

/* The contact page's detail rows: a soft chip, a label, a value. Each row is one line of information,
   and the value is the only thing that ever changes weight. */
.c-contact-list {
    display: grid;
    gap: var(--c-space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.c-contact-list > li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--c-space-3);
    align-items: center;
    padding: var(--c-space-3);
    background: var(--c-surface-sunken);
    border-radius: var(--c-radius-md);
}

.c-contact-list__icon {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    color: var(--c-brand-700);
    background: color-mix(in srgb, var(--c-brand-500) 12%, transparent);
    border-radius: var(--c-radius-sm);
}

.c-contact-list__icon--accent {
    color: var(--c-accent-800);
    background: color-mix(in srgb, var(--c-accent-500) 16%, transparent);
}

.c-contact-list__label {
    display: block;
    font-size: var(--c-fs-xs);
    color: var(--c-text-muted);
}

.c-contact-list__value {
    font-weight: var(--c-fw-semibold);
    color: var(--c-ink-900);
    text-decoration: none;
}

a.c-contact-list__value:hover { text-decoration: underline; }

/* Forms on the public pages: the field geometry lives here, not in per-field inline styles. */
.c-form .form-control,
.c-form .form-select {
    padding: var(--c-space-3) var(--c-space-3);
    border-color: var(--c-border);
    border-radius: var(--c-radius-md);
}

/* A whole-card link for content that is not an image (collections, journal posts, category tiles):
   the card is the hit area, the badge/title/copy/meta have one rhythm, and the arrow is the site's
   one “go there” affordance. */
.c-content-card {
    display: grid;
    align-content: start;
    gap: var(--c-space-2);
    height: 100%;
    padding: var(--c-space-5);
    color: inherit;
    text-decoration: none;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-xl);
    transition: border-color var(--c-hover-dur) var(--c-ease-standard),
                box-shadow var(--c-hover-dur) var(--c-ease-standard),
                transform var(--c-hover-dur) var(--c-ease-standard);
}

.c-content-card--flush { padding: 0; overflow: hidden; }

@media (hover: hover) {
    .c-content-card:hover {
        border-color: color-mix(in srgb, var(--c-brand-500) 34%, var(--c-border));
        box-shadow: var(--c-hover-shadow);
    }
}

.c-content-card:focus-visible {
    outline: var(--c-ring) solid var(--c-brand-600);
    outline-offset: 3px;
}

.c-content-card__body { display: grid; gap: var(--c-space-2); padding: var(--c-space-5); }

.c-content-card__badge {
    justify-self: start;
    padding: var(--c-space-1) var(--c-space-3);
    font-size: var(--c-fs-label);
    font-weight: var(--c-fw-bold);
    color: var(--c-brand-700);
    background: var(--c-brand-100);
    border-radius: var(--c-radius-pill);
}

.c-content-card__title {
    margin: 0;
    font-size: var(--c-fs-h4);
    font-weight: var(--c-fw-bold);
    letter-spacing: var(--c-tracking-heading);
    line-height: var(--c-lh-heading);
    color: var(--c-ink-900);
}

.c-content-card__text {
    margin: 0;
    font-size: var(--c-fs-sm);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
    text-wrap: pretty;
}

.c-content-card__meta {
    font-size: var(--c-fs-xs);
    color: var(--c-text-muted);
}

.c-content-card__cta {
    margin-top: var(--c-space-1);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-bold);
    color: var(--c-brand-700);
}

/* The journal post's banner: the pre-UI build derived an hsl() pair from the post id in an inline
   style — two arbitrary hues per post. The system cycles three token pairs instead, so the list still
   looks varied but every colour comes from the palette. */
.c-post-banner {
    display: grid;
    place-items: center;
    height: 160px;
    background: linear-gradient(135deg, var(--c-brand-500), var(--c-brand-800));
}

.c-post-banner--b { background: linear-gradient(135deg, var(--c-accent-500), var(--c-brand-700)); }
.c-post-banner--c { background: linear-gradient(135deg, var(--c-brand-700), var(--c-accent-700)); }
.c-post-banner .bi { font-size: 3rem; color: rgb(255 255 255 / 34%); }

/* A themed category tile: the same card, with a tinted top band the tile's own icon sits in. */
.c-cat-band {
    display: grid;
    place-items: center;
    height: 140px;
    background: linear-gradient(135deg, var(--c-brand-500), var(--c-brand-800));
}

.c-cat-band--b { background: linear-gradient(135deg, var(--c-accent-500), var(--c-brand-700)); }
.c-cat-band--c { background: linear-gradient(135deg, var(--c-brand-700), var(--c-accent-500)); }
.c-cat-band .bi { font-size: 2.5rem; color: rgb(255 255 255 / 40%); }

/* A prose column that stays on a measured measure inside a card (the journal article body). */
.c-prose--card {
    max-width: 72ch;
    margin-inline: auto;
    padding: var(--c-space-7);
    font-size: var(--c-fs-lg);
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-xl);
}

.c-page-head__inner--prose { max-width: 72ch; }

.c-page-head__back {
    display: inline-block;
    margin-bottom: var(--c-space-4);
    font-size: var(--c-fs-sm);
    color: var(--c-text-muted);
    text-decoration: none;
}

.c-page-head--dark .c-page-head__back { color: rgb(255 255 255 / 66%); }

.c-page-head__back:hover { text-decoration: underline; }

.c-page-head__back:focus-visible {
    outline: var(--c-ring) solid var(--c-brand-600);
    outline-offset: 3px;
}

/* The small line under a page head's lede (a version stamp, a collection's author). */
.c-page-head__meta {
    margin: var(--c-space-3) 0 0;
    font-size: var(--c-fs-sm);
    color: var(--c-text-muted);
}

.c-page-head__meta a { color: var(--c-brand-700); text-underline-offset: 2px; }
.c-page-head--dark .c-page-head__meta { color: rgb(255 255 255 / 62%); }
.c-page-head--dark .c-page-head__meta a { color: var(--c-accent-300); }

/* The search results header lines, and a body that starts closer to its head (the results follow the
   search panel immediately — the standard section gap would read as a break). */
.c-page-body--tight { padding-block: var(--c-space-6) var(--c-space-9); }
.c-results-count { font-size: var(--c-fs-sm); color: var(--c-text-muted); }
.c-results-count strong { color: var(--c-ink-900); }
.c-results-range { color: var(--c-text-muted); }
.c-page-head__slot { margin-top: var(--c-space-6); }

/* ── §19 (cont.) — the public footer, stated once ─────────────────────────────────────────────────
   The layout carried these as inline styles; ui-refresh.css already owns the paint (dark surface
   #201a2d, muted links #c9bfd6, the newsletter field). What the class layer adds is the geometry and
   the pieces ui-refresh never covered (the brand mark, the social discs, the submit button, the rule,
   the legal footer) — all from tokens, so the footer can no longer be half-styled by two files. */

.c-footer {
    /* the surface paint stays with ui-refresh (it wins with !important); this restores the two things
       the removed inline style owned that nothing else did: the inherited ink and the bottom rhythm */
    padding-bottom: var(--c-space-6);
    color: rgb(255 255 255 / 70%);
}

.c-footer__brand { font-size: var(--c-fs-h3); }

.c-footer__mark {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    font-size: var(--c-fs-sm);
    color: var(--c-white);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    border-radius: var(--c-radius-sm);
}

.c-footer__tagline { max-width: 300px; }

.c-footer__social {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    color: rgb(255 255 255 / 70%);
    background: rgb(255 255 255 / 8%);
    border-radius: var(--c-radius-pill);
    transition: background var(--c-hover-dur) var(--c-ease-standard),
                color var(--c-hover-dur) var(--c-ease-standard);
}

@media (hover: hover) {
    .c-footer__social:hover { background: rgb(255 255 255 / 16%); }
}

.c-footer__social:focus-visible {
    outline: var(--c-ring) solid var(--c-accent-300);
    outline-offset: 2px;
}

.c-footer__icon { color: var(--c-accent-300); }
.c-footer__icon--whatsapp { color: #25d366; }   /* the provider's own mark colour, deliberately not a token */

.c-footer__link { color: rgb(255 255 255 / 70%); text-decoration: none; }
.c-footer__link:hover { color: var(--c-white); text-decoration: underline; }

.ui-refresh .c-footer__submit,
.c-footer__submit {
    white-space: nowrap;
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    border: 0;
}

.c-footer__rule {
    margin: var(--c-space-6) 0 var(--c-space-5);
    border-color: rgb(255 255 255 / 8%);
    opacity: 1;
}

.c-footer__legal,
.c-footer__legal-link { color: rgb(255 255 255 / 40%); text-decoration: none; }
.c-footer__legal-link:hover { color: var(--c-white); text-decoration: underline; }
.c-footer__dot { margin-inline: var(--c-space-2); color: rgb(255 255 255 / 20%); }

/* A modal surface on the system radius (the terms dialog). */
.c-modal { border-radius: var(--c-radius-xl); }

/* ── §19 (cont.) — the sponsored slot and small shared utilities ────────────────────────────────────
   The ad slot is deliberately quiet: it must never look like part of the catalogue, so its label is
   the smallest type on the page and its card is the sunken surface with a brand hairline. */

.ad-slot { margin: var(--c-space-5) 0; }

.c-ad__label {
    font-size: 0.7rem;
    font-weight: var(--c-fw-bold);
    letter-spacing: 0.06em;
    color: var(--c-text-muted);
}

.c-ad__placement { font-size: 0.7rem; color: var(--c-border-strong); }

.c-ad__card {
    background: var(--c-surface-sunken);
    border: 1px solid color-mix(in srgb, var(--c-brand-500) 12%, transparent);
    border-radius: var(--c-radius-lg);
}

.c-ad__creative {
    max-height: 96px;
    max-width: 220px;
    object-fit: contain;
    background: var(--c-white);
    border-radius: var(--c-radius-sm);
}

.c-ad__body { min-width: 200px; }
.c-ad__headline { font-weight: var(--c-fw-bold); color: var(--c-ink-900); }
.c-ad__text { color: var(--c-text-muted); }

/* Account numbers and references read as data: one monospace utility, used wherever a value must be
   copied exactly. */
.c-mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    word-break: break-all;
}

/* The rendered legal document (version stamp, sections, closing note). The wording comes from the
   policy service; only the presentation lives here. */
.c-legal__title {
    margin: 0 0 var(--c-space-1);
    font-size: var(--c-fs-h2);
    font-weight: var(--c-fw-black);
    letter-spacing: var(--c-tracking-heading);
    line-height: var(--c-lh-heading);
    color: var(--c-ink-900);
}

.c-legal__stamp { font-size: var(--c-fs-sm); color: var(--c-text-muted); }
.c-legal__summary { color: var(--c-text); }

.c-legal__heading {
    margin: var(--c-space-6) 0 var(--c-space-2);
    font-size: var(--c-fs-h3);
    font-weight: var(--c-fw-bold);
    letter-spacing: var(--c-tracking-heading);
    line-height: var(--c-lh-heading);
    color: var(--c-ink-900);
}

.c-legal__p,
.c-legal__list { color: var(--c-text); line-height: var(--c-lh-body); }
.c-legal__list { padding-left: var(--c-space-5); }
.c-legal__list > li + li { margin-top: var(--c-space-2); }

.c-legal__foot {
    font-size: var(--c-fs-xs);
    color: var(--c-ink-400);
    border-top: 1px solid var(--c-border);
}

.c-legal__link { color: var(--c-brand-700); }

/* ── §19 (cont.) — the one delivery control ─────────────────────────────────────────────────────────
   Geometry, weight and states for every “download the original” affordance on the site. The states are
   the component's own (`OriginalDeliveryService`), so the disabled look is a state, not a variant. */

.c-download {
    color: var(--c-white);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-brand-500));
    border: 0;
}

.c-download:hover:not(:disabled) { color: var(--c-white); filter: brightness(1.04); }

.c-download--sm { padding: 0.5rem; border-radius: var(--c-radius-sm); }
.c-download--block { padding: 0.6rem; border-radius: var(--c-radius-md); }
.c-download__sub { font-size: var(--c-fs-label); font-weight: var(--c-fw-medium); opacity: 0.85; }
.c-download__note { font-size: var(--c-fs-label); color: var(--c-ink-400); }

.c-download--done,
.c-download--done:disabled {
    color: var(--c-ink-400);
    background: var(--c-ink-100);
    opacity: 1;
}

.c-download__icon {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    padding: 0;
    color: var(--c-brand-600);
    background: var(--c-brand-100);
    border: 0;
    border-radius: var(--c-radius-sm);
    cursor: pointer;
}

.c-download__icon--done {
    color: var(--c-ink-400);
    background: var(--c-ink-100);
    cursor: default;
}

/* A CMS intro inside a page head (features/about): same measure as the lede, same muted ink, with the
   paragraph rhythm the rich text needs when an editor writes more than one paragraph. */
.c-page-head__rich {
    max-width: 62ch;
    margin-top: var(--c-space-4);
    font-size: var(--c-fs-lg);
    line-height: var(--c-lh-body);
    color: var(--c-text-muted);
}

.c-page-head__rich > :last-child { margin-bottom: 0; }
.c-page-head--center .c-page-head__rich { margin-inline: auto; }
.c-page-head--dark   .c-page-head__rich { color: rgb(255 255 255 / 74%); }

.c-page-head__rich a { color: var(--c-brand-700); }
.c-page-head--dark .c-page-head__rich a { color: var(--c-accent-300); }

/* ── §19 (cont.) — the contributor profile head ─────────────────────────────────────────────────────
   A page head whose content is an identity block. It keeps the head's own band, spacing and dark
   treatment; only the avatar, the ID line and the outbound links are new. */

.c-profile__inner {
    display: flex;
    flex-direction: column;
    gap: var(--c-space-5);
}

@media (min-width: 768px) {
    .c-profile__inner { flex-direction: row; align-items: center; }
}

.c-profile__avatar {
    flex: none;
    width: 88px;
    height: 88px;
    object-fit: cover;
    border: 4px solid rgb(255 255 255 / 20%);
    border-radius: var(--c-radius-pill);
}

.c-profile__avatar--initial {
    display: grid;
    place-items: center;
    font-size: var(--c-fs-display);
    font-weight: var(--c-fw-black);
    color: var(--c-white);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
}

.c-profile__body { min-width: 0; }

.c-profile__verified { margin-left: var(--c-space-2); color: var(--c-accent-300); }

.c-profile__id {
    margin: var(--c-space-1) 0 0;
    font-size: var(--c-fs-sm);
    color: rgb(255 255 255 / 75%);
}

.c-profile__id-value {
    font-weight: var(--c-fw-bold);
    letter-spacing: var(--c-tracking-heading);
    color: var(--c-accent-300);
}

.c-profile__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--c-space-2) var(--c-space-5);
    margin-top: var(--c-space-4);
}

.c-profile__link {
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-semibold);
    color: var(--c-accent-300);
    text-decoration: none;
}

.c-profile__link:hover { color: var(--c-white); text-decoration: underline; }

.c-profile__link:focus-visible {
    outline: var(--c-ring) solid var(--c-accent-300);
    outline-offset: 3px;
}

/* A breadcrumb inside a page head: the trail sits in the head's band, so it drops the bar's own
   surface and keeps only the type and separators (which flip to the dark palette with the head). */
.c-breadcrumb--head {
    padding: 0;
    background: none;
    border: 0;
}

.c-page-head--dark .c-breadcrumb__list { color: rgb(255 255 255 / 60%); }
.c-page-head--dark .c-breadcrumb__item + .c-breadcrumb__item::before { color: rgb(255 255 255 / 35%); }
.c-page-head--dark .c-breadcrumb__link { color: rgb(255 255 255 / 60%); }
.c-page-head--dark .c-breadcrumb__link:hover { color: var(--c-white); }
.c-breadcrumb--head + .c-page-head__eyebrow,
.c-breadcrumb--head + .c-page-head__title { margin-top: var(--c-space-4); }

/* ── §19 (cont.) — the compare table ────────────────────────────────────────────────────────────────
   A wide data table on a narrow screen: the panel scrolls horizontally, the property column stays
   readable, and every cell keeps the same rhythm so the columns line up with the images above them. */

.c-compare {
    overflow: hidden;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-xl);
}

.c-compare__table { --bs-table-bg: transparent; }

.c-compare__row--head { background: var(--c-surface-sunken); }
.c-compare__row { border-bottom: 1px solid var(--c-border); }
.c-compare__row--last { border-bottom: 0; }

.c-compare__label {
    min-width: 140px;
    padding: var(--c-space-4);
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
    vertical-align: middle;
}

.c-compare__label--head { color: var(--c-text-muted); }

.c-compare__cell {
    padding: var(--c-space-3) var(--c-space-4);
    color: var(--c-text-muted);
    text-align: center;
    vertical-align: middle;
}

.c-compare__cell--head { padding: var(--c-space-4); }

.c-compare__cell--price {
    font-size: var(--c-fs-lg);
    font-weight: var(--c-fw-black);
    color: var(--c-brand-600);
}

.c-compare__media { position: relative; }

.c-compare__thumb {
    width: 100%;
    max-height: 180px;
    margin-bottom: var(--c-space-2);
    object-fit: cover;
    background: var(--c-surface-sunken);
    border-radius: var(--c-radius-md);
}

.c-compare__remove-form { position: absolute; top: var(--c-space-2); right: var(--c-space-2); }

.c-compare__remove {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    color: var(--c-white);
    background: color-mix(in srgb, var(--c-danger) 90%, transparent);
    border: 0;
    border-radius: var(--c-radius-pill);
    cursor: pointer;
}

.c-compare__remove:hover { background: color-mix(in srgb, var(--c-danger) 78%, var(--c-ink-900)); }

.c-compare__remove:focus-visible {
    outline: var(--c-ring) solid var(--c-danger);
    outline-offset: 2px;
}

.c-compare__name {
    display: block;
    font-size: var(--c-fs-sm);
    font-weight: var(--c-fw-black);
    color: var(--c-ink-900);
}

/* ── §19 (cont.) — the error pages ──────────────────────────────────────────────────────────────────
   Every 4xx/5xx page is the same narrow, centred panel with a status chip, one sentence and a way out.
   These were the last pages still setting their own measure and heading type by hand. */

.c-error__panel { max-width: 680px; }
.c-error__panel--wide { max-width: 720px; }

.c-error__title {
    font-size: var(--c-fs-h2);
    font-weight: var(--c-fw-black);
    letter-spacing: var(--c-tracking-heading);
    line-height: var(--c-lh-heading);
}

.c-error__text { color: var(--c-ink-700); }
.c-error__note { color: var(--c-text-muted); }

.c-error__callout {
    background: color-mix(in srgb, var(--c-danger) 6%, var(--c-surface));
    border-color: color-mix(in srgb, var(--c-danger) 22%, var(--c-border)) !important;
}

/* A message body written on more than one line keeps its line breaks; referenced identifiers are set in
   the monospace utility. */
.c-pre-wrap { white-space: pre-wrap; }

/* ── §19 (cont.) — the activity stream (Module 12.2) ─────────────────────────────────────────────────
   The banner numbers, the status badges and the table rhythm for the account screens. The badge tones
   are the product's three states (done / refused / informational), so they are named here rather than
   written as hex pairs inside the partial. */

.c-activity__alert { border-left: 4px solid var(--c-danger); }

.c-activity__alert-title { color: var(--c-danger); }

/* the two policy panels keep the small bold heading they always had — only the ink comes from a token */
.c-activity__panel-title { color: var(--c-ink-900); }

.c-activity__text { color: var(--c-ink-700); }
.c-activity__when { white-space: nowrap; }

.c-mono--xs { font-size: 0.72rem; }

.c-metric__label {
    display: block;
    font-size: 0.7rem;
    color: var(--c-ink-300);
}

.c-metric__value {
    font-size: 0.95rem;
    font-weight: var(--c-fw-bold);
    color: var(--c-ink-900);
}

.c-metric__value--danger { color: var(--c-danger); }

.c-metric__note {
    display: block;
    font-size: 0.72rem;
    color: var(--c-text-muted);
}

.badge.c-badge--success { color: var(--c-accent-800); background: var(--c-accent-100); font-weight: var(--c-fw-semibold); }
.badge.c-badge--danger  { color: var(--c-danger);       background: color-mix(in srgb, var(--c-danger) 12%, transparent); font-weight: var(--c-fw-semibold); }
.badge.c-badge--info    { color: var(--c-brand-700);    background: var(--c-brand-100); font-weight: var(--c-fw-semibold); }

/* ── §19 (cont.) — the sign-in shell ────────────────────────────────────────────────────────────────
   One split screen shared by login, registration, password reset and contributor sign-up. The brand
   panel keeps its deep wash and the two slow-floating discs (motion is the `float` keyframe the page
   already used, and it is switched off for reduced motion below); the form column is the canvas. */

.c-auth {
    display: flex;
    min-height: 100vh;
    font-family: var(--c-font-sans);
    background: var(--c-surface-sunken);
}

.c-auth__split { display: flex; width: 100%; min-height: 100vh; }

.c-auth__brand {
    /* ui-refresh owns this panel's width (43%) and its flat plum wash; the class only states what the
       removed inline style owned and nothing else did. */
    position: relative;
    overflow: hidden;
}




.c-auth__brand-inner { position: relative; z-index: var(--c-z-base); }

.c-auth__wordmark {
    margin: 0.67em 0;
    font-size: var(--c-fs-h1);
    font-weight: var(--c-fw-black);
    letter-spacing: var(--c-tracking-heading);
    line-height: var(--c-lh-heading);
    color: var(--c-white);
}

.c-auth__tagline {
    max-width: 350px;
    margin: var(--c-space-4) auto 0;
    font-size: var(--c-fs-body);
    color: rgb(255 255 255 / 60%);
}

.c-auth__form-panel { background: var(--c-surface-sunken); }

.c-auth__form-container {
    width: 100%;
    max-width: 640px;
    padding: var(--c-space-6);
}

.c-auth__mobile-lockup { font-size: var(--c-fs-h3); color: var(--c-ink-900); }

.c-auth__mobile-mark {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    font-size: var(--c-fs-body);
    color: var(--c-white);
    background: linear-gradient(135deg, var(--c-brand-600), var(--c-accent-500));
    border-radius: var(--c-radius-sm);
}

.c-auth__card {
    padding: var(--c-space-40);
    background: var(--c-surface);
    border: 1px solid color-mix(in srgb, var(--c-ink-900) 4%, transparent);
    border-radius: var(--c-radius-2xl);
    box-shadow: var(--c-shadow-md);
}

.c-auth__back {
    font-size: var(--c-fs-sm);
    color: var(--c-text-muted);
    text-decoration: none;
}

.c-auth__back:hover { color: var(--c-brand-700); text-decoration: underline; }

@media (prefers-reduced-motion: reduce) {
    .c-auth__orb { animation: none; }
}

/* ── §19 (cont.) — actions on a brand surface, and the quiet action ──────────────────────────────────
   A button that sits on the brand band cannot be Bootstrap's `btn-light`/`btn-outline-light`: those two
   carry the stock palette (the stock sweep caught them the moment these pages were migrated). These
   classes are the same buttons the inline styles drew — a white pill with brand ink, and a ghost. */

.c-btn-on-brand {
    padding: var(--c-space-3) var(--c-space-6);
    font-weight: var(--c-fw-bold);
    color: var(--c-brand-700);
    background: var(--c-white);
    border: 0;
    border-radius: var(--c-radius-pill);
}

.c-btn-on-brand:hover,
.c-btn-on-brand:focus-visible { color: var(--c-brand-800); background: var(--c-white); }

.c-btn-on-brand--ghost {
    color: var(--c-white);
    background: transparent;
    border: 2px solid rgb(255 255 255 / 30%);
}

.c-btn-on-brand--ghost:hover,
.c-btn-on-brand--ghost:focus-visible {
    color: var(--c-white);
    background: rgb(255 255 255 / 12%);
    border-color: rgb(255 255 255 / 48%);
}

/* The second-choice action: a real button, deliberately quieter than the primary (error pages, the
   empty state, “read the FAQ”). */
.c-btn-quiet {
    color: var(--c-ink-700);
    background: var(--c-surface);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--c-radius-md);
}

.c-btn-quiet:hover,
.c-btn-quiet:focus-visible {
    color: var(--c-brand-700);
    background: var(--c-brand-50);
    border-color: var(--c-brand-300);
}

/* Skip links stay hidden until focused; when they appear they belong to the system too. */
.c-skip-link {
    z-index: var(--c-z-toast);
    font-weight: var(--c-fw-semibold);
    color: var(--c-brand-700);
    background: var(--c-white);
    border: 1px solid var(--c-brand-300);
    border-radius: var(--c-radius-md);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   §20 · CADPNG BRAND COLOR SYSTEM (logo-derived)
   Navy foundation · blue/cyan interactive accent · red/orange/yellow for attention only.
   The heavy lifting lives in §1 (token re-palette); these rules are the component recipes the
   brief names explicitly, written here so the older sheets never have to be edited again.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */

/* §4 — Primary buttons are CadPNG navy; hover swings to the brand blue. Beats the
   `var(--ui-violet) !important` recipe in ui-refresh.css (:91) because this sheet loads last. */
.ui-refresh .btn-primary,
.ui-refresh .btn-primary:focus {
    background-color: var(--c-ink-900) !important;
    border-color: var(--c-ink-900) !important;
    color: var(--c-white);
    box-shadow: 0 4px 12px rgb(11 31 51 / 18%) !important;
}

.ui-refresh .btn-primary:hover,
.ui-refresh .btn-primary:focus-visible {
    background-color: var(--c-brand-600) !important;   /* #0067a8 */
    border-color: var(--c-brand-600) !important;
    color: var(--c-white);
    box-shadow: 0 5px 16px rgb(0 103 168 / 24%) !important;
    transform: none;
}

/* The hero search button keeps the brighter blue (§9 allows navy *or* blue) so the one
   brand-blue action stays visible next to the navy primary. */
.ui-refresh :is(.btn-cadpng, .btn-search) {
    background-color: var(--c-brand-600) !important;
    border-color: var(--c-brand-600) !important;
}

.ui-refresh :is(.btn-cadpng, .btn-search):hover {
    background-color: var(--c-brand-700) !important;   /* the logo blade #085098 */
    border-color: var(--c-brand-700) !important;
}

/* §5 — the accent CTA: CadPNG red, reserved for the single conversion action on the
   image page. White text on #f41717 measures 4.55:1 (AA). */
.c-btn--accent {
    background: var(--cad-red) !important;
    border-color: var(--cad-red) !important;
    color: var(--c-white) !important;
}

.c-btn--accent:hover,
.c-btn--accent:focus-visible {
    background: var(--cad-red-dark) !important;
    border-color: var(--cad-red-dark) !important;
    color: var(--c-white) !important;
    transform: none;
}

/* §6 — secondary buttons: white, quiet border, hover springs the brand blue. */
.btn-outline-secondary {
    background: var(--c-white);
    border-color: var(--c-border);
    color: var(--c-heading);
}

.btn-outline-secondary:hover,
.btn-outline-secondary:focus-visible {
    background: var(--c-white);
    border-color: var(--c-brand-600);
    color: var(--c-brand-600);
}

/* §18 — text links inside prose: brand blue, hover swings to CadPNG red on the shared tempo.
   Scoped to copy (`p`, legal docs, prose cards) so card titles, nav and buttons keep their ink. */
main p a:not(.btn):not([class]), 
main li a:not(.btn):not([class]),
.c-prose--card a:not(.btn),
.c-legal__doc a:not(.btn),
.cms-body a:not(.btn) {
    color: var(--c-brand-600);
    transition: color var(--c-dur-fast) var(--c-ease-standard);
}

main p a:not(.btn):not([class]):hover,
main li a:not(.btn):not([class]):hover,
.c-prose--card a:not(.btn):hover,
.c-legal__doc a:not(.btn):hover,
.cms-body a:not(.btn):hover {
    color: var(--cad-red);
}

/* §15 — portal sidebars: navy surface with the brief's active treatment
   (blue gradient wash + the red 3px indicator, painted as an inset shadow so nothing shifts). */
.ui-refresh .portal-sidebar nav a.active {
    background: linear-gradient(90deg, rgb(0 103 168 / 35%), rgb(21 159 227 / 12%));
    border-color: transparent;
    box-shadow: inset 3px 0 0 var(--cad-red);
    color: var(--c-white);
}

/* §20 — the hero shells keep the light, logo-inspired look (white → cool blue → warm hint).
   Token cascade already turned them cool; this only guarantees the warm tail never returns. */
.c-hero, .page-hero {
    background-color: var(--c-white);
}

/* The §19 quote styles just above serve the LIGHT cards (success-stories, CMS prose). The same
   component on the homepage's dark testimonial band must invert — scope kept narrow on purpose. */
.c-section--dark .c-quote__name { color: var(--c-white); }
.c-section--dark .c-quote__role { color: rgb(255 255 255 / 55%); }
.c-section--dark .c-quote__text { color: rgb(255 255 255 / 80%); }

/* ── §19 (cont.) — UI.26 legacy migration: KPI tiles, pricing deck, content grids ────────────────────
   Replaces the last pre-redesign Bootstrap rows (row/row-cols/col) and `metric-card` with design-token
   components. Geometry mirrors what the Bootstrap grids produced (2-up mobile, 4-up ≥lg for KPIs;
   1 → 2 → 3-up for content cards) so the migration is placement-identical, not a redesign. */

/* KPI tiles — the dashboards' metric row. */
.c-kpis {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--c-space-3);
    margin-bottom: var(--c-space-5);
}

@media (min-width: 992px) { .c-kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.c-kpi {
    min-width: 0;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--c-radius-lg);
    padding: var(--c-space-20) var(--c-space-5);
    box-shadow: var(--c-shadow-xs);
}

.c-kpi__icon {
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
    margin-bottom: var(--c-space-3);
    border-radius: var(--c-radius-md);
    font-size: 1.15rem;
}

.c-kpi__icon--brand   { background: var(--c-brand-50);    color: var(--c-brand-600); }
.c-kpi__icon--success { background: color-mix(in srgb, var(--c-success) 12%, transparent); color: var(--c-success); }
.c-kpi__icon--info    { background: var(--c-brand-100);   color: var(--c-brand-700); }
.c-kpi__icon--warning { background: var(--c-warning-soft); color: var(--c-warning); }

.c-kpi__label { display: block; font-size: var(--c-fs-xs); font-weight: var(--c-fw-semibold); color: var(--c-text-muted); }

.c-kpi__value {
    display: block;
    margin-top: var(--c-space-1);
    font-size: 1.65rem;
    line-height: 1.15;
    font-weight: var(--c-fw-black);
    color: var(--c-heading);
    font-variant-numeric: tabular-nums;
}

.c-kpi__note { margin-top: var(--c-space-1); font-size: var(--c-fs-xs); color: var(--c-text-muted); }
.c-kpi__delta { font-size: var(--c-fs-xs); }

/* The bare 2→4-up stat line (no tile — used inside an existing panel, e.g. the commission band). */
.c-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--c-space-3);
}

@media (min-width: 992px) { .c-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* Pricing deck — 1 → 2 (≥576px) → N-up (≥1200px), where N tracks the package count like the old
   row-cols-xl-{{ count }} did, without any inline styles. */
.c-plan-deck {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--c-space-4);
    align-items: stretch;
}

@media (min-width: 576px) { .c-plan-deck { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

@media (min-width: 1200px) {
    .c-plan-deck--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .c-plan-deck--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .c-plan-deck--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* Content card grid (collections et al) — 1 → 2 (≥768px) → 3-up (≥1200px). */
.c-grid-3 {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--c-space-4);
}

@media (min-width: 768px)  { .c-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1200px) { .c-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* A grid child that must span the whole row (the empty state inside a card grid). */
.c-grid-3 > .c-grid-span { grid-column: 1 / -1; }

/* Panel areas that were col-lg-8/col-lg-4 splits — equal halves are calmer at every width. */
.c-panels {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--c-space-5);
}

@media (min-width: 992px) { .c-panels { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Narrow-form / wide-content split (payout form beside its history). */
.c-split {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--c-space-5);
}

@media (min-width: 992px) {
    .c-split--5-7 { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
    .c-split--8-4 { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}

/* The admin revenue chart's fixed stage — geometry lives in the system, the bars' heights stay
   inline because they are data, not design. */
.c-chart-zone { height: 230px; }
.c-chart-tick { font-size: 0.62rem; }

/* The same chip, list-safe (rows in the activity feed / top-assets lists — legacy `metric-icon`). */
.c-icon-chip {
    display: grid;
    place-items: center;
    width: 2.45rem;
    height: 2.45rem;
    flex: 0 0 auto;
    border-radius: 0.85rem;
    font-size: var(--c-fs-h4);
}

.c-icon-chip--brand { background: var(--c-brand-50); color: var(--c-brand-600); }

/* Keyboard parity for the lift treatment: focusing anything inside a lifted tile reads exactly
   like hovering it (the probe checks card, frame, picture and actions). Disarmed for reduced motion. */
.hover-lift-xl:focus-within,
.c-plan:focus-within {
    transform: translateY(calc(-1 * var(--c-hover-lift)));
    box-shadow: var(--c-shadow-lg);
}

@media (prefers-reduced-motion: reduce) {
    .hover-lift-xl:focus-within,
    .c-plan:focus-within { transform: none !important; }
}
