/* div.studio — WordPress component layer for X Pro / Cornerstone.
   Everything here is token-driven; no hex values. React hover/press state
   from the design system's .jsx components is reproduced in CSS.
   Loads after ds/styles.css, so :root tokens are already defined. */

/* ── X Pro bridge ─────────────────────────────────────────────────────────
   Cornerstone's own elements (Headline, Text, Button, Section) inherit from
   the theme's base typography. Pointing that base at the brand tokens means
   stock elements come out on-brand without per-element restyling. */
/* Every bridge rule is wrapped in :where() so it contributes ZERO specificity.
   Without it, `.x-root a` (0,1,1) beats `.ds-btn--secondary` (0,1,0) and every
   button inherits the link colour and the hover underline instead of its own
   variant — which is exactly what happened. :where() lets these set a baseline
   that any component rule overrides naturally, with no !important anywhere. */
:where(.x-root,.x-root p,.cs-content){font:var(--type-body);color:var(--text-body)}
:where(.x-root){background:var(--surface-page);-webkit-font-smoothing:antialiased}
:where(.x-root :is(h1,h2,h3,h4)){color:var(--text-strong);letter-spacing:var(--tracking-heading);text-wrap:pretty}
:where(.x-root h1){font:var(--type-h1)}
:where(.x-root h2){font:var(--type-h2)}
:where(.x-root h3){font:var(--type-h3)}
:where(.x-root h4){font:var(--type-h4)}
:where(.x-root a){color:var(--text-link);text-decoration:none;transition:color var(--dur-fast) var(--ease-out)}
:where(.x-root a:hover){color:var(--text-link-hover);text-decoration:underline}
:where(.x-root .x-container){max-width:var(--container-max)}
/* Cornerstone content areas: cap the measure, the way the system does. */
:where(.x-root :is(.x-text,.x-raw-content) p){max-width:var(--measure)}
/* Belt and braces: Cornerstone ships its own anchor rules we don't control. */
.x-root a.ds-btn,.x-root a.ds-btn:hover,.x-root a.ds-card,.x-root a.ds-case-card,.x-root a.ds-logo:hover{text-decoration:none}

/* ── Icon (Lucide via CSS mask, so glyphs take a token colour) ───────── */
/* Font Awesome output (SVG or webfont) wrapped so it takes a token size and
   inherits currentColor like any other glyph. */
.ds-icon-wrap{display:inline-flex;align-items:center;justify-content:center;flex:none;width:var(--ds-icon-size,20px);height:var(--ds-icon-size,20px);line-height:0}
.ds-icon-wrap svg{width:100%;height:100%;display:block;fill:currentColor}
.ds-icon-wrap i{font-size:var(--ds-icon-size,20px);line-height:1;color:currentColor}
/* Legacy masked-Lucide fallback, used only where Font Awesome is unavailable. */
.ds-icon{display:inline-block;flex:none;width:var(--ds-icon-size,20px);height:var(--ds-icon-size,20px);background:currentColor;-webkit-mask:var(--ds-icon) center/contain no-repeat;mask:var(--ds-icon) center/contain no-repeat}
.ds-icon--arrow,.ds-icon-wrap.ds-icon--arrow{color:var(--text-faint);transition:color var(--dur-fast) var(--ease-out)}

/* ── Button ──────────────────────────────────────────────────────────── */
.ds-btn{display:inline-flex;align-items:center;justify-content:center;font-family:var(--font-display);font-weight:var(--weight-semibold);letter-spacing:-.01em;border-radius:var(--radius-control);border:1px solid transparent;cursor:pointer;white-space:nowrap;text-decoration:none;transition:background-color var(--dur-fast) var(--ease-out),border-color var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out),box-shadow var(--dur-fast) var(--ease-out),transform var(--dur-fast) var(--ease-out)}
.ds-btn:hover{text-decoration:none}
.ds-btn:active{transform:scale(var(--press-scale))}
.ds-btn:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
.ds-btn--full{display:flex;width:100%}
.ds-btn--sm{height:36px;padding:0 18px;font-size:14px;gap:8px}
.ds-btn--md{height:44px;padding:0 24px;font-size:15px;gap:9px}
.ds-btn--lg{height:54px;padding:0 32px;font-size:17px;gap:10px}
.ds-btn--primary{background:var(--green-400);border-color:var(--green-400);color:var(--ink-900)}
.ds-btn--primary:hover{background:var(--green-500);border-color:var(--green-500);color:var(--ink-900);box-shadow:var(--shadow-accent)}
.ds-btn--secondary{background:var(--ink-600);border-color:var(--ink-600);color:var(--paper)}
.ds-btn--secondary:hover{background:var(--ink-800);border-color:var(--ink-800);color:var(--paper);box-shadow:var(--shadow-sm)}
.ds-btn--outline{background:transparent;border-color:var(--ink-200);color:var(--ink-600)}
.ds-btn--outline:hover{background:var(--ink-050);border-color:var(--ink-600);color:var(--ink-600)}
.ds-btn--ghost{background:transparent;border-color:transparent;color:var(--ink-600)}
.ds-btn--ghost:hover{background:var(--ink-050);color:var(--ink-600)}
.ds-btn--on-dark{background:var(--paper);border-color:var(--paper);color:var(--ink-900)}
.ds-btn--on-dark:hover{background:var(--green-300);border-color:var(--green-300);color:var(--ink-900)}
/* Outline on ink. The plain outline variant uses --ink-200 border and
   --ink-600 text, which is invisible on an ink ground; ghost is worse, being
   transparent with ink text. A dark surface needs its own outline. */
.ds-btn--outline-dark{background:transparent;border-color:var(--border-inverse);color:var(--paper)}
.ds-btn--outline-dark:hover{background:rgb(var(--paper-rgb) / .08);border-color:var(--paper);color:var(--paper)}
.ds-btn--danger{background:var(--coral-600);border-color:var(--coral-600);color:var(--white)}
.ds-btn--danger:hover{background:var(--coral-700);border-color:var(--coral-700);color:var(--white);box-shadow:var(--shadow-sm)}
.ds-btn[disabled]{opacity:.42;cursor:not-allowed;box-shadow:none;transform:none}

/* ── Type utilities used by the renderers ────────────────────────────── */
.ds-eyebrow{font:var(--type-eyebrow);letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--text-faint)}
.ds-eyebrow-row{display:flex;align-items:center;gap:var(--space-4)}
.ds-eyebrow-num{font:var(--type-mono);font-size:var(--text-size-micro);color:var(--text-accent)}
.ds-h3{font:var(--type-h3);color:var(--text-strong);letter-spacing:var(--tracking-heading);margin:0}
.ds-body-sm{font:var(--type-body-sm);color:var(--text-muted);margin:0;text-wrap:pretty}
.ds-mono-micro{font:var(--type-mono);font-size:12px;color:var(--text-muted)}
.ds-price{font:var(--type-mono);font-size:15px;font-weight:var(--weight-bold);color:var(--text-strong);white-space:nowrap}
.ds-wordmark{font:var(--type-wordmark);color:inherit}
.ds-lead{font:var(--type-body-lg);color:var(--text-body);max-width:58ch;margin:0;text-wrap:pretty}

/* ── Section heading ─────────────────────────────────────────────────── */
.ds-section-heading{display:flex;flex-direction:column;gap:var(--space-5);margin:0 0 var(--space-9)}
.ds-section-heading--center{align-items:center;text-align:center}
.ds-section-heading--center .ds-lead{margin-inline:auto}
.ds-section-title{font:var(--type-display-3);color:var(--text-strong);letter-spacing:var(--tracking-display);max-width:24ch;margin:0;text-wrap:balance}
.ds-section-heading--center .ds-section-title{max-width:20ch}

/* ── Ink-surface tone switch ─────────────────────────────────────────── */
.ds-on-ink,.ds-on-ink .ds-section-title,.ds-on-ink .ds-h3{color:var(--text-on-inverse)}
.ds-on-ink .ds-lead,.ds-on-ink .ds-body-sm,.ds-on-ink .ds-mono-micro{color:var(--text-on-inverse-muted)}
.ds-on-ink .ds-eyebrow{color:var(--text-on-inverse-muted)}
.ds-on-ink .ds-eyebrow-num{color:var(--green-400)}

/* ── Card ────────────────────────────────────────────────────────────── */
.ds-card{background:var(--surface-card);border:1px solid var(--border-subtle);border-radius:var(--radius-card);padding:var(--pad-card);box-shadow:var(--shadow-sm);transition:box-shadow var(--dur-base) var(--ease-out),transform var(--dur-base) var(--ease-out)}
/* ds-card--interactive is about AFFORDANCE only — cursor, lift, shadow. It
   must not carry layout: the card's flex column has to exist whether or not
   the card happens to be a link, otherwise a linked and an unlinked card of
   the same type space their contents differently. */
.ds-card--interactive{text-decoration:none;cursor:pointer}
.ds-card--interactive:hover{box-shadow:var(--shadow-lg);transform:translateY(var(--lift-y))}

/* ── Service card ────────────────────────────────────────────────────── */
.ds-service-card{display:flex;flex-direction:column;gap:var(--space-5);padding:var(--pad-card-lg);text-decoration:none}
/* Without a link the card is a div and does not lift, so give it the same
   focus treatment the tariff card gets — something happens on hover, but it
   doesn't imply a click target. */
.ds-service-card:not(.ds-card--interactive):hover{border-color:var(--border-default);box-shadow:var(--shadow-md)}
.ds-service-card__top{display:flex;align-items:center;justify-content:space-between}
.ds-service-card p{flex:1}
.ds-icon-chip{display:inline-flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:var(--radius-full);background:var(--green-100);color:var(--green-700);transition:background-color var(--dur-base) var(--ease-out),color var(--dur-base) var(--ease-out)}
.ds-service-card:hover .ds-icon-chip{background:var(--green-400);color:var(--ink-900)}
.ds-service-card__foot{display:flex;align-items:center;justify-content:space-between;gap:var(--space-5);padding-top:var(--space-5);border-top:1px solid var(--border-subtle)}
.ds-service-card__meta{display:flex;flex-direction:column;gap:3px}
.ds-service-card:hover .ds-icon--arrow{color:var(--green-700)}

/* ── Media frame — honest placeholder, never generated imagery ───────── */
/* The ink background is the honest-placeholder ground. Once an image fills the
   frame it must go: at the rounded corners, antialiasing on the image edge
   lets a sliver of the dark ground through as four dark arcs, which is
   obvious on a light screenshot. --filled swaps it for a tint and adds the
   hairline that gives a white-background screenshot an edge — 1px, not a
   shadow, because the system contains things with rules. */
.ds-media{position:relative;overflow:hidden;border-radius:var(--radius-media);background:var(--surface-inverse);display:flex;align-items:center;justify-content:center}
.ds-media--filled{background:var(--surface-page-alt)}
/* The hairline has to be an overlay, not an inset shadow: an inset shadow
   paints below content, so an object-fit:cover image sits on top of it and
   hides it entirely. ::after with border-radius:inherit tracks the frame. */
.ds-media--filled::after{content:"";position:absolute;inset:0;border:1px solid var(--border-subtle);border-radius:inherit;pointer-events:none}
.ds-media img{width:100%;height:100%;object-fit:cover;display:block;transition:transform var(--dur-base) var(--ease-out)}
.ds-media__label{font:var(--type-mono);font-size:12px;letter-spacing:var(--tracking-mono);color:var(--text-on-inverse-muted);padding:var(--space-5);text-align:center}

/* ── Case card ───────────────────────────────────────────────────────── */
.ds-case-card{display:flex;flex-direction:column;gap:var(--space-5);text-decoration:none}
.ds-case-card:hover img{transform:scale(1.03)}
.ds-case-card__body{display:flex;align-items:flex-start;gap:var(--space-5)}
.ds-case-card__body>div{flex:1}
.ds-case-card .ds-eyebrow{display:block;margin-bottom:8px}
.ds-case-card__title{transition:color var(--dur-fast) var(--ease-out)}
.ds-case-card:hover .ds-case-card__title{color:var(--text-accent)}
.ds-case-card:hover .ds-icon--arrow{color:var(--green-700)}
.ds-case-card__body .ds-icon--arrow{margin-top:20px}
.ds-case-card .ds-body-sm{margin-top:8px}
.ds-tag-row{display:flex;flex-wrap:wrap;gap:var(--space-3)}
.ds-tag{font:var(--type-mono);font-size:12px;letter-spacing:var(--tracking-mono);color:var(--text-muted);background:var(--surface-sunken);border-radius:var(--radius-full);padding:4px 12px}

/* ── Stat ────────────────────────────────────────────────────────────── */
.ds-stat{display:flex;flex-direction:column;gap:var(--space-3)}
.ds-stat__value{font:var(--type-display-2);color:var(--text-strong);letter-spacing:var(--tracking-display);display:flex;align-items:baseline;gap:6px}
.ds-stat__unit{font:var(--type-mono);font-size:15px;color:var(--text-accent);letter-spacing:var(--tracking-mono)}
.ds-stat__label{font:var(--type-body-sm);color:var(--text-muted);max-width:24ch}
.ds-on-ink.ds-stat .ds-stat__value{color:var(--text-on-inverse)}
.ds-on-ink.ds-stat .ds-stat__label{color:var(--text-on-inverse-muted)}
.ds-on-ink.ds-stat .ds-stat__unit{color:var(--green-400)}

/* ── Quote ───────────────────────────────────────────────────────────── */
.ds-quote{margin:0;display:flex;flex-direction:column;gap:var(--space-5);padding-left:var(--space-7);border-left:1px solid var(--border-accent)}
.ds-quote__text{font:var(--type-h3);font-weight:var(--weight-regular);color:var(--text-strong);letter-spacing:var(--tracking-heading);margin:0;max-width:44ch;text-wrap:pretty}
.ds-quote__by{display:flex;flex-direction:column;gap:2px}
.ds-quote__author{font:var(--type-body-sm);font-weight:var(--weight-semibold);color:var(--text-strong)}
.ds-quote__role{font:var(--type-mono);font-size:12px;color:var(--text-muted)}
.ds-on-ink.ds-quote .ds-quote__text,.ds-on-ink.ds-quote .ds-quote__author{color:var(--text-on-inverse)}
.ds-on-ink.ds-quote .ds-quote__role{color:var(--text-on-inverse-muted)}

/* ── Tariff row ──────────────────────────────────────────────────────── */
.ds-tariff{display:flex;flex-direction:column;gap:var(--space-7);height:100%}
/* Hover: border firms up, shadow deepens one step. Deliberately NOT a lift —
   ds-card--interactive lifts because a service card is a link and the whole
   card is clickable. A tariff card's affordance is its CTA, so lifting it
   would promise a click that isn't there. This reads as "the one you're
   looking at" without implying the card itself does anything. */
.ds-tariff:hover{border-color:var(--border-default);box-shadow:var(--shadow-md)}
.ds-tariff--accent:hover{border-color:var(--green-600);box-shadow:var(--shadow-accent)}
@media (hover:none){.ds-tariff:hover{border-color:var(--border-subtle);box-shadow:var(--shadow-sm)}}
.ds-tariff--accent{background:var(--surface-accent-soft);border-color:var(--border-accent)}
.ds-tariff__head{display:flex;flex-direction:column;gap:var(--space-3)}
.ds-tariff__price{font:var(--type-mono);font-size:22px;font-weight:var(--weight-bold);color:var(--text-strong);white-space:nowrap}
.ds-tariff__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;flex:1}
.ds-tariff__list li{font:var(--type-body-sm);color:var(--text-body);padding:12px 0;border-top:1px solid var(--border-subtle)}

/* ── Logo lockup ─────────────────────────────────────────────────────── */
.ds-logo{display:inline-flex;align-items:center;gap:var(--space-4);text-decoration:none;color:var(--text-strong)}
.ds-logo:hover{text-decoration:none;color:var(--text-strong)}
.ds-logo__text{display:flex;flex-direction:column;gap:3px}
.ds-logo__motto{font:var(--type-eyebrow);letter-spacing:.06em;color:var(--text-muted)}
.ds-on-ink.ds-logo,.ds-on-ink.ds-logo:hover{color:var(--text-on-inverse)}
.ds-on-ink.ds-logo .ds-logo__motto{color:var(--text-on-inverse-muted)}

/* ── Layout primitives ────────────────────────────────────────────────
   Put these in a Cornerstone element's Custom Class field instead of setting
   flex/grid and gap by hand. They live here rather than in a saved Component
   on purpose: the stylesheet is version-controlled, deploys with the theme,
   and cannot be edited by accident. A Component lives in the database and
   does none of those things.

   Every one carries the design system's own gap, so spacing stays correct
   without anyone typing a number. */
/* Every modifier is self-sufficient — it declares display itself rather than
   depending on the base class being present too. A layout class that silently
   does nothing when used alone is worse than no class. */
.ds-stack,.ds-stack--tight,.ds-stack--loose{display:flex;flex-direction:column;gap:var(--gap-stack)}
.ds-stack--tight{gap:var(--space-3)}
.ds-stack--loose{gap:var(--gap-group)}
/* A vertical flex container stretches children across the cross axis, which
   is right for text blocks and wrong for a button — it becomes page-width.
   Buttons and logos size to their content; everything else still fills. */
.ds-stack>.ds-btn,.ds-stack--tight>.ds-btn,.ds-stack--loose>.ds-btn,.ds-stack>.ds-logo,.ds-stack--loose>.ds-logo{align-self:flex-start}
.ds-stack>.ds-btn--full,.ds-stack--loose>.ds-btn--full{align-self:stretch}
.ds-row,.ds-row--tight,.ds-row--center,.ds-row--between{display:flex;flex-wrap:wrap;gap:var(--gap-group);align-items:flex-start}
/* A section heading carries its own bottom margin so it works standalone.
   Inside a gap-based parent that margin would stack on top of the gap and
   double the space, so drop it — the parent owns spacing there. */
.ds-stack>.ds-section-heading,.ds-stack--tight>.ds-section-heading,.ds-stack--loose>.ds-section-heading,.ds-row>.ds-section-heading,.ds-grid>.ds-section-heading{margin-bottom:0}
.ds-row--tight{gap:var(--gap-inline)}
.ds-row--center{align-items:center}
.ds-row--between{justify-content:space-between}

/* Card grids. Cards stay equal height; the minmax floor is what makes them
   wrap instead of squeezing below a readable width. */
/* Each modifier is self-sufficient — it sets display:grid itself rather than
   depending on a base .ds-grid class alongside it. A layout class that
   silently does nothing when used alone is worse than no class. */
.ds-grid,.ds-grid--2,.ds-grid--3,.ds-grid--4{display:grid;gap:var(--space-7);align-items:stretch}
.ds-grid--2{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-9)}
.ds-grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
.ds-grid--4{grid-template-columns:repeat(4,minmax(0,1fr))}
/* Column ladders. A service card stops being readable below about 300px —
   the title breaks to two lines and the price row crowds the arrow — so three
   and four columns both step down at 1024px rather than holding on to 768. */
@media (max-width:1023px){.ds-grid--3,.ds-grid--4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:767px){.ds-grid--2,.ds-grid--3,.ds-grid--4{grid-template-columns:minmax(0,1fr)}}

/* Stat row: wider gap than a card grid, and stats stay side by side longer
   because the numbers are short. */
.ds-stat-row{display:flex;flex-wrap:wrap;gap:var(--space-9) var(--space-11)}
@media (max-width:479px){.ds-stat-row{flex-direction:column;gap:var(--space-8)}}

/* Measure caps. --measure on anything holding body copy; a page-builder
   column is as wide as the screen and nothing else stops the line length. */
.ds-measure{max-width:var(--measure)}
.ds-measure-narrow{max-width:var(--container-narrow)}
.ds-container{max-width:var(--container-max);margin-inline:auto;padding-inline:var(--gutter)}
.ds-container--narrow{max-width:var(--container-narrow);margin-inline:auto;padding-inline:var(--gutter)}
@media (max-width:767px){.ds-container,.ds-container--narrow{padding-inline:var(--gutter-mobile)}}

/* ── Section rhythm helpers for Cornerstone Sections ──────────────────
   Add these as a Custom Class on a Section instead of typing padding by
   hand — sections breathe at 128px, compact at 80px, and that vertical
   space is load-bearing. */
/* Two selectors each: the bare class for Divs and non-Cornerstone contexts,
   and an .x-root-scoped one to match the weight of Cornerstone's own generated
   rules. On a Cornerstone SECTION this still loses — Cornerstone writes
   per-element padding at higher specificity than any class — so set a
   Section's padding in its own field as var(--section-y). The class remains
   the right tool on a Div. */
.ds-section,.x-root .ds-section{padding-block:var(--section-y)}
.ds-section--compact,.x-root .ds-section--compact{padding-block:var(--section-y-compact)}
.ds-section--tint{background:var(--surface-page-alt)}
.ds-section--ink{background:var(--surface-inverse);color:var(--text-on-inverse)}
.ds-section--ink h1,.ds-section--ink h2,.ds-section--ink h3,.ds-section--ink p{color:inherit}
/* An ink section imposes the on-ink tiers on every ds- component inside it,
   whatever each element's own Surface control says.

   Without this, getting an ink band right means setting tone on the heading,
   two stats and a quote separately — four chances to miss one, and a missed
   one renders --text-strong ink on ink, which is invisible rather than merely
   wrong. The per-element control stays useful for a component on an ink
   background the section doesn't own (a dark card, a hero glow). */
.ds-section--ink .ds-section-title,.ds-section--ink .ds-h3,.ds-section--ink .ds-stat__value,.ds-section--ink .ds-quote__text,.ds-section--ink .ds-quote__author{color:var(--text-on-inverse)}
.ds-section--ink .ds-lead,.ds-section--ink .ds-body-sm,.ds-section--ink .ds-mono-micro,.ds-section--ink .ds-eyebrow,.ds-section--ink .ds-stat__label,.ds-section--ink .ds-quote__role{color:var(--text-on-inverse-muted)}
.ds-section--ink .ds-eyebrow-num,.ds-section--ink .ds-stat__unit{color:var(--green-400)}
.ds-section--ink .ds-quote{border-left-color:var(--green-400)}
/* The wordmark's TEXT follows the section, but its MARK cannot: the paper
   mark is a separate SVG file, not a recolour, and the renderer can't know
   what it's nested inside. So set Tone on the element. This rule only stops a
   missed toggle rendering ink text on ink — the mark will still be wrong,
   which is visible rather than invisible. */
.ds-section--ink .ds-logo,.ds-section--ink .ds-logo:hover,.ds-section--ink .ds-wordmark{color:var(--text-on-inverse)}
.ds-section--ink .ds-logo__motto{color:var(--text-on-inverse-muted)}
/* Cards keep their own paper surface inside an ink band — that contrast is the
   point of the band — so undo the inherited tiers within them. */
.ds-section--ink .ds-card .ds-h3,.ds-section--ink .ds-case-card .ds-h3{color:var(--text-strong)}
.ds-section--ink .ds-card .ds-body-sm,.ds-section--ink .ds-case-card .ds-body-sm{color:var(--text-muted)}
@media (max-width:768px){.ds-section{padding-block:var(--section-y-compact)}}

@media (prefers-reduced-motion:reduce){.ds-btn,.ds-card,.ds-media img,.ds-icon-chip{transition:none}.ds-card--interactive:hover{transform:none}.ds-case-card:hover img{transform:none}}


/* ── Case study page ──────────────────────────────────────────────────────
   Used by single-case.php. Mirrors the section sequence of the design
   system's own case-study template, so ACF data renders as that layout. */
.ds-case{width:100%;max-width:100%;overflow-x:hidden}
.ds-case-inner{max-width:var(--container-max);margin:0 auto}
.ds-case-band{padding:var(--section-y) var(--gutter)}
.ds-case-band--ink-flush{background:var(--ink-600);padding-top:0;padding-bottom:var(--section-y)}
.ds-case-band--cta{padding-bottom:var(--space-14)}
@media(max-width:767px){.ds-case-band{padding:var(--section-y-compact) var(--gutter-mobile)}}

/* Hero — ink, with the one permitted gradient: a soft green radial glow. */
.ds-case-hero{position:relative;overflow:hidden;background:var(--ink-600);padding:var(--space-13) var(--gutter) var(--space-12)}
@media(max-width:767px){.ds-case-hero{padding:var(--space-11) var(--gutter-mobile) var(--space-9)}}
.ds-case-hero__glow{position:absolute;right:-140px;top:-120px;width:560px;height:560px;border-radius:50%;background:radial-gradient(circle at 50% 50%,rgb(var(--green-400-rgb) / .18) 0%,rgb(var(--green-400-rgb) / 0) 70%);pointer-events:none}
.ds-case-hero__inner{position:relative;max-width:var(--container-max);margin:0 auto}
/* Client badge — translucent paper pill on ink. Uses the -rgb companion,
   which is why the colour bridge has to emit those alongside each colour. */
.ds-case-hero__badge{display:inline-flex;align-items:center;height:24px;padding:0 10px;border-radius:var(--radius-full);background:rgb(var(--paper-rgb) / .10);border:1px solid var(--border-inverse);font:var(--type-eyebrow);letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--paper)}
.ds-case-hero__title{font:var(--type-display-1);color:var(--text-on-inverse);letter-spacing:var(--tracking-display);max-width:20ch;margin:var(--space-6) 0 0;text-wrap:balance}
.ds-case-hero__lead{font:var(--type-body-lg);color:var(--text-on-inverse-muted);max-width:52ch;margin:var(--space-7) 0 0;text-wrap:pretty}
.ds-case-hero__cta{margin-top:var(--space-9)}

/* Facts row — four mono columns under the hero lead. */
.ds-case-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--space-7);margin:var(--space-11) 0 0;padding-top:var(--space-7);border-top:1px solid var(--border-inverse)}
.ds-case-facts__item{display:flex;flex-direction:column;gap:var(--space-3);min-width:0}
.ds-case-facts dt{font:var(--type-eyebrow);letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--text-on-inverse-muted)}
.ds-case-facts dd{margin:0;font:var(--type-body-sm);color:var(--text-on-inverse);text-wrap:pretty}

/* Numbered section eyebrow. */
.ds-case-eyebrow{font:var(--type-eyebrow);letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--text-accent);display:inline-flex;align-items:baseline;gap:var(--space-4)}
.ds-case-eyebrow.ds-on-ink{color:var(--green-300)}
.ds-case-eyebrow__n{font:var(--type-mono);font-size:var(--text-size-micro);letter-spacing:var(--tracking-mono)}
.ds-case-h2{font:var(--type-display-3);color:var(--text-strong);letter-spacing:var(--tracking-display);max-width:24ch;margin:var(--space-5) 0 0;text-wrap:balance}
.ds-case-h2.ds-on-ink{color:var(--text-on-inverse)}

.ds-case-split{display:grid;grid-template-columns:.9fr 1.1fr;gap:var(--space-13);align-items:start}
@media(max-width:1023px){.ds-case-split{grid-template-columns:1fr;gap:var(--space-9)}}
/* The first paragraph is a lead-in: larger and at full ink. Everything after
   it is quieter supporting text. Matches the design system's case-study
   template, where the shift is authored per paragraph — here it is structural,
   so an editor writing in the WYSIWYG gets it without knowing the rule. */
.ds-case-prose{display:flex;flex-direction:column;gap:var(--space-6);max-width:var(--measure)}
.ds-case-prose p{font:var(--type-body);color:var(--text-muted);margin:0;text-wrap:pretty}
.ds-case-prose p:first-child{font:var(--type-body-lg);color:var(--text-body)}

.ds-case-bandhead{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--space-11);flex-wrap:wrap;margin-bottom:var(--space-9)}
.ds-case-bandhead__body{font:var(--type-body);color:var(--text-muted);max-width:46ch;margin:0;text-wrap:pretty}

.ds-case-media{box-shadow:var(--shadow-md);border:1px solid var(--border-subtle)}
.ds-case-band--ink-flush .ds-case-media,.ds-section--ink .ds-case-media{border-color:var(--border-inverse);background:var(--ink-800)}
.ds-case-trio__lead{font:var(--type-body);color:var(--text-muted);max-width:52ch;margin:var(--space-6) 0 0;text-wrap:pretty}
.ds-case-trio{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-7);margin-top:var(--space-9)}
@media(max-width:767px){.ds-case-trio{grid-template-columns:1fr;max-width:390px;margin-inline:auto}}

.ds-stat__source{font:var(--type-mono);font-size:12px;letter-spacing:var(--tracking-mono);color:var(--text-muted);margin-top:var(--space-3)}
.ds-on-ink .ds-stat__source{color:var(--text-on-inverse-muted)}
.ds-section--ink .ds-stat-row{margin-top:var(--space-11)}

.ds-case-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.ds-case-list li{font:var(--type-body);color:var(--text-body);padding:var(--space-5) 0;border-top:1px solid var(--border-subtle);text-wrap:pretty}
.ds-case-list li:last-child{border-bottom:1px solid var(--border-subtle)}

/* Draft badge — coral, so an unapproved quote can't ship unnoticed. */
.ds-badge--draft{display:inline-block;font:var(--type-mono);font-size:11px;letter-spacing:var(--tracking-mono);text-transform:uppercase;color:var(--white);background:var(--coral-600);border-radius:var(--radius-full);padding:4px 12px;margin-bottom:var(--space-7)}

.ds-case-cta{background:var(--green-100);border:1px solid var(--green-200);border-radius:var(--radius-card);padding:var(--space-12);display:flex;align-items:center;justify-content:space-between;gap:var(--space-9);flex-wrap:wrap}
@media(max-width:767px){.ds-case-cta{padding:var(--space-9);flex-direction:column;align-items:flex-start}}
.ds-case-cta .ds-case-h2{margin-top:0}
.ds-case-cta .ds-lead{margin-top:var(--space-5)}

/* Surface feature cards — what was actually solved on that surface. Three or
   none: two leave a hole in the row, and the row is the point. */
.ds-case-features{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-7);margin-top:var(--space-7)}
@media(max-width:1023px){.ds-case-features{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:767px){.ds-case-features{grid-template-columns:1fr}}
.ds-case-feature{display:flex;flex-direction:column;gap:var(--space-5)}

/* Case quote — larger and heavier than the standalone Quote element, because
   here it carries a whole band. The green rule sits beside the quote only;
   the attribution aligns to it with padding, not by extending the border. */
.ds-case-quotegrid{display:grid;grid-template-columns:1.2fr .8fr;gap:var(--space-13);align-items:center}
@media(max-width:1023px){.ds-case-quotegrid{grid-template-columns:1fr;gap:var(--space-9)}}
.ds-case-quote{margin:0}
.ds-case-quote blockquote{margin:0;padding-left:var(--space-8);border-left:2px solid var(--green-400);font:var(--type-display-3);font-weight:var(--weight-medium);letter-spacing:var(--tracking-display);color:var(--text-strong);max-width:38ch;text-wrap:pretty}
.ds-case-quote figcaption{padding-left:var(--space-8);margin-top:var(--space-8);font:var(--type-body-sm);display:flex;flex-direction:column;gap:2px;max-width:36ch}
.ds-case-quote__name{color:var(--text-strong);font-weight:var(--weight-semibold);text-wrap:pretty}
.ds-case-quote__org{color:var(--text-muted);text-wrap:pretty}
.ds-case-stack{display:flex;flex-direction:column;gap:var(--space-5)}
.ds-case-stack__row{display:flex;flex-wrap:wrap;gap:var(--space-3)}
.ds-case-stack__tag{display:inline-flex;align-items:center;height:32px;padding:0 var(--space-5);font:var(--type-mono);font-size:13px;border-radius:var(--radius-full);border:1px solid var(--border-default);color:var(--text-muted)}


/* ── Gallery band: title + lead, screenshot pair, column row, summary ─────
   The column row is hairline-separated rather than carded — these are facts
   about the surface above, not three things to choose between. Cards here
   would read as a pricing table the visitor is meant to act on. */
.ds-case-h2--domain{font-family:var(--font-display);word-break:break-word}
.ds-case-gallery__caption{font:var(--type-mono);font-size:12px;letter-spacing:var(--tracking-mono);color:var(--text-muted);margin:var(--space-5) 0 0}
.ds-case-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--space-9);margin-top:var(--space-11);padding-top:var(--space-9);border-top:1px solid var(--border-subtle)}
.ds-case-col{display:flex;flex-direction:column;gap:var(--space-4);min-width:0;padding-right:var(--space-7);border-right:1px solid var(--border-subtle)}
.ds-case-col:last-child{border-right:0;padding-right:0}
/* Stacked, the columns need a horizontal rule instead of a vertical one:
   keeping padding-right after removing border-right left each row visually
   indented from a border that was no longer there. */
@media(max-width:767px){
.ds-case-cols{gap:0;margin-top:var(--space-9);padding-top:0;border-top:0}
.ds-case-col{border-right:0;padding-right:0;padding-block:var(--space-7);border-top:1px solid var(--border-subtle)}
.ds-case-col:last-child{border-bottom:1px solid var(--border-subtle)}
}
.ds-case-col__value{font:var(--type-mono);font-size:30px;font-weight:var(--weight-bold);letter-spacing:var(--tracking-mono);color:var(--text-strong);white-space:nowrap}
.ds-case-col__unit{font:var(--type-mono);font-size:12px;letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--text-muted)}
.ds-case-col__body{font:var(--type-body-sm);color:var(--text-muted);margin:0;text-wrap:pretty}
.ds-case-gallery__summary{font:var(--type-body);color:var(--text-muted);max-width:var(--measure);margin:var(--space-11) 0 0;text-wrap:pretty}


/* ── Results band ─────────────────────────────────────────────────────────
   Three columns under a hairline. A result is a pill plus a claim by default;
   only a measured figure gets display weight. */
.ds-case-results{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-11);margin-top:var(--space-11);padding-top:var(--space-8);border-top:1px solid var(--border-inverse)}
@media(max-width:900px){.ds-case-results{grid-template-columns:1fr;gap:var(--space-9)}}
.ds-case-result{display:flex;flex-direction:column;gap:var(--space-4);min-width:0}
.ds-case-result__pill{display:inline-flex;align-items:center;align-self:flex-start;height:24px;padding:0 10px;border-radius:var(--radius-full);background:rgb(var(--paper-rgb) / .10);border:1px solid var(--border-inverse);font:var(--type-eyebrow);letter-spacing:var(--tracking-eyebrow);text-transform:uppercase;color:var(--paper)}
.ds-case-result__claim{font:var(--type-body-sm);color:var(--text-on-inverse-muted);max-width:26ch;margin:0;text-wrap:pretty}
.ds-case-result--stat .ds-case-result__claim{max-width:24ch}
.ds-case-result__source{font:var(--type-mono);font-size:12px;letter-spacing:var(--tracking-mono);color:var(--text-on-inverse-muted)}
.ds-case-result--stat .ds-stat__value{font:var(--type-display-3);color:var(--text-on-inverse);letter-spacing:var(--tracking-display);display:flex;align-items:baseline;gap:6px}
.ds-case-result--stat .ds-stat__unit{font:var(--type-mono);font-size:15px;color:var(--green-300);letter-spacing:var(--tracking-mono)}
.ds-case-results__summary{font:var(--type-body-sm);color:var(--text-on-inverse-muted);max-width:64ch;margin:var(--space-9) 0 0;text-wrap:pretty}


/* Linked band heading. The arrow is a glyph, not an icon font — it has to
   survive in a heading at display weight, where a masked span would not
   inherit the optical size. */
.ds-case-h2__link{color:inherit;text-decoration:none;display:inline-flex;align-items:baseline;gap:.2em;transition:color var(--dur-fast) var(--ease-out)}
.ds-case-h2__link:hover{color:var(--text-accent);text-decoration:none}
.ds-case-h2__arrow{font-size:.6em;transition:transform var(--dur-fast) var(--ease-out)}
.ds-case-h2__link:hover .ds-case-h2__arrow{transform:translate(2px,-2px)}
.ds-on-ink .ds-case-h2__link:hover{color:var(--green-300)}
.ds-case-cta__actions{display:flex;gap:var(--space-4);flex-wrap:wrap}


/* ── CTA band ─────────────────────────────────────────────────────────────
   Shared by the «ds · CTA band» element and the case study's closing band.
   Three tones because the primary button's colour depends on its ground:
   green on green-100 fails contrast, so the accent card takes an ink button
   and the paper card takes the green one. Variant names a colour, not a rank. */
.ds-cta-band{border-radius:var(--radius-card);padding:var(--space-12);display:flex;align-items:center;justify-content:space-between;gap:var(--space-9);flex-wrap:wrap}
@media(max-width:900px){.ds-cta-band{padding:var(--space-9);flex-direction:column;align-items:flex-start}}
.ds-cta-band--accent{background:var(--green-100);border:1px solid var(--green-200)}
.ds-cta-band--paper{background:var(--surface-card);border:1px solid var(--border-subtle);box-shadow:var(--shadow-sm)}
.ds-cta-band--ink{background:var(--surface-inverse);border:1px solid var(--border-inverse)}
.ds-cta-band__text{display:flex;flex-direction:column;gap:var(--space-5);min-width:0}
.ds-cta-band__title{font:var(--type-display-3);color:var(--text-strong);letter-spacing:var(--tracking-display);margin:0;max-width:24ch;text-wrap:balance}
.ds-cta-band__lead{font:var(--type-body-lg);color:var(--text-body);margin:0;max-width:52ch;text-wrap:pretty}
.ds-cta-band--ink .ds-cta-band__title{color:var(--text-on-inverse)}
.ds-cta-band--ink .ds-cta-band__lead{color:var(--text-on-inverse-muted)}
.ds-cta-band__actions{display:flex;gap:var(--space-4);flex-wrap:wrap;flex:none}


/* Below 480px the CTA buttons go full width and stack.
   Two buttons of different label lengths side by side on a 390px screen leave
   a ragged pair with no room to breathe; matching their width to the container
   makes them read as one stack of equal choices. The 44px minimum hit target
   is already met by size lg, so this is about rhythm, not reach. */
@media(max-width:479px){
.ds-cta-band__actions,.ds-case-cta__actions,.ds-case-hero__cta{display:flex;flex-direction:column;align-items:stretch;width:100%;gap:var(--space-4)}
.ds-cta-band__actions .ds-btn,.ds-case-cta__actions .ds-btn,.ds-case-hero__cta .ds-btn{display:flex;width:100%}
}


/* The quote-to-stack gap is set for a two-column layout, where the stack sits
   beside the quote. Stacked, the same value reads as cramped — the pills look
   attached to the quote's last line rather than being their own block. */
@media(max-width:1023px){
.ds-case-quotegrid{gap:var(--space-11)}
.ds-case-stack{padding-top:var(--space-7);border-top:1px solid var(--border-inverse)}
}


/* ── Cases archive ────────────────────────────────────────────────────────
   Used by archive-case.php and taxonomy-industry.php.

   Token audit, 2026-09-12. Literals replaced where a token covers the value:
     13px  → var(--text-size-caption)   chip label, pagination numerals
     11px  → var(--text-size-micro)     per-term count

   Literals that REMAIN, each because no token covers them. All are listed in
   design-system/tokens.css under MISSING — they are decisions, not oversights:
     min-height:36px / min-width:40px   control sizing            (MISSING 1)
     border:1px / outline:2px           border + ring width       (MISSING 2, 3)
     font-size:12px on the count line   off-scale, must move      (MISSING 4)
     @media 479px                       CSS cannot read a token here
   Do not "tidy" these into the nearest spacing token — --space-9 happens to
   be 40px, but coupling a control height to the spacing scale means one
   cannot change without the other. */
.ds-archive-head{display:flex;flex-direction:column;gap:var(--space-5);max-width:var(--container-narrow)}
.ds-archive-head .ds-eyebrow{color:var(--text-accent)}
.ds-archive-title{margin-top:0}
.ds-archive-grid{margin-top:var(--space-9)}

/* Industry chips. Pills, because every control in the system is a pill — the
   nav's 2px green underline belongs to the site nav and would read as a second
   navigation bar here. Current is a filled ink pill: the strongest available
   state that still sits on tint without becoming a button.
   Spec: design-system/components/filter-chip.md */
.ds-filter{display:flex;flex-wrap:wrap;gap:var(--space-3);margin-top:var(--space-11)}
/* min-height 36px: MISSING 1 — no control-height token, and 36px is below the
   44px touch minimum. Needs a decision, not a silent bump. */
.ds-filter__chip{display:inline-flex;align-items:center;gap:var(--space-3);min-height:36px;padding:0 var(--space-6);border:1px solid var(--border-default);border-radius:var(--radius-control);background:transparent;font:var(--type-mono);font-size:var(--text-size-caption);letter-spacing:var(--tracking-mono);color:var(--text-muted);text-decoration:none;transition:background-color var(--dur-fast) var(--ease-out),border-color var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out)}
/* Hover moves border AND text, never the fill: these sit on tint, and a
   fill-on-hover makes the row flicker as the pointer crosses it. */
.ds-filter__chip:hover{border-color:var(--ink-600);color:var(--ink-900);text-decoration:none}
/* outline 2px/2px: MISSING 3 — --focus-ring is the colour only. */
.ds-filter__chip:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
.ds-filter__chip:active{transform:scale(var(--press-scale))}
.ds-filter__chip.is-current,.ds-filter__chip.is-current:hover{background:var(--ink-600);border-color:var(--ink-600);color:var(--paper)}
/* The per-term count is a fact about the filter, not part of its label, so it
   sits a tier back — and inverts with the chip, because --text-faint is 3.6:1
   on ink and would fail there. */
.ds-filter__n{font-size:var(--text-size-micro);color:var(--text-faint)}
.ds-filter__chip.is-current .ds-filter__n{color:var(--text-on-inverse-muted)}

/* MISSING 4: this 12px is off the scale — the ladder has 13px (caption) and
   11px (micro) and nothing between. Move this line to one of them; do not add
   a 12px token for a single line. Spec: components/archive-header.md */
.ds-archive-count{font:var(--type-mono);font-size:12px;letter-spacing:var(--tracking-mono);color:var(--text-muted);margin:var(--space-7) 0 0}

/* Left-aligned, not centred: the rest of the page is left-aligned and a
   centred empty state reads as an error page. */
.ds-archive-empty{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-7);padding:var(--space-12) 0}

/* Pagination. Mono numerals on a hairline, matching the case page's rules
   rather than the parent theme's own pagination block.
   Spec: design-system/components/pagination.md */
.ds-pagination{margin-top:var(--space-12);padding-top:var(--space-8);border-top:1px solid var(--border-subtle)}
.ds-pagination .nav-links{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-3)}
/* min 40px target: MISSING 1, and also below the 44px touch minimum. */
.ds-pagination .page-numbers{display:inline-flex;align-items:center;justify-content:center;min-width:40px;min-height:40px;padding:0 var(--space-4);border-radius:var(--radius-control);font:var(--type-mono);font-size:var(--text-size-caption);letter-spacing:var(--tracking-mono);color:var(--text-muted);text-decoration:none;transition:background-color var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out)}
/* Hover DOES fill here, unlike the chips: these sit on paper, where
   --surface-sunken is a quiet step rather than a flicker. */
.ds-pagination a.page-numbers:hover{background:var(--surface-sunken);color:var(--ink-900);text-decoration:none}
.ds-pagination .page-numbers.current{background:var(--ink-600);color:var(--paper)}
.ds-pagination .page-numbers.dots{min-width:0;padding:0 var(--space-2);color:var(--text-faint)}
.ds-pagination .page-numbers.prev,.ds-pagination .page-numbers.next{padding:0 var(--space-6);border:1px solid var(--border-default)}
/* 479px = --breakpoint-sm (480px) minus 1. Literal because custom properties
   cannot be used in media query conditions — not a missing token. */
@media(max-width:479px){.ds-pagination .page-numbers.prev,.ds-pagination .page-numbers.next{flex:1}}
