/* ============================================================
   components.css: the components, for every theme

   Nothing here belongs to a theme. A theme is a mode in
   software-subatomic, selected by data-brand on <html>, plus an
   optional themes/<name>.tokens.css that loads after this file and
   can override any token below. Pages must carry a data-brand.

   Colors and type come from @hobbs-og/software (--color-*,
   --typography-*). This file aliases them into component tokens and
   lays the components out.

   Rules:
   1. Every color, size and space is a custom property declared in
      the token blocks below. Rules reference tokens only.
   2. Component tokens alias semantic tokens, so light and dark
      follow the system theme with no per-theme values here, except
      for the wordmark blend mode, which has no semantic equivalent.
   3. Motion is CSS only. Scroll-driven effects sit behind
      @supports and prefers-reduced-motion: no-preference. The
      resting state is the finished state, so browsers without
      support and users who reduce motion see a static page.
   ============================================================ */

/* ---------- Tokens ---------- */

/* Below the large grid, roles use their -mobile tokens, so every theme steps
   down with the screen. A theme can add steps of its own, because it loads last. */
@media (max-width: 59.9375em) {
  :root {
    --typography-display-default-font-size: var(--typography-display-default-mobile-font-size);
    --typography-display-default-line-height: var(--typography-display-default-mobile-line-height);
    --typography-display-default-letter-spacing: var(--typography-display-default-mobile-letter-spacing);
    --typography-display-sm-font-size: var(--typography-display-sm-mobile-font-size);
    --typography-display-sm-line-height: var(--typography-display-sm-mobile-line-height);
    --typography-display-sm-letter-spacing: var(--typography-display-sm-mobile-letter-spacing);
    --typography-headline-lg-font-size: var(--typography-headline-lg-mobile-font-size);
    --typography-headline-lg-line-height: var(--typography-headline-lg-mobile-line-height);
    --typography-headline-lg-letter-spacing: var(--typography-headline-lg-mobile-letter-spacing);
    --typography-headline-default-font-size: var(--typography-headline-default-mobile-font-size);
    --typography-headline-default-line-height: var(--typography-headline-default-mobile-line-height);
    --typography-headline-default-letter-spacing: var(--typography-headline-default-mobile-letter-spacing);
    --typography-headline-sm-font-size: var(--typography-headline-sm-mobile-font-size);
    --typography-headline-sm-line-height: var(--typography-headline-sm-mobile-line-height);
    --typography-headline-sm-letter-spacing: var(--typography-headline-sm-mobile-letter-spacing);
    --typography-title-lg-font-size: var(--typography-title-lg-mobile-font-size);
    --typography-title-lg-line-height: var(--typography-title-lg-mobile-line-height);
    --typography-title-lg-letter-spacing: var(--typography-title-lg-mobile-letter-spacing);
    --typography-display-emphasized-font-size: var(--typography-display-emphasized-mobile-font-size);
    --typography-display-emphasized-line-height: var(--typography-display-emphasized-mobile-line-height);
    --typography-headline-emphasized-font-size: var(--typography-headline-emphasized-mobile-font-size);
    --typography-headline-emphasized-line-height: var(--typography-headline-emphasized-mobile-line-height);
    --typography-title-emphasized-font-size: var(--typography-title-emphasized-mobile-font-size);
    --typography-title-emphasized-line-height: var(--typography-title-emphasized-mobile-line-height);
    --typography-lead-font-size: var(--typography-lead-mobile-font-size);
    --typography-lead-line-height: var(--typography-lead-mobile-line-height);
    --typography-subheading-font-size: var(--typography-subheading-mobile-font-size);
    --typography-subheading-line-height: var(--typography-subheading-mobile-line-height);
  }
}

/* Medium screens: the section heading, card title and hero lead step to
   headline-lg's size, and the subheading to headline-sm's, not to their
   phone size. The card title keeps its 40 line height. */
@media (min-width: 35em) and (max-width: 59.9375em) {
  :root {
    --typography-headline-emphasized-font-size: var(--typography-headline-lg-font-size);
    --typography-headline-emphasized-line-height: var(--typography-headline-lg-line-height);
    --typography-lead-font-size: var(--typography-headline-lg-font-size);
    --typography-lead-line-height: var(--typography-headline-lg-line-height);
    --typography-title-emphasized-font-size: var(--typography-headline-lg-font-size);
    --typography-subheading-font-size: var(--typography-headline-sm-font-size);
    --typography-subheading-line-height: var(--typography-headline-sm-line-height);
  }
}

/* Type roles only this site uses. A theme can override any of them. */
:root {
  --typography-font-family-jetbrains-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --typography-font-family-mono: var(--typography-font-family-jetbrains-mono);
  --typography-eyebrow-default-font-size: 0.75rem;
  --typography-eyebrow-default-line-height: 1rem;
  --typography-eyebrow-default-font-weight: 500;
  --typography-eyebrow-default-letter-spacing: 0.06em;
  --typography-eyebrow-default-font-family: var(--typography-font-family-base);
  --typography-eyebrow-lg-font-size: 0.75rem;
  --typography-eyebrow-lg-line-height: 1rem;
  --typography-eyebrow-lg-font-weight: 500;
  --typography-eyebrow-lg-letter-spacing: 0.06em;
  --typography-eyebrow-lg-font-family: var(--typography-font-family-base);
  --typography-nav-default-font-size: 0.875rem;
  --typography-nav-default-line-height: 1.5rem;
  --typography-nav-default-font-weight: 400;
  --typography-nav-default-letter-spacing: 0;
  --typography-nav-default-font-family: var(--typography-font-family-base);
  --typography-logo-default-font-size: 1rem;
  --typography-logo-default-line-height: 1.5rem;
  --typography-logo-default-font-weight: 600;
  --typography-logo-default-letter-spacing: 0;
  --typography-logo-default-font-family: var(--typography-font-family-base);
  --typography-display-lg-font-size: 10rem;
  --typography-display-lg-line-height: 10rem;
  --typography-display-lg-font-weight: 900;
  --typography-display-lg-letter-spacing: -0.05em;
  --typography-display-lg-font-family: var(--typography-font-family-base);
  --typography-display-section-font-size: clamp(2.5rem, 11vw, 4.5rem);
  --typography-display-section-line-height: 1.111;
  --typography-display-section-font-weight: 300;
  --typography-display-section-letter-spacing: -0.05em;
  --typography-display-section-font-family: var(--typography-font-family-base);
  --typography-numeral-lg-font-size: 2rem;
  --typography-numeral-lg-line-height: 1;
  --typography-numeral-lg-font-weight: 900;
  --typography-numeral-lg-letter-spacing: -0.04em;
  --typography-numeral-lg-font-family: var(--typography-font-family-base);
  --typography-wordmark-default-font-size: 37.5cqi;
  --typography-wordmark-default-line-height: 1;
  --typography-wordmark-default-font-weight: 700;
  --typography-wordmark-default-letter-spacing: -0.1em;
  --typography-wordmark-default-font-family: var(--typography-font-family-base);
}

@media (min-width: 35em) {
  :root {
    --typography-numeral-lg-font-size: 2.5rem;
  }
}

@media (min-width: 60em) {
  :root {
    --typography-numeral-lg-font-size: 3.5rem;
  }
}

:root {
  /* Grid gutter follows software's grid token (1 / 1rem below 60em; 48px
     at large via --page-grid-gutter, see the 60em block).
     To give a theme its own gutter, set --page-grid-gutter here, or inside a
     breakpoint below, e.g. --page-grid-gutter: var(--spacing-48). */
  --page-spacing-gutter: var(--page-grid-gutter, var(--container-gutter));
  --media-frame-spacing-gap: var(--page-spacing-gutter);

  /* Page grid overlay: the four modules from 35em. Phones draw one, the
     outer edges only (Mark, 2026-10-27). */
  --page-grid-display: block;
  --page-grid-modules: 1;
  --page-grid-color-border: var(--color-border-subtle);
  --page-grid-border-width: 0.0625rem;

  /* page */
  --page-color-background-default: var(--color-background-default);
  --page-color-content-default: var(--color-content-emphasized);
  --page-color-content-link-hover: var(--color-content-link-hover);
  --page-color-border-focus: var(--color-border-focus);
  --page-border-width-focus: 0.125rem;
  --page-spacing-focus-offset: 0.1875rem;
  --page-spacing-padding-x: var(--spacing-16);
  --page-size-max-width: 100rem;

  /* motion */
  --motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);
  --motion-easing-out: cubic-bezier(0.5, 1, 0.89, 1);

  /* section rhythm */
  --section-spacing-padding-y: var(--spacing-168);
  --section-spacing-padding-x: var(--spacing-0);

  /* navbar comes from software. These are the site's values for it. */
  --navbar-color-background-default: color-mix(in srgb, var(--color-background-default) 72%, transparent);
  --navbar-color-content-default: var(--color-content-emphasized);
  --navbar-color-content-hover: var(--color-content-link-hover);
  --navbar-color-content-active: var(--color-content-brand);
  --navbar-spacing-nav-links-gap: var(--spacing-24);
  --site-nav-size-blur: 1rem;
  --site-nav-size-icon: 1rem;
  /* One row of logo plus the padding and border around it, so the height
     follows those tokens and is never typed in. */
  --site-nav-size-height: calc(var(--navbar-spacing-padding-y) * 2 + var(--typography-line-height-32) + var(--navbar-border-width-default));
  --site-nav-spacing-links-gap-y: var(--spacing-8);
  --site-nav-spacing-toggle-gap: var(--spacing-2);
  --page-spacing-icon-gap: var(--spacing-8);
  --site-nav-spacing-toggle-padding-x: var(--spacing-8);
  --site-nav-spacing-toggle-padding-y: var(--spacing-4);
  --site-nav-size-toggle-bar-width: 1.25rem;
  --site-nav-size-toggle-bar-thickness: 0.0625rem;
  --site-nav-spacing-toggle-bar-gap: 0.3125rem;
  --site-nav-color-logo-band: color-mix(in srgb, var(--color-background-brand) 40%, transparent);
  --site-nav-motion-range: 6rem;

  /* nav-panel: the phone menu, under the header */
  --nav-panel-spacing-padding-x: var(--page-spacing-padding-x);
  --nav-panel-spacing-padding-top: var(--spacing-48);
  --nav-panel-spacing-links-gap: var(--spacing-16);
  --nav-panel-spacing-switches-margin-top: var(--spacing-48);
  --nav-panel-spacing-switches-gap: var(--spacing-24);
  --nav-panel-color-content-link: var(--navbar-color-content-default);
  --nav-panel-color-content-link-hover: var(--navbar-color-content-hover);

  /* index-hero */
  --index-hero-color-content-eyebrow: var(--color-content-subtle);
  --index-hero-color-content-title: var(--color-content-subtitle);
  --index-hero-color-content-sub: var(--color-content-subtle);
  --index-hero-spacing-padding-top: var(--spacing-96);
  /* The wordmark's baseline lands on the bottom edge of the viewport on
     load. The hero is one screen tall below the nav plus room for the
     descender of the "g", which hangs below the fold. */
  --index-hero-size-descender: 8vw;
  --index-hero-size-min: calc(100svh - var(--site-nav-size-height) + var(--index-hero-size-descender));
  --index-hero-spacing-gap-min: var(--spacing-120);
  --index-hero-spacing-intro-gap: var(--spacing-24);
  --index-hero-spacing-statement-gap: var(--spacing-8);
  /* Empty line box above the wordmark's letters: its baseline sits
     0.8633em from the box top and Inter's ascender (h, b, d at opsz 32)
     is 0.7275em tall, so the ink starts 0.1358em down. */
  --index-hero-spacing-wordmark-ink-top: calc(var(--typography-wordmark-default-font-size) * (0.8633 - 0.7275));

  /* wordmark */
  --wordmark-color-content-primary: color-mix(in srgb, var(--color-background-brand) 86%, white);
  --wordmark-color-content-secondary: var(--color-content-brand-secondary);
  --wordmark-blend: multiply;
  --wordmark-blend-secondary: normal;
  --wordmark-opacity: 1;
  --wordmark-opacity-dark: 0.6;
  --wordmark-spacing-baseline-trim: -0.1367em;
  --wordmark-spacing-optical-start: -0.05em;
  --wordmark-spacing-optical-end: -0.05em;
  --wordmark-motion-amplitude: 0.08em;
  --wordmark-motion-stagger: 2vh;
  --wordmark-motion-length: 70vh;

  /* stat-band comes from software. The wide gap, the stagger and the motion
     are this page's composition. */
  --stat-band-spacing-stat-gap: var(--spacing-240);
  --site-stat-spacing-stagger: var(--spacing-120);
  --stat-band-motion-stagger: 15vh;
  --stat-band-motion-length: 70vh;

  /* feature-list */
  --feature-list-color-content-title: var(--color-content-headline);
  --feature-list-color-content-lead: var(--color-content-subtitle);
  --feature-list-spacing-lead-gap: var(--spacing-8);
  --feature-list-spacing-list-margin-top: var(--spacing-120);

  /* feature-row */
  --feature-row-elevation-flat:
    drop-shadow(0 0.0625rem 0.0625rem color-mix(in srgb, var(--color-elevation-shadow) 16.5%, transparent))
    drop-shadow(0 0.375rem 0.625rem color-mix(in srgb, var(--color-elevation-shadow) 19.25%, transparent))
    drop-shadow(0 1.375rem 2rem color-mix(in srgb, var(--color-elevation-shadow) 22%, transparent));
  --feature-row-elevation-phone:
    drop-shadow(0 0.125rem 0.1875rem color-mix(in srgb, var(--color-elevation-shadow) 22%, transparent))
    drop-shadow(0 1.25rem 2.5rem color-mix(in srgb, var(--color-elevation-shadow) 33%, transparent))
    drop-shadow(0 3rem 5rem color-mix(in srgb, var(--color-elevation-shadow) 27.5%, transparent));
  --feature-row-spacing-specs-margin-top: var(--spacing-24);
  --feature-row-spacing-lead-specs-margin-top: var(--spacing-32);
  --feature-row-size-spec-min: 9.375rem;
  --feature-row-motion-drift: 1.5rem;

  /* index-hero actions */
  --button-spacing-actions-gap-x: var(--spacing-32);
  --button-spacing-actions-gap-y: var(--spacing-16);

  /* how-i-work */
  --how-i-work-color-content-title: var(--color-content-headline);
  --how-i-work-color-content-body: var(--color-content-default);
  --how-i-work-color-content-quote: var(--color-content-brand);
  --how-i-work-spacing-row-gap: var(--spacing-48);
  --how-i-work-spacing-body-gap: var(--spacing-24);
  --how-i-work-size-body-column-min: 15rem;

  /* page-hero */
  --page-hero-border-width-band: var(--stat-band-border-width-default);
  --page-hero-spacing-title-optical: -0.039em;

  /* content-section */
  --content-section-color-content-heading: var(--color-content-headline);
  --content-section-color-content-caption: var(--color-content-default);
  --case-flow-spacing-row-gap: var(--spacing-48);
  --content-section-spacing-rule: var(--spacing-64);
  --content-section-border-width-rule: 0.0625rem;
  /* The rule under each h2 matches the hero title row's 4px rule. */
  --content-section-color-border-rule: var(--page-grid-color-border);
  /* Scales with the title: 48px (--spacing-48) at the 136px desktop
     title, as a share of the title size. A fixed 48px crowds the arrow off the
     row once the title shrinks on phones. */
  --page-hero-scale-title-with-icon: 0.86;
  /* software page-hero and content-section tokens, set for this site: the
     title row opens 192px under the band, and both arrows sit a gap away
     that scales with the type (48px at the 136px desktop title). */
  --page-hero-spacing-heading-gap: calc(var(--spacing-48) * 4);
  --page-hero-spacing-title-gap: calc(1em * 48 / 136);
  --content-section-spacing-title-gap: calc(1em * 48 / 136);
  /* Title size: fills the hero (20cqi sets an 8-letter name edge to edge)
     and stops at display-lg (10rem) on wide screens. Line height stays at
     display-lg's 1:1 ratio, so it scales with the size. */
  --page-hero-font-size-title: min(var(--typography-display-lg-font-size), 20cqi);
  --page-hero-line-height-title: 1;
  /* Same ratio as the hero title arrow: 48px at 136px type. */
  --content-section-spacing-head-padding-top: var(--spacing-24);
  --content-section-spacing-row-gap: var(--spacing-24);

  --content-section-spacing-lead-row-gap: var(--spacing-16);

  /* case-readout, case-notes */
  --case-readout-color-border-default: var(--color-border-strong);
  --case-readout-color-content-label: var(--color-content-subtle);
  --case-readout-color-background-brand: var(--color-background-brand);
  --case-readout-color-content-brand: var(--color-content-knockout);
  --case-readout-border-width: 0.0625rem;
  --case-readout-spacing-padding: var(--spacing-16);
  --case-readout-spacing-gap: var(--spacing-40);
  --case-readout-spacing-desc-margin-top: var(--spacing-8);

  /* sheet comes from software. Width and motion are the site's. */
  --sheet-size-container-max: 100rem;
  --sheet-motion-duration: var(--motion-duration-lg);
  --sheet-motion-easing: cubic-bezier(0.32, 0.72, 0, 1);
  --sheet-motion-duration-ui: var(--motion-duration-sm);

  /* feature-row--next */
  --next-color-content-title: var(--color-content-headline);
  --next-spacing-gap: var(--spacing-16);
}

@media (min-width: 35em) {
  :root {
    --page-spacing-padding-x: var(--spacing-24);
    --page-grid-modules: 4;
  }
}

@media (min-width: 60em) {
  :root {
    /* Large grid gutter: 48px. --container-gutter stops at 24px, so the
       components set their own. Layout and the grid-line overlay both read it. */
    --page-grid-gutter: var(--spacing-48);
    --page-spacing-padding-x: max(var(--spacing-48), calc((100% - var(--page-size-max-width)) / 2));
  }
}

/* The wordmark's two colors overlap; the overlap reads darker on light
   and lighter on dark. No semantic token carries a blend mode. In dark,
   the red takes the same accent as the stat numbers
   (--color-content-headline-brand) and screens over the blue, which
   on a near-black page only lightens the overlap. The logo is
   exempt from contrast minimums. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --wordmark-blend: screen;
    --wordmark-blend-secondary: normal;
    --wordmark-color-content-primary: var(--color-content-headline-brand);
    --wordmark-opacity: var(--wordmark-opacity-dark);
  }
}

[data-theme="dark"] {
  --wordmark-blend: screen;
  --wordmark-blend-secondary: normal;
  --wordmark-color-content-primary: var(--color-content-headline-brand);
    --wordmark-opacity: var(--wordmark-opacity-dark);
}

/* ---------- Page ---------- */

body {
  background: var(--page-color-background-default);
  color: var(--page-color-content-default);
  font-family: var(--typography-body-default-font-family);
  font-size: var(--typography-body-default-font-size);
  line-height: var(--typography-body-default-line-height);
  font-weight: var(--typography-body-default-font-weight);
}

/* Page grid overlay. Each module is three columns; lines mark both edges
   of every module. Set --page-grid-display: none to hide it. */
body::before {
  --period: calc((100% + var(--page-spacing-gutter)) / var(--page-grid-modules));
  content: "";
  display: var(--page-grid-display);
  position: fixed;
  inset-block: 0;
  inset-inline: var(--page-spacing-padding-x);
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--page-grid-color-border) 0 var(--page-grid-border-width), transparent var(--page-grid-border-width)),
    linear-gradient(to right, transparent calc(100% - var(--page-spacing-gutter) - var(--page-grid-border-width)), var(--page-grid-color-border) calc(100% - var(--page-spacing-gutter) - var(--page-grid-border-width)) calc(100% - var(--page-spacing-gutter)), transparent calc(100% - var(--page-spacing-gutter)));
  background-size: var(--period) 100%;
  background-repeat: repeat-x;
}

a {
  color: inherit;
  text-decoration: none;
}

a:hover {
  color: var(--page-color-content-link-hover);
}

:focus-visible {
  outline: var(--page-border-width-focus) solid var(--page-color-border-focus);
  outline-offset: var(--page-spacing-focus-offset);
}

:is(h1, h2, h3, h4, p, dl, dd, figure, ul) {
  margin: 0;
}

/* Content can come from a CMS, so a word too long for its column breaks
   instead of pushing the page sideways. Words that fit never change. */
:is(h1, h2, h3, h4, p, dd, li, figcaption) {
  overflow-wrap: break-word;
}

.container {
  padding-inline: var(--page-spacing-padding-x);
}

.grid {
  column-gap: var(--page-spacing-gutter);
  row-gap: 0;
}

/* .section comes from software. Here it opens with space above only, and
   its children don't stack. */
.section {
  display: block;
  padding-block-end: 0;
}

.section-label {
  font-family: var(--typography-eyebrow-default-font-family);
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
  font-weight: var(--typography-eyebrow-default-font-weight);
  letter-spacing: var(--typography-eyebrow-default-letter-spacing);
  text-transform: uppercase;
}

code {
  font-family: inherit;
}

/* ---------- navbar ---------- */

.navbar {
  position: sticky;
  inset-block-start: 0;
  z-index: 40;
  -webkit-backdrop-filter: blur(var(--site-nav-size-blur));
  backdrop-filter: blur(var(--site-nav-size-blur));
}

.navbar__inner {
  gap: var(--site-nav-spacing-links-gap-y) var(--navbar-spacing-nav-links-gap);
}

.navbar__logo {
  margin-inline-end: auto;
  flex: none;
  color: var(--navbar-color-content-default);
  font-size: var(--typography-logo-default-font-size);
  line-height: var(--typography-logo-default-line-height);
  font-weight: var(--typography-logo-default-font-weight);
  white-space: nowrap;
}

/* Logo, as production (organisms/nav.css): "hobbs." bold against
   "design" regular at 24/32, with a marker highlight drawn as an inset
   shadow rising 0.5em from the bottom of the line box. Highlight is the
   portfolio brand's #E12A0966 (brand red at 40%). The inner span keeps
   the highlight on the word while the link flexes to centre the nav. */
.navbar__logo-type {
  display: inline-block;
  font-size: var(--typography-font-size-24);
  line-height: var(--typography-line-height-32);
  font-weight: var(--typography-font-weight-400);
  color: var(--color-content-headline);
  box-shadow: 0 -0.5em 0 var(--site-nav-color-logo-band) inset;
  transition: color var(--motion-duration-sm) ease;
}

.navbar__logo-mark {
  font-weight: var(--typography-font-weight-700);
}

.navbar__logo:hover .navbar__logo-type {
  color: var(--color-content-link-hover);
}

/* The switch group sits on the links' baseline: it takes the links' type
   so it has a real line box (the switch's own text is visually hidden,
   so on its own it had no baseline and its bottom edge sat on the
   links' baseline, 4px high). The switch centers on the middle of the
   link caps (Mark, 2026-10-28). Its baseline comes from its first item,
   the icon's bottom edge, and the icon and track share one center, so
   it's raised half the cap height (Inter caps are 0.727em) less half
   the icon's height. */
.navbar__switches {
  flex: none;
  display: block;
  text-align: end;
  font-size: var(--typography-meta-sm-font-size);
  line-height: var(--typography-meta-sm-line-height);
}

.navbar__switches .switch {
  vertical-align: calc(var(--typography-meta-sm-font-size) * 0.727 / 2 - var(--site-nav-size-icon) / 2);
}

/* The links center in the space between the logo and the toggle, not
   on the page (Mark, 2026-10-28): the logo and switch group take their
   own widths and the links take the rest. */
.navbar__links {
  flex: 1 1 auto;
  display: none;
  justify-content: center;
  gap: var(--site-nav-spacing-links-gap-y) var(--navbar-spacing-nav-links-gap);
  font-family: var(--typography-meta-sm-font-family);
  font-size: var(--typography-meta-sm-font-size);
  line-height: var(--typography-meta-sm-line-height);
  font-weight: var(--typography-font-weight-400);
  letter-spacing: var(--typography-letter-spacing-half);
  /* Nav only: meta-sm at regular weight with half tracking, in uppercase.
     Swiss meta-sm is the heavier eyebrow label. */
  text-transform: uppercase;
}

.navbar__links a {
  gap: var(--page-spacing-icon-gap);
}

.navbar .icon {
  inline-size: var(--site-nav-size-icon);
  aspect-ratio: 1;
}

/* .switch comes from software. Here it takes the nav's type and color, stays
   hidden below 35em, and animates. */
.switch {
  display: none;
  font-family: inherit;
  font-size: var(--typography-nav-default-font-size);
  line-height: var(--typography-nav-default-line-height);
  font-weight: inherit;
  color: var(--navbar-color-content-default);
}

.switch input {
  transition: background-color var(--motion-duration-sm) ease, border-color var(--motion-duration-sm) ease;
}

.switch input::before {
  transition: translate var(--motion-duration-sm) var(--motion-easing-standard), background-color var(--motion-duration-sm) ease;
}

@media (prefers-reduced-motion: reduce) {
  .switch input,
  .switch input::before {
    transition: none;
  }
}

.navbar__toggle {
  appearance: none;
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--site-nav-spacing-toggle-bar-gap);
  padding: var(--site-nav-spacing-toggle-padding-x);
  border: 0;
  background: transparent;
  align-self: center;
}

.navbar__toggle span {
  display: block;
  inline-size: var(--site-nav-size-toggle-bar-width);
  border-block-start: var(--site-nav-size-toggle-bar-thickness) solid var(--navbar-color-content-default);
}

@media (min-width: 35em) {
  .navbar__links,
  .switch {
    display: flex;
  }

  .navbar__switches .switch {
    display: inline-flex;
  }

  .navbar__toggle {
    display: none;
  }
}

/* ---------- index-hero + wordmark ---------- */

/* One-screen grid so the wordmark baseline always lands on the bottom
   edge of the viewport: free space above the intro, then the intro, then
   a gap that takes three parts of that free space to the top's one and
   stops at 120px on short screens, then the wordmark. The "g" descender hangs into the
   bottom padding, below the fold. */
.index-hero {
  container-type: inline-size;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 1fr auto minmax(var(--index-hero-spacing-gap-min), 3fr) auto;
  min-block-size: var(--index-hero-size-min);
  padding-block: 0 var(--index-hero-size-descender);
}

.index-hero > .index-hero__intro {
  grid-row: 2;
}

.index-hero > .index-hero__wordmark {
  grid-row: 4;
}

/* Desktop: the intro sits centered between the header and the top of the
   wordmark's letters (Mark, 2026-10-27). It takes the whole space above
   the wordmark as one row and centers in it. Its top and bottom padding
   add up to the 120px minimum gap, and the row's free space does the
   rest, so the taller intro doesn't push the wordmark past the fold.
   The padding is shifted by
   half the wordmark's empty line box, so the visible gaps come out even:
   header to intro equals intro to the tops of the letters. */
@media (min-width: 60em) {
  .index-hero {
    grid-template-rows: 1fr auto;
  }

  .index-hero > .index-hero__intro {
    grid-row: 1;
    align-content: center;
    padding-block:
      calc(var(--index-hero-spacing-gap-min) / 2 + var(--index-hero-spacing-wordmark-ink-top) / 2)
      calc(var(--index-hero-spacing-gap-min) / 2 - var(--index-hero-spacing-wordmark-ink-top) / 2);
  }

  .index-hero > .index-hero__wordmark {
    grid-row: 2;
  }
}

.index-hero__intro {
  row-gap: var(--index-hero-spacing-intro-gap);
  align-items: first baseline;
}

.index-hero__eyebrow {
  color: var(--index-hero-color-content-eyebrow);
}

.index-hero__title {
  font-family: var(--typography-display-sm-font-family);
  font-size: var(--typography-display-sm-font-size);
  line-height: var(--typography-display-sm-line-height);
  font-weight: var(--typography-display-sm-font-weight);
  letter-spacing: var(--typography-display-sm-letter-spacing);
  color: var(--index-hero-color-content-title);
  text-wrap: balance;
}

.index-hero__statement {
  display: grid;
  row-gap: var(--index-hero-spacing-statement-gap);
}

.index-hero__sub {
  font-family: var(--typography-body-default-font-family);
  font-size: var(--typography-body-default-font-size);
  line-height: var(--typography-body-default-line-height);
  font-weight: var(--typography-body-default-font-weight);
  letter-spacing: var(--typography-body-default-letter-spacing);
  color: var(--index-hero-color-content-sub);
  text-wrap: balance;
}

/* Pull the box's bottom edge up to the alphabetic baseline so the grid
   row ends on it. Inter at line-height 1 puts the baseline 0.8633em from
   the top, leaving 0.1367em below it. A metric margin works in every
   browser, unlike text-box. */
.index-hero__wordmark {
  margin-block-end: var(--wordmark-spacing-baseline-trim);
  display: grid;
  /* One column exactly the grid's width. An auto column grows to fit
     ".design" (a hair wider than the grid at 37.5cqi), which pushed its
     end past the grid line. Fixed to the grid, justify-self: end keeps
     the n on the right edge and the dot's side bearing absorbs the rest. */
  grid-template-columns: minmax(0, 1fr);
  font-family: var(--typography-wordmark-default-font-family);
  font-size: var(--typography-wordmark-default-font-size);
  line-height: var(--typography-wordmark-default-line-height);
  font-weight: var(--typography-wordmark-default-font-weight);
  letter-spacing: var(--typography-wordmark-default-letter-spacing);
  white-space: nowrap;
}

.index-hero__word {
  grid-area: 1 / 1;
  opacity: var(--wordmark-opacity);
}

.index-hero__word--secondary {
  justify-self: end;
  margin-inline-end: var(--wordmark-spacing-optical-end);
  color: var(--wordmark-color-content-secondary);
  mix-blend-mode: var(--wordmark-blend-secondary);
}

.index-hero__word--primary {
  justify-self: start;
  margin-inline-start: var(--wordmark-spacing-optical-start);
  color: var(--wordmark-color-content-primary);
  mix-blend-mode: var(--wordmark-blend);
}

.index-hero__letter {
  display: inline-block;
}

/* Tracking is added after every letter, the last one included, which
   left ".design"'s final n 0.1em past its box and past the grid. With
   the trailing tracking gone, the -0.05em optical offset cancels the
   glyph's side bearing and the ink sits on the grid edge. */
.index-hero__letter:last-child {
  letter-spacing: 0;
}

/* Each letter's order across both words, its direction (even rises,
   odd drops) and its speed. */
.index-hero__word--primary .index-hero__letter:nth-child(1) { --i: 0; --dir: -1; --k: 0.55; }
.index-hero__word--primary .index-hero__letter:nth-child(2) { --i: 1; --dir: 1; --k: 1.2; }
.index-hero__word--primary .index-hero__letter:nth-child(3) { --i: 2; --dir: -1; --k: 0.8; }
.index-hero__word--primary .index-hero__letter:nth-child(4) { --i: 3; --dir: 1; --k: 1.45; }
.index-hero__word--primary .index-hero__letter:nth-child(5) { --i: 4; --dir: -1; --k: 0.65; }
.index-hero__word--secondary .index-hero__letter:nth-child(1) { --i: 5; --dir: 1; --k: 1.05; }
.index-hero__word--secondary .index-hero__letter:nth-child(2) { --i: 6; --dir: -1; --k: 1.6; }
.index-hero__word--secondary .index-hero__letter:nth-child(3) { --i: 7; --dir: 1; --k: 0.9; }
.index-hero__word--secondary .index-hero__letter:nth-child(4) { --i: 8; --dir: -1; --k: 1.3; }
.index-hero__word--secondary .index-hero__letter:nth-child(5) { --i: 9; --dir: 1; --k: 0.7; }
.index-hero__word--secondary .index-hero__letter:nth-child(6) { --i: 10; --dir: -1; --k: 1.15; }
.index-hero__word--secondary .index-hero__letter:nth-child(7) { --i: 11; --dir: 1; --k: 1.5; }

/* ---------- stat-band ---------- */

/* stat-band comes from software. Here it sits close under the hero, and steps
   down the page. */
.stat-band .stat-band__grid {
  margin-block-start: var(--spacing-24);
  padding-block-start: 0;
}

.stat:nth-of-type(2) { --i: 1; margin-block-start: var(--site-stat-spacing-stagger); }
.stat:nth-of-type(3) { --i: 2; margin-block-start: calc(var(--site-stat-spacing-stagger) * 2); }
.stat:nth-of-type(1) { --i: 0; }

/* The stats share one staggered row at every width. Desktop: the spacer
   fills the first 3-column module and each stat takes one module after
   it. Tablet mirrors that on the 8-column grid's 2-column modules
   (columns 3-4, 5-6, 7-8), so the rules sit on the grid lines. Phones
   have two modules for three stats, so they split the width evenly. */
@media (max-width: 34.9375em) {
  .stat-band__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .stat-band__grid > .stat {
    grid-column: auto;
  }
}

@media (min-width: 35em) and (max-width: 59.9375em) {
  .stat-band__grid > .stat:nth-of-type(1) { grid-column: 3 / span 2; }
  .stat-band__grid > .stat:nth-of-type(2) { grid-column: 5 / span 2; }
  .stat-band__grid > .stat:nth-of-type(3) { grid-column: 7 / span 2; }
}

.stat__label {
  font-family: var(--typography-eyebrow-default-font-family);
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
  font-weight: var(--typography-eyebrow-default-font-weight);
  letter-spacing: var(--typography-eyebrow-default-letter-spacing);
  text-transform: uppercase;
}

.stat__value {
  font-family: var(--typography-numeral-lg-font-family);
  font-size: var(--typography-numeral-lg-font-size);
  line-height: var(--typography-numeral-lg-line-height);
  font-weight: var(--typography-numeral-lg-font-weight);
  letter-spacing: var(--typography-numeral-lg-letter-spacing);
}

.stat__reel {
  display: inline-flex;
}

/* One digit window. The hidden ::after sizes it to one glyph; ::before
   is a strip of 0–9 repeated four times that slides into place. The
   window clips only top and bottom. The numerals' negative tracking
   makes each box narrower than its glyph, so clipping the sides too cut
   the right edge off every digit (Mark saw it on the last one). The
   sides extend by the tracking, the width the box lost. */
.stat__digit {
  position: relative;
  display: inline-block;
  clip-path: inset(0 min(0em, var(--typography-numeral-lg-letter-spacing)));
}

.stat__digit::after {
  content: attr(data-digit);
  visibility: hidden;
}

.stat__digit::before {
  content: "0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9\A 0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9\A 0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9\A 0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  white-space: pre;
  text-align: center;
  translate: 0 calc(var(--d) * -1em);
}

.stat__digit[data-digit="0"] { --d: 0; }
.stat__digit[data-digit="1"] { --d: 1; }
.stat__digit[data-digit="2"] { --d: 2; }
.stat__digit[data-digit="3"] { --d: 3; }
.stat__digit[data-digit="4"] { --d: 4; }
.stat__digit[data-digit="5"] { --d: 5; }
.stat__digit[data-digit="6"] { --d: 6; }
.stat__digit[data-digit="7"] { --d: 7; }
.stat__digit[data-digit="8"] { --d: 8; }
.stat__digit[data-digit="9"] { --d: 9; }
.stat__digit:nth-child(1) { --spin: 20; }
.stat__digit:nth-child(2) { --spin: 30; }
.stat__digit:nth-child(3) { --spin: 20; }

/* ---------- feature-list, feature-row ----------
   Both come from software. Here a row takes the Swiss look: uppercase
   type, the heavier title, device shadows, depth blur, scroll drift and
   motion. A preset sets where each layer sits; these rules set how it
   looks, slot by slot. The list's header is the site's own. */

.feature-list__header {
  align-items: baseline;
}

.feature-list__title {
  font-family: var(--typography-display-section-font-family);
  font-size: var(--typography-display-section-font-size);
  line-height: var(--typography-display-section-line-height);
  font-weight: var(--typography-display-section-font-weight);
  letter-spacing: var(--typography-display-section-letter-spacing);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--feature-list-color-content-title);
}

.feature-list__lead {
  margin-block-start: var(--feature-list-spacing-lead-gap);
  font-family: var(--typography-title-lg-font-family);
  font-size: var(--typography-title-lg-font-size);
  line-height: var(--typography-title-lg-line-height);
  font-weight: var(--typography-display-section-font-weight);
  letter-spacing: var(--typography-display-sm-letter-spacing);
  color: var(--feature-list-color-content-lead);
  text-wrap: balance;
}

/* Fact lists sit on the page grid's gutter, which is wider than the
   container gutter software uses. */
.feature-row__specs {
  column-gap: var(--page-spacing-gutter);
}

@media (min-width: 60em) {
  .feature-list__list > .feature-row:nth-child(odd of .feature-row) .hyperlink--cta {
    margin-inline-start: calc(50% + var(--page-spacing-gutter) / 2);
  }
}

.feature-row__title {
  transition: color var(--motion-duration-sm) ease;
}

.feature-row__media > img {
  filter: var(--feature-row-elevation-flat);
  --drift: var(--feature-row-motion-drift);
}

.feature-row__media--devices > .feature-row__layer {
  filter: var(--layer-blur, ) var(--layer-elevation, var(--feature-row-elevation-flat));
}

:is(.feature-row__media--laptop-plus-tablet, .feature-row__media--laptop-plus-phones) > .feature-row__layer:nth-child(1) { --drift: 1rem; }
:is(.feature-row__media--laptop-plus-tablet, .feature-row__media--laptop-plus-phones) > .feature-row__layer:nth-child(2) { --drift: 2rem; }
:is(.feature-row__media--laptop-plus-tablet, .feature-row__media--laptop-plus-phones) > .feature-row__layer:nth-child(3) { --drift: 3rem; }
.feature-row__media--laptop-plus-phones > .feature-row__layer:nth-child(n + 2) { --layer-elevation: var(--feature-row-elevation-phone); }

.feature-row__media--laptop-pair > .feature-row__layer:nth-child(1) { --layer-blur: blur(0.03rem); --drift: 1rem; }
.feature-row__media--laptop-pair > .feature-row__layer:nth-child(2) { --drift: 3rem; }

.feature-row__media--phone-fan > .feature-row__layer { --layer-elevation: var(--feature-row-elevation-phone); }
.feature-row__media--phone-fan > .feature-row__layer:nth-child(1) { --layer-blur: blur(0.03rem); --drift: 1rem; }
.feature-row__media--phone-fan > .feature-row__layer:nth-child(2) { --layer-blur: blur(0.015rem); --drift: 2rem; }
.feature-row__media--phone-fan > .feature-row__layer:nth-child(3) { --drift: 3rem; }
.feature-row__media--phone-fan:has(> .feature-row__layer:nth-child(4)) > .feature-row__layer:nth-child(2) { --drift: 1.5rem; }
.feature-row__media--phone-fan:has(> .feature-row__layer:nth-child(4)) > .feature-row__layer:nth-child(3) { --layer-blur: blur(0.01rem); --drift: 2.5rem; }
.feature-row__media--phone-fan:has(> .feature-row__layer:nth-child(4)) > .feature-row__layer:nth-child(4) { --drift: 3rem; }

/* ---------- cta ---------- */

/* .hyperlink--cta comes from software. Here it takes motion and the site's
   capitalized arrow CTAs (Mark, 2026-10-28). The plain email link has no
   arrow and stays as typed. */
.hyperlink--cta {
  transition: color var(--motion-duration-sm) ease;
}

.hyperlink--cta:has(> .icon) {
  text-transform: capitalize;
}

.hyperlink--cta .icon {
  transition: translate var(--motion-duration-md) var(--motion-easing-standard);
}

.index-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--button-spacing-actions-gap-y) var(--button-spacing-actions-gap-x);
}

/* ---------- how-i-work ---------- */

.how-i-work {
  row-gap: var(--how-i-work-spacing-row-gap);
  align-items: baseline;
}

.how-i-work__title {
  font-family: var(--typography-headline-default-font-family);
  font-size: var(--typography-headline-default-font-size);
  line-height: var(--typography-headline-default-line-height);
  font-weight: var(--typography-headline-default-font-weight);
  letter-spacing: var(--typography-headline-default-letter-spacing);
  color: var(--how-i-work-color-content-title);
  text-wrap: balance;
}

.how-i-work__body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--how-i-work-size-body-column-min)), 1fr));
  gap: var(--how-i-work-spacing-body-gap) var(--page-spacing-gutter);
  align-items: start;
  color: var(--how-i-work-color-content-body);
}

.how-i-work__body p {
  text-wrap: pretty;
}

.how-i-work__body > .how-i-work__quote,
.how-i-work__body > .index-hero__actions {
  grid-column: 1 / -1;
}

.how-i-work__body > .how-i-work__quote {
  font-family: var(--typography-title-lg-font-family);
  font-size: var(--typography-title-lg-font-size);
  line-height: var(--typography-title-lg-line-height);
  font-weight: var(--typography-headline-lg-font-weight);
  letter-spacing: var(--typography-headline-sm-letter-spacing);
  color: var(--how-i-work-color-content-quote);
  text-wrap: balance;
}

/* ---------- contact ---------- */

/* .contact comes from software. Here it takes the site's title and label type. */
.contact__title {
  font-size: var(--typography-font-size-48);
  line-height: var(--typography-line-height-48);
  font-weight: var(--typography-font-weight-800);
  letter-spacing: var(--typography-headline-sm-letter-spacing);
  text-transform: uppercase;
}

.contact__link-label {
  font-family: var(--typography-eyebrow-default-font-family);
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
  font-weight: var(--typography-eyebrow-default-font-weight);
  letter-spacing: var(--typography-eyebrow-default-letter-spacing);
  text-transform: uppercase;
}

.contact__support,
.contact__links {
  column-gap: var(--page-spacing-gutter);
}

/* ---------- footer ---------- */

/* .footer comes from software. Here it takes the site's eyebrow type. */
.footer .footer__inner {
  font-family: var(--typography-eyebrow-default-font-family);
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
  font-weight: var(--typography-eyebrow-default-font-weight);
  letter-spacing: var(--typography-eyebrow-default-letter-spacing);
  text-transform: uppercase;
}

/* ---------- page-hero ---------- */

/* ---------- content-section ---------- */

/* Blocks stack on the gap. A hidden block takes no gap, so the parked
   "before and after" figure needs no rule of its own. */
/* Readout: four ruled cells, the last one filled with the brand. */
.case-readout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  border-inline-start: var(--case-readout-border-width) solid var(--case-readout-color-border-default);
}

.case-readout__cell {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--case-readout-spacing-gap);
  padding: var(--case-readout-spacing-padding);
  border-inline-end: var(--case-readout-border-width) solid var(--case-readout-color-border-default);
}

.case-readout__cell dt {
  font-family: var(--typography-eyebrow-default-font-family);
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
  font-weight: var(--typography-eyebrow-default-font-weight);
  letter-spacing: var(--typography-eyebrow-default-letter-spacing);
  text-transform: uppercase;
  color: var(--case-readout-color-content-label);
}

.case-readout__cell dd {
  display: flex;
  flex-direction: column;
  gap: var(--case-readout-spacing-desc-margin-top);
  font-family: var(--typography-body-sm-font-family);
  font-size: var(--typography-body-sm-font-size);
  line-height: var(--typography-body-sm-line-height);
  font-weight: var(--typography-body-sm-font-weight);
  letter-spacing: var(--typography-body-sm-letter-spacing);
}

.case-readout__value {
  font-family: var(--typography-numeral-lg-font-family);
  font-size: var(--typography-numeral-lg-font-size);
  line-height: var(--typography-numeral-lg-line-height);
  font-weight: var(--typography-numeral-lg-font-weight);
  letter-spacing: var(--typography-numeral-lg-letter-spacing);
}

.case-readout__cell--brand {
  background: var(--case-readout-color-background-brand);
  color: var(--case-readout-color-content-brand);
}

.case-readout__cell--brand dt {
  color: var(--case-readout-color-content-brand);
}

@media (min-width: 35em) {
  .case-readout{
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 60em) {
  .case-readout {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

}

/* ---------- sheet ----------
   .sheet comes from software. Here it takes motion, the case study width,
   and the page grid overlay. */

.sheet {
  transition:
    transform var(--sheet-motion-duration) var(--sheet-motion-easing),
    display var(--sheet-motion-duration) allow-discrete,
    overlay var(--sheet-motion-duration) allow-discrete;
}

@media (min-width: 48em) {
  .sheet--case {
    inset-inline-start: auto;
    inline-size: min(100% - var(--sheet-spacing-peek), var(--sheet-size-container-max));
  }
}

.sheet--case .page-hero {
  border-block-end: 0;
}

/* The case sheet covers the page grid, so it draws its own copy of the
   overlay, pinned while the body scrolls. */
.sheet--case::before {
  --period: calc((100% + var(--page-spacing-gutter)) / var(--page-grid-modules));
  content: "";
  display: var(--page-grid-display);
  position: absolute;
  inset-block: 0;
  inset-inline: var(--page-spacing-padding-x);
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--page-grid-color-border) 0 var(--page-grid-border-width), transparent var(--page-grid-border-width)),
    linear-gradient(to right, transparent calc(100% - var(--page-spacing-gutter) - var(--page-grid-border-width)), var(--page-grid-color-border) calc(100% - var(--page-spacing-gutter) - var(--page-grid-border-width)) calc(100% - var(--page-spacing-gutter)), transparent calc(100% - var(--page-spacing-gutter)));
  background-size: var(--period) 100%;
  background-repeat: repeat-x;
}

.sheet--case > .sheet__body {
  position: relative;
  z-index: 1;
}

.sheet::backdrop {
  transition:
    opacity var(--sheet-motion-duration) var(--sheet-motion-easing),
    display var(--sheet-motion-duration) allow-discrete,
    overlay var(--sheet-motion-duration) allow-discrete;
}

.sheet__control {
  transition: color var(--sheet-motion-duration-ui) ease;
}

.sheet__back-label {
  transition: transform var(--sheet-motion-duration-ui) ease;
}

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

@media (prefers-reduced-motion: reduce) {
  .sheet,
  .sheet::backdrop {
    transition-duration: 0s;
  }
}

/* ---------- feature-row--next ---------- */

.feature-row--next {
  grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
  column-gap: var(--page-spacing-gutter);
  row-gap: var(--content-section-spacing-row-gap);
  padding-block-start: var(--content-section-spacing-head-padding-top);
}

.feature-row--next .feature-row__body {
  gap: var(--next-spacing-gap);
}

.feature-row--next .feature-row__media {
  padding-block-end: 0;
}

.feature-row--next .feature-row__media > img {
  filter: none;
}

/* Readout numbers match the stat band. */
.case-readout__cell:not(.case-readout__cell--brand) .case-readout__value {
  color: var(--stat-band-color-value-content-default);
}

/* ---------- case study: production template ----------
   bullhorn.html keeps the live page's structure (.page-hero__inner grid,
   .align subgrids, span spacers, .stack--tight figures, .text-caption).
   These rules dress that structure in the index styles. */

/* page-hero comes from software. Here: the page gutter, a flat media
   filter, the fluid display-lg title, the eyebrow band and the lead's
   display weight. */
/* .grid here sets row-gap: 0, so the hero takes its own gap back. */
.page-hero__inner {
  max-inline-size: none;
  padding-inline: var(--page-spacing-padding-x);
  column-gap: var(--page-spacing-gutter);
  row-gap: var(--page-hero-spacing-gap);
}

.page-hero__media {
  filter: var(--feature-row-elevation-flat);
}

/* Captions: monospace, numbered 1.0, 2.0… down the page. */
main {
  counter-reset: site-figure;
}

.text-caption {
  counter-increment: site-figure;
  font-family: var(--typography-font-family-mono);
  font-size: var(--typography-body-sm-font-size);
  line-height: var(--typography-body-sm-line-height);
  font-weight: var(--typography-body-default-font-weight);
  color: var(--content-section-color-content-caption);
  text-wrap: pretty;
}

.text-caption::before {
  content: counter(site-figure) ".0" "\00a0\00a0";
  color: var(--feature-row-color-number-content-default);
}

/* The study's true h1: the one display-lg moment on the site. Sized to
   the hero's width (cqi) so the name fills the row without overflowing.
   Names wrap at spaces. Chrome won't auto-hyphenate a capitalized word,
   so a name too long for one line carries &shy; at its break point.
   From 60em the title ignores them (see below), so a multi-word name
   breaks between words, not inside one. */
.page-hero__title {
  font-family: var(--typography-display-lg-font-family);
  font-size: var(--page-hero-font-size-title);
  line-height: var(--page-hero-line-height-title);
  letter-spacing: var(--typography-display-lg-letter-spacing);
}

@media (min-width: 60em) {
  .page-hero__title {
    hyphens: none;
  }

  /* A single-word name too long for the row at 136px (Groundwork) keeps
     its soft hyphens on wide screens too. */
  .page-hero__title--hyphenate {
    hyphens: manual;
  }
}

.page-hero__summary > .page-hero__lead {
  inline-size: 100%;
  max-inline-size: none;
}

/* Info band: four cells on the four 3-column modules, each hung from a
   default-width ink rule. The study number is the one red mark. */
.page-hero__band {
  column-gap: var(--page-spacing-gutter);
}

/* The hero shows the project's index media, from the same partial as its
   index row (projects/<slug>-media.html): one image, or a device combo
   whose layers are sized as percentages of this box. The images carry
   their own elevation, so the wrapper doesn't add a second shadow, and
   the row's clearance under the media doesn't apply here. */
.page-hero__media.feature-row__media {
  padding-block-end: 0;
  filter: none;
  /* The hero drifts like the index rows (Mark, 2026-10-28), at half the
     travel. Its text sits above the media, 96px away, and the deepest
     layers rise 2 × 3rem on the index, so Proof's touched its specs on
     load. One times the drift keeps the depth ratios and a 48px floor. */
  --drift-travel: -1;
}

/* From 60em the brief column splits in half on the page grid (one
   gutter between halves = columns 7–9 and 10–12). */
@media (min-width: 60em) {
  .page-hero__specs {
    column-gap: var(--page-spacing-gutter);
  }
}

/* Medium screens: production's spans assume a 12-column grid that pairs
   narrow spans into halves and widens the rest. software's grid gives 8
   columns here, so the case study maps spans the same way production does. */
@media (min-width: 35em) and (max-width: 59.9375em) {

  :is(.page-hero__inner, .content-section) :is(.span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11){
    grid-column: 1 / -1;
  }

  .page-hero__media {
    grid-column: 1 / -1;
  }
}

/* Case study h2: uppercase, 40px, weight 800. Visually hidden h2s are
   unaffected. */

/* ---------- case-flow: study sections as index cards ----------
   Each h2 section is built like a .feature-row: media, head (h2 + support),
   body (subsections, figures, notes). From 60em the sections alternate
   right, left, right… on six columns, each on three subgrid rows of the
   flow and starting on the previous section's body row, so every
   section's media sits beside its neighbour's body, as on the index. */

.case-flow {
  row-gap: var(--case-flow-spacing-row-gap);
  align-items: start;
  margin-block-start: var(--content-section-spacing-rule);
  position: relative;
}

/* Every visible h2 block opens on a 1px ink rule (full page width, see
   below), whatever columns the section sits in. Equal space above
   (to the image or hero before it) and below (to the h2). The rule is
   positioned against .case-flow, so nothing between them is positioned. */
/* Every section reads copy first at every width: head (h2 + support),
   then media, then body, whatever the markup order. From 60em the
   desktop block below restates this for its flex layout. */
.content-section--card > .content-section__head {
  order: -1;
}

/* The rule above each h2 runs the full width of the page, or of the
   case sheet when the study opens there (Mark, 2026-10-29): it reaches
   from the centered grid out to the edges of the nearest page-width
   container, main or the sheet body. 100cqi leaves out the scrollbar,
   where 100vw would not. */
:is(main, .sheet__body) {
  container-type: inline-size;
}

/* The rule above each heading is a full-bleed page line here, drawn by
   ::before, so the head's own top border is off. */
.content-section__head {
  padding-block-start: var(--content-section-spacing-head-padding-y);
  border-block-start: 0;
}

/* A heading hidden for assistive tech stays hidden: the shared heading
   rule outranks the utility. */
.content-section__head > h2.visually-hidden {
  display: block;
  margin: -1px;
  padding: 0;
  border: 0;
}

.content-section__head:has(> h2:not(.visually-hidden))::before {
  content: "";
  position: absolute;
  inset-inline: calc(50% - 50cqi);
  block-size: var(--content-section-border-width-rule);
  background: var(--content-section-color-border-rule);
  translate: 0 calc(var(--content-section-spacing-rule) * -1);
}

/* A second rule closes the h2, inside the grid, with the same space
   above it as the top rule has below it, and the same again below it
   (Mark, 2026-10-29): the head's gap to the support copy is the rule
   spacing, and a head with no support copy pads out what the section's
   row gap is short of it. */
/* Padding, not margin: the desktop card resets its children's block
   margins. */
.content-section__head:has(> h2:not(.visually-hidden)):not(:has(> .content-section__support)) {
  padding-block-end: calc(var(--content-section-spacing-rule) - var(--case-flow-spacing-row-gap));
}

.case-flow > .content-section--card {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: var(--case-flow-spacing-row-gap);
  align-items: start;
  padding-block-start: 0;
}

/* A head whose heading is hidden has no rules, so it sits closer. */
.content-section__head:has(> h2.visually-hidden) {
  padding-block-start: var(--content-section-spacing-head-padding-top);
  gap: var(--content-section-spacing-lead-row-gap);
}

:is(.content-section__support, .content-section__part, .content-section__notes) {
  column-gap: var(--page-spacing-gutter);
}

/* Media and body stack their blocks (parts, figures, notes) on the same
   48px rhythm below 60em; from 60em both become 12-column grids with the
   same row gap. */
/* Figure captions at the meta size (12/16). */
figcaption.text-caption {
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
}

/* The first section's screen pair stacks vertically. */
.case-flow > .content-section--card:first-child .media-frame {
  grid-template-columns: minmax(0, 1fr);
}

/* With the arrow, the optical offset moves to the text (text-indent) so
   the rule starts on the grid line like the band rules above, and the
   size steps down so the name and arrow share the row. */
.page-hero__title:has(.page-hero__title-icon) {
  text-indent: var(--page-hero-spacing-title-optical);
  font-size: calc(var(--page-hero-font-size-title) * var(--page-hero-scale-title-with-icon));
  /* Snap to the 8px scale (136px at the 160px display size). */
  font-size: round(down, calc(var(--page-hero-font-size-title) * var(--page-hero-scale-title-with-icon)), var(--spacing-8));
}

/* Results: heading led by a hard-edged down-right arrow hung in the
   margin (8px gap, Mark 2026-10-28; 16px clipped the arrow at the case
   sheet's edge on smaller screens) so the text starts on the grid line;
   arrow sits on the text baseline. Body text below. */
@media (min-width: 60em) {
  .case-flow > .content-section--card {
    grid-template-rows: subgrid;
  }

  /* Text blocks inside a six-column section split into two three-column
     tracks with one gutter between, so every paragraph sits on the page
     grid lines. */
  .content-section--card .content-section__support,
  .content-section__part,
  .content-section__notes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--page-spacing-gutter);
  }

  .case-flow > .content-section--card:nth-child(odd) {
    grid-column: 7 / span 6;
  }

  .case-flow > .content-section--card:nth-child(even) {
    grid-column: 1 / span 6;
  }

  /* Media hugs the bottom of its row, one clearance step above its own
     h2, mirroring the index cards. */
  .content-section--card > .content-section__media {
    align-self: end;
    margin-block-end: calc(var(--content-section-spacing-rule) - var(--case-flow-spacing-row-gap));
  }

  /* Sections without media drop the subgrid: head and body stack from the
     top of their slot, beside the neighbour's body, with no dead column. */
  .content-section--card:not(:has(> .content-section__media > :not([hidden]))) {
    grid-template-rows: none;
    align-content: start;
  }

  /* First section opens the flow across the full width: head on the
     left, its screens stacked in the left half starting one row lower,
     beside the body. Later sections shift down one row to clear it. */
  .case-flow > .content-section--card:nth-child(1) {
    grid-column: 1 / -1;
    grid-row: 1 / span 3;
    grid-template-columns: subgrid;
  }

  .case-flow > .content-section--card:nth-child(1) > .content-section__head {
    grid-column: 4 / span 6;
    grid-row: 1;
  }

  .case-flow > .content-section--card:nth-child(1) > .content-section__media {
    grid-column: 1 / -1;
    grid-row: 2;
    align-self: start;
    margin-block-end: 0;
  }

  /* First section's copy is centred in columns 4–9 (three empty columns
     each side); the screens run full width. Subsections: h3 in 4–9,
     paragraphs in 4–6 and 7–9. */
  .case-flow > .content-section--card:nth-child(1) > .content-section__body {
    grid-column: 1 / -1;
    grid-row: 3;
  }

  .case-flow > .content-section--card:nth-child(1) .content-section__part {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .case-flow > .content-section--card:nth-child(1) .content-section__part > h3 { grid-column: 4 / span 6; }
  .case-flow > .content-section--card:nth-child(1) .content-section__part > p:nth-of-type(1) { grid-column: 4 / span 3; }
  .case-flow > .content-section--card:nth-child(1) .content-section__part > p:nth-of-type(2) { grid-column: 7 / span 3; }

  /* Study image treatment: every figure sits on columns 4–9, the frame
     pushed out one gutter each side onto the neighbouring column lines.
     Sections with figures span the full grid; their media and body become
     12-column grids so text keeps its own columns. */
  .case-flow > .content-section--card:nth-child(n) {
    grid-column: 1 / -1;
  }

  .case-flow > .content-section--card:nth-child(n) > :is(.content-section__media, .content-section__body) {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--page-spacing-gutter);
    row-gap: var(--case-flow-spacing-row-gap);
    align-content: start;
  }

  .case-flow > .content-section--card > :is(.content-section__media, .content-section__body) > figure {
    grid-column: 4 / span 6;
  }

  .case-flow > .content-section--card > :is(.content-section__media, .content-section__body) > figure[hidden] {
    display: none;
  }

  .case-flow > .content-section--card figure > .media-frame {
    margin-inline: calc(-1 * var(--page-spacing-gutter));
    inline-size: calc(100% + 2 * var(--page-spacing-gutter));
    max-inline-size: none;
    justify-self: start;
  }

  /* Every section follows section 1's pattern: h2 on columns 1–6, all
     other copy (support, subsections, notes) on columns 4–9. */
  .case-flow > .content-section--card > :is(.content-section__media, .content-section__body) > :not(figure),
  .case-flow > .content-section--card > .content-section__head > :not(h2) {
    grid-column: 4 / span 6;
  }

  .case-flow > .content-section--card:nth-child(n) .content-section__part {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .case-flow > .content-section--card:nth-child(n) .content-section__part > h3 { grid-column: 1 / -1; }
  .case-flow > .content-section--card:nth-child(n) .content-section__part > p:nth-of-type(n) { grid-column: auto; }

  /* Every h2 starts on column 1 at six columns wide; the rest of the head
     keeps its slot. */
  .case-flow > .content-section--card:nth-child(n) > .content-section__head {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--page-spacing-gutter);
    row-gap: var(--content-section-spacing-rule);
    align-content: start;
  }

  /* The h2 spans the grid so its closing rule does; the text keeps six
     columns. The end padding is half the grid plus half a gutter, which
     leaves exactly columns 1-6 for the text. */
  .case-flow > .content-section--card > .content-section__head > h2 {
    grid-column: 1 / -1;
    padding-inline-end: calc(50% + var(--page-spacing-gutter) / 2);
  }

  /* With an arrow, the h2 spans the whole grid as a subgrid: the text
     keeps columns 1-6 and the arrow sits on the grid's end, like the hero
     title's, so every section's arrow lands on the same line (Mark,
     2026-10-29). normal gap = the head grid's gutter. */
  .case-flow > .content-section--card > .content-section__head > h2:has(> .content-section__title-icon) {
    grid-column: 1 / -1;
    padding-inline-end: 0;
    display: grid;
    grid-template-columns: subgrid;
    gap: normal;
    align-items: last baseline;
  }

  .case-flow .content-section__head > h2 > .content-section__title-text {
    grid-column: 1 / span 6;
    grid-row: 1;
  }

  /* The arrow's area is the half the text doesn't use. At 1024 the
     arrow (1cap of 48px) is wider than one column, so a single-column
     area pushed it past the grid end. */
  .case-flow .content-section__head > h2 > .content-section__title-icon {
    grid-column: 7 / -1;
    grid-row: 1;
    justify-self: end;
  }

  /* At nine columns the legacy pair sits side by side. */
  .case-flow > .content-section--card:nth-child(1) .media-frame:has(> img + img) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* Second section runs the full grid: its opener stacks down the centre
     (image, then "Atomic elements…", both in columns 4–9); head and body stay in 1–6 so the
     next section bricks in beside the body. */
  .case-flow > .content-section--card:nth-child(2) {
    grid-row: 4 / span 3;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
  }

  .case-flow > .content-section--card:nth-child(2) > .content-section__head,
  .case-flow > .content-section--card:nth-child(2) > .content-section__body {
    grid-column: 1 / span 6;
  }

  /* Every section follows section 1's flow: head (h2 + support), then
     media, then body, stacked in its own row so nothing overlaps. */
  .case-flow > .content-section.content-section--card:nth-child(n) {
    grid-row: auto;
    display: flex;
    flex-direction: column;
    gap: var(--case-flow-spacing-row-gap);
  }

  .case-flow > .content-section.content-section--card:nth-child(n) > .content-section__head { order: -1; }

  .case-flow > .content-section.content-section--card:nth-child(n) > .content-section__media:not(:has(> :not([hidden]))),
  .case-flow > .content-section.content-section--card:nth-child(n) > .content-section__body:not(:has(> *)) {
    display: none;
  }

  .case-flow > .content-section.content-section--card:nth-child(n) > * {
    align-self: stretch;
    margin-block: 0;
  }
  .case-flow > .content-section--card:nth-child(4) { grid-row: 8 / span 3; }
  .case-flow > .content-section--card:nth-child(5) { grid-row: 10 / span 3; }
  .case-flow > .content-section--card:nth-child(6) { grid-row: 12 / span 3; }
  .case-flow > .content-section--card:nth-child(7) { grid-row: 14 / span 3; }
  .case-flow > .content-section--card:nth-child(8) { grid-row: 16 / span 3; }
}

/* Case study h3: bold (the headline-sm weight, 700). Matches the
   specificity of the shared h3/h4 rule so it wins by order. */
/* ---------- Motion (CSS only) ---------- */

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Header surface fades in over the first stretch of scroll. */
    .navbar {
      animation: site-nav-surface linear both;
      animation-timeline: scroll(root);
      animation-range: 0 var(--site-nav-motion-range);
    }

    /* Wordmark letters drift apart as the page scrolls, each on its own
       rate and direction, staggered in reading order. */
    .index-hero__letter {
      animation: site-letter-drift var(--motion-easing-out) both;
      animation-timeline: scroll(root);
      animation-range:
        calc(var(--i) * var(--wordmark-motion-stagger))
        calc(var(--i) * var(--wordmark-motion-stagger) + var(--wordmark-motion-length));
    }

    /* Stat rules draw down, labels fade in, digits spin to their value. */
    .stat::before {
      animation: site-stat-rule linear both;
      animation-timeline: scroll(root);
      animation-range:
        calc(var(--i) * var(--stat-band-motion-stagger))
        calc(var(--i) * var(--stat-band-motion-stagger) + var(--stat-band-motion-length));
    }

    .stat__label {
      animation: site-stat-label linear both;
      animation-timeline: scroll(root);
      animation-range:
        calc(var(--i) * var(--stat-band-motion-stagger))
        calc(var(--i) * var(--stat-band-motion-stagger) + var(--stat-band-motion-length));
    }

    .stat__digit::before {
      animation: site-stat-spin cubic-bezier(0.33, 1, 0.68, 1) both;
      animation-timeline: scroll(root);
      animation-range:
        calc(var(--i) * var(--stat-band-motion-stagger))
        calc(var(--i) * var(--stat-band-motion-stagger) + var(--stat-band-motion-length));
    }

    /* Phones: the stats are tall (wrapped labels and meta) and the band
       sits just under the fold, so on the page timeline the rules drew
       below the screen and looked static. Each stat runs on its own
       view timeline instead: everything plays from the moment its top
       enters until its middle reaches the middle of the screen, so the
       rule's tip climbs into view. */
    @media (max-width: 34.9375em) {
      .stat {
        view-timeline: --stat block;
      }

      .stat::before,
      .stat__label,
      .stat__digit::before {
        animation-timeline: --stat;
        animation-range: cover 0% cover 50%;
      }
    }

    /* Case-study images drift down toward their text as they cross the
       viewport; device layers drift by depth. */
    .feature-row__media > img {
      animation: site-media-drift linear both;
      animation-timeline: view();
      animation-range: cover 0% exit 0%;
      will-change: translate;
    }
  }
}

@keyframes site-nav-surface {
  from {
    background-color: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-block-end-color: transparent;
  }
}

@keyframes site-letter-drift {
  to {
    translate: 0 calc(var(--dir) * var(--k) * var(--wordmark-motion-amplitude));
  }
}

@keyframes site-stat-rule {
  from {
    scale: 1 0;
  }
}

@keyframes site-stat-label {
  0%,
  60% {
    opacity: 0;
  }
}

@keyframes site-stat-spin {
  from {
    translate: 0 calc((var(--d) + var(--spin)) * -1em);
  }
}

/* Images start high and settle at their resting position (1rem above the
   headline) as the card's top leaves the viewport, so the drift never
   brings them closer than --feature-row-spacing-media-clearance. */
@keyframes site-media-drift {
  from {
    translate: 0 calc(var(--drift) * var(--drift-travel, -2));
  }
  to {
    translate: 0 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  :is(.hyperlink--cta, .feature-row__title) .icon,
  .hyperlink--cta {
    transition-duration: 0s;
  }
}

/* Static media: no scroll drift (used for the next-study card). */
.feature-row__media--static > img {
  animation: none;
  translate: none;
}

/* Study motion: each row rises 24px and fades in as it enters the
   viewport, driven by scroll (no JS). Rows are the hero blocks, each
   section's head, figure and subsection, and each results note. */
@keyframes site-row-rise {
  from { translate: 0 var(--spacing-24); }
  to { translate: 0 0; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .page-hero__heading > *,
    .page-hero__summary > *,
    .page-hero__inner > .page-hero__media,
    .case-flow .content-section__head > *,
    .case-flow :is(.content-section__media, .content-section__body) > :not(.content-section__notes),
    .case-flow .content-section__notes > * {
      animation: site-row-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}

/* Work index lead: an h2 in markup, styled for the index only, at the
   display-sm role, the h1's (56/64, weight 200). Restates the .feature-list__lead
   type so global h2 styles don't leak in. */
.feature-list__header > h2.feature-list__lead {
  margin-block-start: var(--feature-list-spacing-lead-gap);
  font-family: var(--typography-display-sm-font-family);
  font-size: var(--typography-display-sm-font-size);
  line-height: var(--typography-display-sm-line-height);
  font-weight: var(--typography-display-sm-font-weight);
  letter-spacing: var(--typography-display-sm-letter-spacing);
  text-transform: none;
  color: var(--feature-list-color-content-lead);
  text-wrap: balance;
}

/* Index headings below 60em: the fixed 48–56px desktop sizes overrun a
   phone, so all three step with the screen on headline-lg (32/40 small,
   40/48 medium, the same 56/64 the h1 and lead use on desktop). Each
   keeps its desktop weight at every width (Mark, 2026-10-28). Desktop
   is unchanged. */
@media (max-width: 59.9375em) {
  .index-hero__title,
  .feature-list__header > h2.feature-list__lead,
  .contact__title {
    font-size: var(--typography-headline-lg-font-size);
    line-height: var(--typography-headline-lg-line-height);
  }

  /* Full width, so the h1 stacks under the eyebrow as on phones and the
     lead doesn't run 6–8 lines in half the grid. */
  .index-hero__statement,
  .feature-list__header > h2.feature-list__lead {
    grid-column: 1 / -1;
  }
}

/* Tablet h1: 40/48 left the portrait screen bare (Mark, 2026-10-28), so
   the h1 alone steps up to 64/72 between 35em and 60em. text-wrap:
   balance sets the breaks: two lines from 768px, three near 600px. */
@media (min-width: 35em) and (max-width: 59.9375em) {
  .index-hero__title {
    font-size: var(--typography-font-size-64);
    line-height: var(--typography-line-height-72);
  }
}

/* Phone h1: 48/56 (Mark, 2026-10-31). */
@media (max-width: 34.9375em) {
  .index-hero__title {
    font-size: var(--typography-font-size-48);
    line-height: var(--typography-line-height-56);
  }
}

.contact__support > .index-hero__actions {
  align-items: first baseline;
}

/* The plain email link sits flat beside its paragraph. Arrow CTAs keep
   the standard CTA padding so they match "View case study" exactly. */
.contact__support > .index-hero__actions > .hyperlink--cta:not(:has(> .icon)) {
  padding-block: 0;
}

/* A stacked pair of CTAs sits in the support grid's right column, under
   the second paragraph (Mark, 2026-10-29). On phones that's the only
   column. */
.contact__support > .contact__actions {
  grid-column: -2 / -1;
  flex-direction: column;
  align-items: flex-start;
}

/* ---------- nav-panel (phone menu) ----------
   Not in the handoff; built from these patterns. The panel sits inside
   the sticky header and opens from its bottom edge (top: 100%), so it
   never needs the header's height. Links use the case h2 style
   (uppercase, 800) at headline-lg size; the switches sit below. */
.sheet--nav {
  position: absolute;
  /* Below the header's bottom border, which top: 100% would cover. */
  inset-block: calc(100% + var(--navbar-border-width-default)) auto;
  inset-inline: 0;
  block-size: 100dvh;
  border-radius: 0;
  transform: translateY(-100%);
  opacity: 0;
  transition:
    transform var(--sheet-motion-duration) var(--sheet-motion-easing),
    opacity var(--sheet-motion-duration) var(--sheet-motion-easing),
    display var(--sheet-motion-duration) allow-discrete,
    overlay var(--sheet-motion-duration) allow-discrete;
}

.sheet--nav[open] {
  transform: none;
  opacity: 1;
}

@starting-style {
  .sheet--nav[open] {
    transform: translateY(-100%);
    opacity: 0;
  }
}

.nav-panel {
  display: flex;
  flex-direction: column;
  padding: var(--nav-panel-spacing-padding-top) var(--nav-panel-spacing-padding-x);
}

.nav-panel__links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--nav-panel-spacing-links-gap);
}

.nav-panel__links a {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-8);
  font-family: var(--typography-headline-sm-font-family);
  font-size: var(--typography-headline-lg-font-size);
  line-height: var(--typography-headline-lg-line-height);
  font-weight: var(--typography-font-weight-800);
  letter-spacing: var(--typography-headline-sm-letter-spacing);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--nav-panel-color-content-link);
  transition: color var(--motion-duration-sm) ease;
}

.nav-panel__links a:is(:hover, :focus-visible) {
  color: var(--nav-panel-color-content-link-hover);
}

.nav-panel__switches {
  display: flex;
  gap: var(--nav-panel-spacing-switches-gap);
  margin-block-start: var(--nav-panel-spacing-switches-margin-top);
}

/* .switch is hidden on phones in the header; in the panel it shows. */
.nav-panel__switches .switch {
  display: flex;
}

/* Open: the hamburger becomes the X that closes the panel. Outer bars
   meet on the middle one (one gap plus one bar) and turn 45°. */
.navbar__toggle span {
  transition:
    transform var(--motion-duration-sm) var(--motion-easing-standard),
    opacity var(--motion-duration-sm) var(--motion-easing-standard);
}

.navbar__toggle[aria-expanded="true"] span:first-child {
  transform: translateY(calc(var(--site-nav-spacing-toggle-bar-gap) + var(--site-nav-size-toggle-bar-thickness))) rotate(45deg);
}

.navbar__toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.navbar__toggle[aria-expanded="true"] span:last-child {
  transform: translateY(calc(-1 * (var(--site-nav-spacing-toggle-bar-gap) + var(--site-nav-size-toggle-bar-thickness)))) rotate(-45deg);
}

/* From 35em the header links show and the toggle hides, so the panel
   never stays open there (nav.js also closes it on the way past). */
@media (min-width: 35em) {
  .sheet--nav[open] {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sheet--nav,
  .navbar__toggle span {
    transition: none;
  }
}

/* X-ray view (html.xray, toggled by the Code switch via xray.js).
   Production's behaviour (utilities/xray.css) in this design's language:
   solid 1px outlines, square corners, labels set in the 12/16 eyebrow
   on a solid layer colour. Layer colours: atom brand red, molecule
   brand blue, organism ink, template subtle grey. */
:root {
  --xray-color-atom: var(--color-background-brand);
  --xray-color-molecule: var(--color-content-brand-secondary, #45b5dd);
  --xray-color-organism: var(--color-content-headline);
  --xray-color-template: var(--color-content-subtle);
}

:where(html.xray [data-xray]) {
  position: relative;
}

html.xray [data-xray] {
  outline: var(--page-grid-border-width) solid var(--_xray, var(--xray-color-organism));
  outline-offset: calc(-1 * var(--page-grid-border-width));
}

html.xray [data-xray]::before {
  content: attr(data-xray);
  position: absolute;
  /* Labels sit outside their element (on the top edge, outward) so they
     never cover content or controls; clicks pass through. */
  inset-block-end: 100%;
  inset-inline-start: 0;
  z-index: 20;
  padding: 0 var(--spacing-4);
  background: var(--_xray, var(--xray-color-organism));
  color: var(--color-background-default);
  font-family: var(--typography-eyebrow-default-font-family);
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
  font-weight: var(--typography-eyebrow-default-font-weight);
  letter-spacing: var(--typography-eyebrow-default-letter-spacing);
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  animation: none;
  translate: none;
}

/* Templates and organisms label inside their own top-right corner;
   atoms and molecules label outside, above. No two share an edge. */
html.xray [data-xray^="template"]::before,
html.xray [data-xray^="organism"]::before {
  inset-block: 0 auto;
  inset-inline: auto 0;
}

/* At the top of the viewport, the nav's parts label below; the nav's
   own label sits inside its bottom edge, centred under the links. */
html.xray .navbar [data-xray]::before {
  inset-block: 100% auto;
}

html.xray .navbar[data-xray]::before {
  inset-block: auto 0;
  inset-inline: 50% auto;
  translate: -50% 0;
}

/* The two switches sit close together: anchor their labels to the
   right edge and stack the second one a line lower. */
html.xray .navbar__switches > [data-xray]::before {
  inset-inline: auto 0;
}

html.xray .navbar__switches > [data-xray] + [data-xray]::before {
  inset-block-start: calc(100% + var(--typography-eyebrow-default-line-height));
}

html.xray [data-xray^="atom"] { --_xray: var(--xray-color-atom); }
html.xray [data-xray^="molecule"] { --_xray: var(--xray-color-molecule); }
html.xray [data-xray^="organism"] { --_xray: var(--xray-color-organism); }
html.xray [data-xray^="template"] { --_xray: var(--xray-color-template); }

.xray-legend {
  display: none;
}

html.xray .xray-legend {
  position: fixed;
  inset-block-end: var(--spacing-24);
  inset-inline-start: var(--page-spacing-padding-x);
  z-index: 30;
  display: flex;
  gap: var(--spacing-16);
  padding: var(--spacing-8) var(--spacing-12);
  background: var(--color-background-knockout);
  color: var(--color-content-knockout);
  font-family: var(--typography-eyebrow-default-font-family);
  font-size: var(--typography-eyebrow-default-font-size);
  line-height: var(--typography-eyebrow-default-line-height);
  font-weight: var(--typography-eyebrow-default-font-weight);
  letter-spacing: var(--typography-eyebrow-default-letter-spacing);
  text-transform: uppercase;
}

.xray-legend span {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-8);
}

.xray-legend span::before {
  content: "";
  inline-size: var(--spacing-8);
  aspect-ratio: 1;
  background: var(--_dot);
}

.xray-legend .legend-atom { --_dot: var(--xray-color-atom); }
.xray-legend .legend-molecule { --_dot: var(--xray-color-molecule); }
.xray-legend .legend-organism { --_dot: var(--color-content-knockout); }
.xray-legend .legend-template { --_dot: var(--xray-color-template); }

/* In-page navigation scrolls smoothly (CSS only). Anchored sections
   stop below the sticky header. Reduced motion keeps the instant jump. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

:is(section, [id]) {
  scroll-margin-block-start: var(--spacing-96, 6rem);
}

/* Case sheet header: same surface as the content (transparent over the
   sheet). The grid overlay starts at the header's bottom edge via anchor
   positioning, so the lines still stop under it. Browsers without anchor
   positioning keep the opaque header from earlier. */
@supports (anchor-name: --sheet-chrome) {
  .sheet--case > .sheet__chrome {
    anchor-name: --sheet-chrome;
    background: transparent;
  }

  .sheet--case::before {
    inset-block-start: anchor(--sheet-chrome bottom);
  }
}

/* Case sheet: room under the last block so the next-study CTA never
   sits on the bottom edge of the screen. */
.sheet--case .sheet__body > .container {
  padding-block-end: var(--spacing-120);
}
