/* =========================================================================
   Site foundation — tokens, resets, page transitions, and the handful of
   primitives shared by every page.

   Everything here is used by the live site. The original scatter homepage's
   styles (cards, tiles, filters, the bookshelf modal, the marker sweep) lived
   here too and have been removed along with that page — they were 156 of the
   file's 171 rules, none of which matched anything on a page that ships.

   Component styles live beside their component: masthead.css, rail.css,
   study.css, chart.css, carousel.css, compare.css, video.css, alt.css.
   ========================================================================= */

@font-face {
  font-family: 'Inter';
  src: url('../fonts/inter-variable.woff2') format('woff2-variations');
  font-weight: 100 900; font-display: swap;
}

/* Caveat is GONE, with the note cards it was for. It was the handwriting face
   on the ruled cards of the previous homepage; the alt homepage replaced that
   family and nothing has referenced it since — the face, its 50KB file and the
   --hand token were all still here, reading like part of the type system.
   Recoverable from git if handwriting ever comes back. */

@font-face {
  font-family: 'Cormorant';
  src: url('../fonts/cormorant-300.woff2') format('woff2');
  font-weight: 300; font-style: normal; font-display: swap;
}

/* Metric-matched stand-in, used only for the moment before Cormorant arrives.
   Georgia sets "/ v-ee /" 22.1% wider than Cormorant at the same size, which
   is why the masthead's pronunciation gloss visibly jumped on load. Scaling
   Georgia to 81.9% makes the two occupy the same width, so the swap has
   nothing to reflow. Paired with a preload of the real face, which usually
   means no swap happens at all. */
@font-face {
  font-family: 'Cormorant Fallback';
  src: local('Georgia'), local('Times New Roman');
  size-adjust: 81.9%;
  ascent-override: 105%; descent-override: 27%; line-gap-override: 0%;
}

/* There is no Light italic. Every italic on the site is requested at 600 or
   700 — Section Titles, Section Divider Titles, and both parts of a Block
   Quote — and all four resolve to the SemiBold cut below, so the 300 italic
   file was 22KB that nothing could ever reach.

   The upright 300 above stays: the masthead's "/ v-ee /" asks for 400 and
   resolves down to it. */

/* SemiBold italic, for the case study's Section Title and Section Divider
   Title. A real cut, not a synthesised one: with only the 300 face present
   Chrome does not embolden at all — every weight from 300 to 900 measured the
   same advance width — so asking for 600 without this file changes nothing. */
@font-face {
  font-family: 'Cormorant';
  src: url('../fonts/cormorant-600-italic.woff2') format('woff2');
  font-weight: 600; font-style: italic; font-display: swap;
}

:root {
  color-scheme: light;
  --paper:#faf8f5; --card:#fff;
  --ink:#17161c; --ink-soft:#57535e; --ink-faint:#918d99; --line:#e3ded6;
  --accent:#8c2280;
  /* Running-copy grey. Shared by the homepage and the case studies; matches the
     text colour used throughout the CallRail Figma. */
  --body-text:#454545;
  /* Delta pill pairs, from the Figma colour variables. Named by meaning rather
     than hue so a positive delta stays positive if the palette changes.
     Figma: Card/ Color/ Green, Stroke/ Green, Card/ Color/ Red, Stroke/ Gap/ Red */
  --pill-pos-bg:#e5fde8; --pill-pos-ink:#65bf73;
  --pill-neg-bg:#feebea; --pill-neg-ink:#de1306;
  /* the role pill's gradient ground, straight from the design */
  /* ---- shared recipes -------------------------------------------------
     Values that three or more components had each hardcoded. Kept as tokens
     rather than shared classes because components are loaded conditionally —
     a page without a carousel never fetches carousel.css — so a shared class
     would have to live here and style selectors that may not exist. Tokens
     single-source the numbers without coupling the stylesheets. */

  /* Figma "Browser Shadow": three stacked drops. Any large media surface —
     a mockup, a compare frame, a video — sits at this elevation. */
  --shadow-media: 0 10px 50px rgb(0 0 0 / .2), 0 20px 30px rgb(0 0 0 / .3),
                  0 0 1px rgb(0 0 0 / .7);

  /* The two recurring micro-type voices.
     `label`  the site's quietest: metadata, captions, eyebrow text.
     `pill`   one step up and bolder: anything set in a pill or tooltip. */
  /* 12px per the Figma header (node 1602:11904), which sets the meta labels
     and the name at the same size. */
  --type-label-size: 12px; --type-label-weight: 600; --type-label-track: .11em;
  --type-pill-size: 11px;    --type-pill-weight: 700;  --type-pill-track: .1em;

  /* `caption`  the line under a piece of media. Carousel, compare and video
     each had their own copy of this — identical font, size, line-height and
     colour, and three different top margins (10px, 12px, 12px). Which is the
     exact failure mode tokens exist for: nobody chose 10, it just drifted. */
  --type-caption-size: 14px; --type-caption-line: 21px;

  /* Corner radius for any large media surface — the homepage project tiles,
     a case study mockup, a compare frame, a video. One value so a video
     dropped next to a tile reads as the same family of object. */
  --tile-radius: 18px;

  /* THE page measure. One value for the whole site, because the masthead, the
     footer and the section rail are the same furniture on every page and have
     to sit in the same place — they can't do that if each page redefines the
     band they sit in. The homepage used to say 1400/52 and the case studies
     1512/97, which put the masthead's name 11px further left on a case study
     than on the homepage. Taken from the case study Figma grid (1512 frame,
     97px margins, 1318 content band). */
  --page-max: 1512px;
  --page-gutter: clamp(1.25rem, 6.4vw, 97px);
  /* The gutter BETWEEN grid columns, as distinct from the page margin above.
     Not a spacing-scale step: it is part of the grid's own arithmetic (four
     275.5px columns inside the Figma's 1318 band), so it is named here with
     --page-gutter rather than rounded onto the rhythm scale. */
  --grid-gutter: clamp(1.5rem, 4.8vw, 72px);

  /* Padding for anything lozenge-shaped. `em`, not the spacing scale, and
     deliberately so: a pill's padding is proportional to its own text, not to
     the page, so it must scale with font-size and never with viewport. There
     were five variants of this in four files — .35em/.75em, .4em/.8em,
     .45em/.85em, .45em/.7em, .6em/1.1em — none of them chosen, all drifted. */
  --pad-pill: .4em .8em;
  --pad-pill-lg: .6em 1.1em;
  /* No site-wide gradient exists. The role pill on a case study takes that
     project's own gradient via --cs-gradient (see gradients.css); a page that
     declares none falls back to a flat card ground rather than borrowing
     another company's colours. */
  --pill-flat: var(--paper);
  /* The flat project tints (--cr, --lxb, --cp, --sap) are gone. The homepage
     tiles were the only thing that ever named them, and they now take each
     company's real gradient artboard from gradients.css instead — so a tile
     and the case study it opens sit on the same ground. Recoverable from git
     if a flat tint is ever wanted again. */
  --sans:'Inter',-apple-system,BlinkMacSystemFont,'Helvetica Neue',sans-serif;
  --serif:'Cormorant','Cormorant Fallback',Georgia,serif;

  /* ======================= spacing scale ==============================
     Every margin, padding and gap on the site comes from here. `npm run
     audit` fails the build on a literal, so there is no quiet path back to
     bespoke values — which is the whole point: before this there were 17
     one-off clamps and 38 raw px/rem/em values, and nothing to be
     consistent WITH.

     Two families, and the split is the load-bearing idea:

     STATIC — component internals. The gap between a number and its pill,
     a caption and its image, a label and its icon. These must NOT shrink
     on a phone: they are proportional to type, not to the page, and a 12px
     gap that becomes 6px just looks broken.

     FLUID — layout rhythm. Space between blocks and sections, which should
     breathe with the page. One base value with everything derived from it,
     so the steps hold their proportions at every width and the floor, the
     cap and the rate of change are each stated exactly once.

     The cap is the 1512 design frame, matching --page-gutter: past 1512 the
     content band stops growing, so the rhythm must stop with it.

     | step | 375 | 1512+ | typical use |
     |------|-----|-------|-------------|
     | fluid-xs |  16 |  32 | inside a dense block |
     | fluid-s  |  24 |  48 | block internals, tile padding |
     | fluid-m  |  32 |  64 | between a row's own children |
     | fluid-l  |  48 |  96 | between blocks in a section |
     | fluid-xl |  85 | 171 | between sections |
     ==================================================================== */
  --space-3xs: 4px;
  --space-2xs: 8px;
  --space-xs: 12px;
  --space-s: 16px;
  --space-m: 24px;
  --space-l: 32px;

  --space-unit: clamp(3rem, 6.35vw, 96px);
  --space-fluid-xs: calc(var(--space-unit) / 3);
  --space-fluid-s:  calc(var(--space-unit) / 2);
  --space-fluid-m:  calc(var(--space-unit) * 2 / 3);
  --space-fluid-l:  var(--space-unit);
  /* ×171/96 — Vi's section gap over the Figma's block pitch, both measured at
     the 1512 frame. Written as the division so it stays legible as the two
     numbers it came from. */
  --space-fluid-xl: calc(var(--space-unit) * 171 / 96);

  /* ========================== radii ================================== */
  --radius-pill: 999px;      /* any lozenge — pills, chips, round buttons */
  --radius-round: 50%;       /* circles */
  --radius-s: 8px;        /* chips, tooltips, small controls */

  /* ========================= elevation ===============================
     FIVE roles, replacing eighteen bespoke shadows. Four of those eighteen
     were the same resting control differing by ±2px of blur and ±0.04
     alpha, and two were byte-identical panels declared in two files.

     Ink matters: controls on paper use --ink (#17161c) so the shadow is the
     page's own dark, while controls sitting ON an image use pure black,
     which is the only thing that reads against arbitrary photography. */
  /* --shadow-raised, --shadow-overlay and --shadow-onmedia were each defined
     as var(--their-own-name) — a self-reference, which CSS treats as invalid
     at computed-value time and silently resolves to nothing. Every consumer
     of those three (the rail's active pill, the carousel arrows, the compare
     handle, the lightbox close/nav buttons, and now the masthead's floating
     pills) had been rendering with no shadow at all. No prior value survives
     anywhere to recover, so these are reconstructed to match the pattern
     their own "-hi" siblings already established: a lighter, tighter version
     of the same hover shadow, same ink-vs-black basis (ink on paper, pure
     black on media, per the note below). */
  --shadow-raised:      0 2px 8px -2px rgb(23 22 28 / .18);
  --shadow-raised-hi:   0 6px 16px -4px rgb(23 22 28 / .4);
  --shadow-overlay:     0 12px 32px -8px rgb(23 22 28 / .24), 0 4px 12px -6px rgb(23 22 28 / .16);
  --shadow-onmedia:     0 2px 8px rgb(0 0 0 / .35);
  --shadow-onmedia-hi:  0 4px 16px rgb(0 0 0 / .5);

  /* ======================== breakpoints ==============================
     Custom properties cannot be used in a media query, so these are
     documented here and written as literals at each site. Three tiers:

       ≤640    phone          one column
       641-900 tablet         two columns
       ≥901    desktop        the design's full composition

     A component may keep its OWN breakpoint only where a measurement
     justifies it. There are exactly two, both recorded:
       masthead 800 — where its contents stop fitting on one line
       chart    560 — where vertical bars become hairlines and it flips
     The rail's 1130/1400 tiers are measured against the distance from the
     content band to the viewport edge; see rail.css.
     ==================================================================== */
}

/* ===================== cross-page fade transitions =====================
   Every same-origin navigation crossfades instead of cutting. This is the
   browser's own cross-document View Transitions, which is why there is no
   JavaScript here: a scripted fade has to intercept link clicks, and doing
   that correctly means re-implementing cmd-click, middle-click, back/forward
   and prefetch — each one a chance to break navigation, which is the one
   thing on a site that must never break. The native version cannot break it,
   because the browser is still the one navigating.

   Browsers without support navigate exactly as they did before: no
   transition, nothing else different. That is the whole fallback.
   ======================================================================= */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .28s;
  animation-timing-function: cubic-bezier(.4, 0, .2, 1);
}


@media (prefers-reduced-motion: reduce) {
  /* Turning the navigation off is the supported way to opt out; zeroing the
     animation instead can leave a snapshot on screen for a frame. */
  @view-transition { navigation: none; }
}

/* border-box everywhere: the whole layout assumes padding sits inside a
   declared width — the page gutter, the study grid, the video mat all break
   without it. */
* { box-sizing:border-box; margin:0; }

body {
  background:var(--paper); color:var(--ink);
  font-family:var(--sans); font-size:16px; line-height:1.5;
  -webkit-font-smoothing:antialiased;
}

img { display:block; max-width:100%; }

[hidden] { display:none !important; }

.page {
  background: var(--card);
  color: var(--body-text);
  font-family: var(--sans);
  line-height: 1.5;
  letter-spacing: -.02em;
  -webkit-font-smoothing: antialiased;
  /* Sticky-footer layout: a page shorter than the viewport — a case study
     whose body isn't written yet — would otherwise float its footer in the
     middle of the screen. */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

.wrap { max-width:var(--page-max); margin-inline:auto; padding-inline:var(--page-gutter); }

/* ------------------------------- links ----------------------------------
   The marker-sweep link, used wherever body copy links out. It lived in
   alt.css, so case studies had no link styling at all — a link in a case
   study would have rendered as a bare browser default. */
.site-link {
  color: var(--ink); text-decoration: none;
  background-image: linear-gradient(var(--mk-a, rgb(23 22 28 / .10)), var(--mk-b, rgb(23 22 28 / .20)));
  background-repeat: no-repeat;
  background-position: 0 88%;
  background-size: 100% .22em;
  transition: background-size .25s cubic-bezier(.16, 1, .3, 1);
  padding-inline: .06em;   /* audit-ok: the marker stroke's overhang, not layout */
}

.site-link:hover, .site-link:focus-visible { background-size: 100% 82%; }

.label {
  font-size:var(--type-label-size); font-weight:var(--type-label-weight);
  letter-spacing:var(--type-label-track); text-transform:uppercase;
  color:var(--ink-faint); line-height:1.35;
}

/* Visually hidden, still announced. `clip-path` rather than display:none or
   visibility:hidden, both of which remove the element from the accessibility
   tree — which is the one thing this must not do. Used by the chart's data
   table and the carousel's live region. */
.u-sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;   /* audit-ok: the visually-hidden clip idiom */
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.meta-row { display:flex; flex-wrap:wrap; align-items:center;
            gap: var(--space-2xs) var(--space-m); padding-bottom: var(--space-fluid-s); }

.meta-row .who { font-size:12px; font-weight:600; letter-spacing:.09em; text-transform:uppercase; color:var(--ink); }

/* The pronunciation gloss. Figma sets it in Cormorant Regular at 12px — the
   same size as the name it follows, not a size up. Only a 300 upright cut is
   on disk, so 400 resolves to it; see docs/DECISIONS.md. */
.meta-row .who i { font-family:var(--serif); font-style:normal; font-weight:400; font-size:12px; text-transform:none; letter-spacing:0; color:var(--ink-faint); }
