/* ---- tokens.css ---- */

/* ==========================================================================
   Design tokens: single source of truth for nd.
   Every colour, font, size and space used in the site traces back to a
   variable here. Palette and type come from Nadia's design direction
   (2026-09-20). Colours are plain hex so tests/test_tokens.py can check
   contrast. Breakpoints (media queries can't use variables): 40rem, 48rem, 64rem.
   ========================================================================== */

:root {
  /* ---- Palette (the brief) ---------------------------------------------
     THE SITE IS DARK (her decision, 2026-09-21): the page sits on the same
     tone the home hero always used, and everything that used to be dark is
     now light. Two page types keep the original light palette and opt in with
     data-theme="light" -- case studies and blog posts (see below).

     Token ROLES are unchanged, only their values flipped, so --bg still means
     "page background" and --text still means "body copy". Each tone keeps the
     contrast its light-mode counterpart had, so the ratios below still hold.
     Both neutrals stay intentionally tinted: never the pure extremes.
     - text/primary: AAA on bg (16.5:1 and 13.5:1).
     - secondary: borders, dividers ONLY. 2.7:1 on bg, never text.
     - fill: the one lifted surface -- cards, image placeholders, the contact
       band. A deliberately faint 1.2:1 step off the page; those panels also
       carry a --secondary hairline, which is what actually delineates them.
     - accent: the one colour, and it IS a text colour now (her call): section
       label chips, About area names, case-study meta terms and inline links
       in long-form copy all carry it. 8.9:1 on the dark page, 7.2:1 on
       --fill. The separate "highlight" blue it used to share this job
       with has been retired.
       On the LIGHT pages accent is redeclared as #92afcc (her pick,
       2026-10-07, chosen over a readable darker shade): 2.2:1 on #faf9f7, so
       accent text on light pages does NOT meet AA. See the block below. */
  --bg:         #1b1a18; /* page background */
  --text:       #faf9f7; /* body copy */
  --primary:    #e3e3e2; /* H1/H2, nav, primary buttons, light surfaces */
  --secondary:  #5f5e54; /* borders, dividers */
  --accent:     #9cbce1; /* labels, links, detail -- text as well as fills */

  /* ---- Derived tones (readable muted text + the lifted surface) ---------
     Recipes are checked by tests/test_tokens.py.                           */
  --muted:             #b7b6b4; /* text 70% over bg: 8.6:1 on bg, 7.0:1 on fill */
  --muted-on-primary:  #575655; /* bg 70% over primary: 5.7:1 ON a primary surface */
  --fill:              #2c2b27; /* secondary 25% over bg: cards, placeholders, band */

  /* ---- Type ------------------------------------------------------------
     (see data-theme="light" below for the pages that keep the light palette)
     Syne: H1 and H2 ONLY. Instrument Sans: everything else.               */
  --font-display: "Syne", "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body:    "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --weight-display: 600;
  --weight-heading: 600;

  /* Fluid scale, clamp(mobile 375px -> desktop 1440px) */
  --step--1: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
  --step-0:  clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --step-1:  clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
  --step-2:  clamp(1.625rem, 1.4rem + 1.1vw, 2.25rem);
  --step-3:  clamp(2.125rem, 1.7rem + 2.1vw, 3.5rem);
  --step-4:  clamp(2.2rem, 1.5rem + 3.4vw, 4.4rem); /* H1 only (20% smaller than the first draft) */

  /* ---- Space ----------------------------------------------------------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;
  --space-9: 6rem;

  /* ---- Layout ----------------------------------------------------------
     Container tiers match the other studio site's, so both hold their content
     at the same width: a percentage cap (so it keeps scaling on wide
     screens instead of freezing at a fixed px) plus a few px of inner padding.
     Tiers: mobile / tablet 640px / desktop 1024px.
     --page-inset is the same edge expressed as a distance from the viewport
     edge ((100% - cap) / 2 + gutter), for full-bleed sections and fixed
     panels that have to line up with .container without being inside one. */
  --container-max: 94%;
  --gutter:        5px;
  --page-inset:    calc(3vw + 5px);
  --measure:       38rem;
}

@media (min-width: 40rem) {
  :root {
    --container-max: 90%;
    --gutter:        8px;
    --page-inset:    calc(5vw + 8px);
  }
}

@media (min-width: 64rem) {
  :root {
    --container-max: 85%;
    --gutter:        12px;
    --page-inset:    calc(7.5vw + 12px);
  }
}

/* ==========================================================================
   Light pages: case studies and blog posts.
   Her call (2026-09-21): those two page types are NOT inverted. A case study
   shows a client's own brand -- Safer Gambling Strategies runs on the client's
   navy #0a1735, which is 1.02:1 against the dark page and would simply vanish
   -- and long-form posts read better on light. Only the palette is redeclared;
   every role, component and rule stays exactly as it is, so nothing else in
   the stylesheet needs to know which theme it is in.
   build.py sets data-theme="light" on <html> for these pages (PAGES: light=True).
   ========================================================================== */
[data-theme="light"] {
  --bg:         #faf9f7;
  --text:       #1b1a18;
  --primary:    #2c2b27;
  --secondary:  #aba79a;
  --accent:     #92afcc; /* her pick (2026-10-07), knowingly pale: 2.2:1 here, below AA for text */

  --muted:             #5e5d5b;
  --muted-on-primary:  #bcbbb9;
  --fill:              #e6e5e0;
}

/* Visitor chose Dark (accessibility settings) on a page built light, or a
   section that must stay dark inside a light-pinned area. Same values as
   :root -- test_tokens checks they never drift apart. */
[data-theme="dark"] {
  --bg:         #1b1a18;
  --text:       #faf9f7;
  --primary:    #e3e3e2;
  --secondary:  #5f5e54;
  --accent:     #9cbce1;

  --muted:             #b7b6b4;
  --muted-on-primary:  #575655;
  --fill:              #2c2b27;
}

/* ==========================================================================
   High contrast (accessibility settings). Muted text goes to full strength,
   borders become readable, and the brand blue shifts (lighter on dark pages,
   deeper on light ones) so every text token reaches 7:1 (AAA).
   test_tokens HighContrastTests reads these exact selectors.
   ========================================================================== */
[data-contrast="high"] {
  --muted:     #faf9f7;
  --secondary: #a3a29c;
  --accent:    #c4d9f2;
}
[data-contrast="high"][data-theme="light"] {
  --muted:     #1b1a18;
  --secondary: #6b6a63;
  --accent:    #1f4675;
}
/* a light-pinned area inside a high-contrast page (same values) */
[data-contrast="high"] [data-theme="light"] {
  --muted:     #1b1a18;
  --secondary: #6b6a63;
  --accent:    #1f4675;
}

/* ==========================================================================
   Colour vision (accessibility settings). Only the site's own accent moves;
   the neutrals are already safe for every type. Client colours never change
   (they don't use these tokens). Declared after high contrast, so a chosen
   colour-vision accent wins when both are on (each still meets AA).
   - rg (red-green): a more saturated blue that never reads grey
   - by (blue-yellow): tritan vision sees blue as grey-teal; a cyan-blue
     stays vivid for it and is the nearest hue to the site blue (was rose)
   - grey: no hue at all
   ========================================================================== */
[data-cv="rg"] {
  --accent: #a8c4ff;
}
[data-cv="rg"][data-theme="light"] {
  --accent: #2f5fb3;
}
[data-cv="by"] {
  --accent: #56cfe1;
}
[data-cv="by"][data-theme="light"] {
  --accent: #0c6b86;
}
[data-cv="grey"] {
  --accent: #c9c8c6;
}
[data-cv="grey"][data-theme="light"] {
  --accent: #5e5d5b;
}


/* ---- fonts.css ---- */

/* Self-hosted variable fonts, Latin subset, SIL OFL 1.1 (via Fontsource).
   Paths are relative to /styles/site.css. */

@font-face {
  font-family: "Syne";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../assets/fonts/Syne-Variable.woff2") format("woff2-variations"),
       url("../assets/fonts/Syne-Variable.woff2") format("woff2");
}

@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/InstrumentSans-Variable.woff2") format("woff2-variations"),
       url("../assets/fonts/InstrumentSans-Variable.woff2") format("woff2");
}

/* Google Sans Flex: NOT a general site typeface (Syne stays H1/H2, Instrument
   Sans stays body text everywhere else) -- self-hosted for one decorative
   spot only, the big testimonial quote mark, where its soft, rounded curl
   reads less sharp than the site's own sans (2026-09-24, her call; replaced
   Playfair Display, tried first). */
@font-face {
  font-family: "Google Sans Flex";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/site/GoogleSansFlex-SemiBold.woff2") format("woff2");
}

/* ---- Client-brand fonts (case-study type specimens only) ---------------
   NOT part of this site's own type system: the site stays Syne + Instrument
   Sans. These are a client's typefaces, self-hosted (SIL OFL) so a case
   study's type specimen renders in the real fonts. @font-face alone does not
   download anything, so pages that never use them never fetch the files. */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../assets/fonts/client/PlusJakartaSans-ExtraBold.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/Inter-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Marcellus";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/Marcellus-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Mulish";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/Mulish-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Orbitron";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/Orbitron-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/Onest-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Onest";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/Onest-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "LINE Seed Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/LINESeedSans-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "LINE Seed Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/LINESeedSans-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant Infant";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/CormorantInfant-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant Infant";
  font-style: italic;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/CormorantInfant-SemiBoldItalic.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/SpaceGrotesk-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Pretendard";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../assets/fonts/client/Pretendard-Light.woff2") format("woff2");
}
@font-face {
  font-family: "Pretendard";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/Pretendard-SemiBold.woff2") format("woff2");
}
/* e-Ukraine (Chervona Ruta case study). (c) Ministry of Digital Transformation of Ukraine,
   CC BY 4.0. The official OTF files, converted to WOFF2 only (outlines unchanged). */
@font-face {
  font-family: "e-Ukraine Head";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/client/e-UkraineHead-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "e-Ukraine";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/e-Ukraine-Regular.woff2") format("woff2");
}
/* Intelligible & Summand case study. Headlines use "Sora" as a temporary
   stand-in for Erbaum (an Adobe Fonts font with no self-hostable woff2 yet):
   swap for the real Erbaum once Nadia sends an Adobe Fonts web-project kit. */
@font-face {
  font-family: "Sora";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/Sora-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Sora";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/Sora-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/NunitoSans-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/NunitoSans-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Red Hat Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/RedHatMono-SemiBold.woff2") format("woff2");
}
/* Ellswood case study. Bricolage Grotesque + Commissioner, both SIL OFL 1.1
   (Google Fonts, latin subset via Fontsource). */
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/client/BricolageGrotesque-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/BricolageGrotesque-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/BricolageGrotesque-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Commissioner";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/client/Commissioner-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Commissioner";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/Commissioner-SemiBold.woff2") format("woff2");
}
/* Nest Therapy for Kids case study. Playfair Display + Inter, both SIL OFL 1.1
   (Google Fonts, latin subset via Fontsource). */
@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/PlayfairDisplay-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/client/PlayfairDisplay-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../assets/fonts/client/PlayfairDisplay-ExtraBold.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("../assets/fonts/client/Inter-Light.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/client/Inter-SemiBold.woff2") format("woff2");
}

/* OpenDyslexic (SIL OFL 1.1, assets/fonts/a11y/LICENSE.txt), only for the
   Dyslexia-friendly accessibility setting. Never preloaded: the browser
   fetches it only once a rule actually uses it. */
@font-face {
  font-family: "OpenDyslexic";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/a11y/OpenDyslexic-Regular.woff2") format("woff2");
}

@font-face {
  font-family: "OpenDyslexic";
  font-style: normal;
  font-weight: 500 800;
  font-display: swap;
  src: url("../assets/fonts/a11y/OpenDyslexic-Bold.woff2") format("woff2");
}


/* ---- reset.css ---- */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
img, svg, video { display: block; max-width: 100%; height: auto; }
button, input, textarea, select { font: inherit; color: inherit; }
ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }


/* ---- base.css ---- */

html { scroll-behavior: smooth; }

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

main { flex: 1 0 auto; }

/* Syne is for H1 and H2 only; everything else is Instrument Sans. */
h1, h2 {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  color: var(--primary);
  line-height: 1.08;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
h3, h4 {
  font-family: var(--font-body);
  font-weight: var(--weight-heading);
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

.nowrap { white-space: nowrap; }

p { max-width: var(--measure); text-wrap: pretty; }
p + p { margin-top: var(--space-4); }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { text-decoration-thickness: 2px; text-decoration-color: var(--accent); }

:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
.band--dark :focus-visible { outline-color: var(--text); }

::selection { background: var(--primary); color: var(--bg); }

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

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}


/* ---- layout.css ---- */

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

.section { padding-block: var(--space-8); }
@media (min-width: 64rem) { .section { padding-block: var(--space-9); } }

/* The one lifted panel: a faint step off the page, not an inverted block. */
.band--dark { background: var(--fill); color: var(--text); }
.band--dark h1, .band--dark h2 { color: var(--text); }

/* .muted was only ever defined inside the dark band, so the class did nothing
   anywhere else. Both resolve to --muted now, so one rule covers it. */
.muted { color: var(--muted); }

.grid-2 { display: grid; gap: var(--space-6); }
@media (min-width: 48rem) {
  .grid-2 { grid-template-columns: 1fr 1fr; gap: var(--space-7); }
}
.band--dark h2 + p { margin-top: var(--space-4); }


/* ---- components.css ---- */

/* ---- Skip link ---------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -4rem;
  z-index: 10;
  padding: var(--space-3) var(--space-4);
  background: var(--primary);
  color: var(--bg);
}
.skip-link:focus { top: var(--space-4); outline-color: var(--bg); }

/* ---- Header --------------------------------------------------------------
   Desktop: logo, links with one sliding underline (moved by header.js), and
   Contact as an outlined pill. Mobile (with JS): round menu button opening a
   full-screen menu. Without JS the links simply stack. The hairline under the
   header sits on __inner, which IS the .container, so it stops at the content
   edge instead of running the full width of the viewport (her call). */
.site-header {
  padding-block: var(--space-4) 0; /* the lower half moves to __inner, which owns the rule */
  background: var(--bg);
  color: var(--text);
}
.site-header :focus-visible { outline-color: var(--text); }
.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 4.5rem;
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--secondary);
  animation: header-in 0.6s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}
@keyframes header-in { from { opacity: 0; transform: translateY(-10px); } }
/* Contact only (her call): the hairline runs the full viewport width here,
   matching the full-bleed waves panel right below it, instead of stopping at
   .container's edge like every other page. */
[data-page="contact"] .site-header__inner { border-bottom: none; }
[data-page="contact"] .site-header { position: relative; }
[data-page="contact"] .site-header::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  border-bottom: 1px solid var(--secondary);
}

.wordmark { display: block; padding-block: var(--space-3); line-height: 0; }
/* logo 21% over the 2.4/2.8rem it was sized at (two 10% steps); the negative margin gives the extra
   back so the header keeps its height */
.wordmark .nd-mark { display: block; width: auto; height: 2.9rem; margin-block: -0.25rem; }
@media (min-width: 48rem) { .wordmark .nd-mark { height: 3.39rem; margin-block: -0.295rem; } }

.icon { flex: none; width: 1.25rem; height: 1.25rem; }

.site-nav { position: relative; }
.site-nav ul {
  display: flex;
  gap: var(--space-6);
  margin: 0;
  padding: 0;
  list-style: none;
}
.site-nav a {
  display: block;
  padding-block: var(--space-2);
  border-bottom: 1px solid transparent;
  color: var(--muted);
  font-weight: 500;
  text-decoration: none;
  transition: color 0.2s;
}
.site-nav a[aria-current="page"] { color: var(--text); border-bottom-color: var(--text); }
.site-nav a.is-active { color: var(--text); }
.site-nav.is-hovering a[aria-current="page"] { color: var(--muted); }
/* with JS the sliding line replaces the per-link line */
.js .site-nav a[aria-current="page"] { border-bottom-color: transparent; }
.nav-slider {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 1px;
  background: var(--text);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), width 0.3s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s;
}
.nav-slider.is-visible { opacity: 1; }
/* gold while hovering or tabbing; it stays light when resting under the current page
   (decorative only: the link's own colour still marks it, so the accent is never the only signal) */
.site-nav.is-hovering .nav-slider { background: var(--accent); }
.nav-slider.is-jump { transition: opacity 0.2s; }

/* The site's ONE button. Outlined by default (header, footer); .nav-cta--solid
   is the filled variant used everywhere else. The arrow icon is added in the
   markup, and only when the button navigates to another page -- never on a
   form's submit. `font` and `background` are reset explicitly so the rule
   behaves identically on a <button> as on an <a>. */
.nav-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-5);
  border: 1px solid var(--secondary);
  border-radius: 999px;
  background: none;
  color: var(--text);
  font: inherit;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}
.nav-cta .icon { width: 1rem; height: 1rem; }
.nav-cta:hover, .nav-cta[aria-current="page"], .nav-cta--solid {
  border-color: var(--text);
  background: var(--text);
  color: var(--bg);
}
/* on hover the button turns accent gold with no visible border (dark text: 7:1 on the gold) */
.nav-cta:hover, .nav-cta--solid:hover, .nav-cta[aria-current="page"]:hover {
  border-color: transparent;
  background: var(--accent);
  color: var(--bg);
}

.nav-toggle, .nav-close {
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 1px solid var(--secondary);
  border-radius: 50%;
  background: none;
  color: var(--text);
  cursor: pointer;
  transition: background-color 0.2s;
}
.nav-toggle { display: none; }
.nav-close { display: flex; }
.nav-toggle:hover, .nav-close:hover { background: var(--fill); }

@media (max-width: 63.99rem) {
  .js .nav-toggle { display: flex; }
  /* scoped to the top bar: the drawer's and the footer's own .nav-cta must stay */
  .js .site-header__inner .site-nav,
  .js .site-header__inner .nav-cta { display: none; }
  .site-nav { flex-basis: 100%; }
  .site-nav ul { flex-direction: column; gap: 0; }
  .site-nav a { padding-block: var(--space-3); border-bottom: 1px solid var(--secondary); }
  .nav-slider { display: none; }
}

/* Mobile menu: a full-screen dialog that slides in from the right; its items
   fade in one after another (--i on each item), the get-in-touch block last. */
.nav-drawer {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  padding: var(--space-4) var(--page-inset) var(--space-6);
  background: var(--bg);
  color: var(--text);
  transform: translateX(100%);
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.nav-drawer[hidden] { display: none; }
.nav-drawer.is-open { transform: none; }
@media (min-width: 64rem) { .nav-drawer, .nav-drawer[hidden] { display: none; } }
.nav-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--secondary);
}
.nav-drawer__logo { display: block; width: auto; height: 2.4rem; }
.nav-drawer__nav { margin-top: var(--space-7); }
.nav-drawer__label {
  color: var(--muted);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.nav-drawer ul { margin: 0; padding: 0; list-style: none; }
.nav-drawer li a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--secondary);
  color: var(--text);
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
  text-decoration: none;
  transition: color 0.2s;
}
.nav-drawer li a .icon { color: var(--muted); transition: color 0.2s; }
.nav-drawer li a:hover { color: var(--muted); }
.nav-drawer li a:hover .icon { color: var(--text); }
.nav-drawer__foot { margin-top: auto; padding-top: var(--space-7); }
.nav-drawer__foot .nav-cta { display: flex; justify-content: center; padding-block: var(--space-4); }
.nav-drawer__item, .nav-drawer__foot { opacity: 0; }
.nav-drawer__item { transform: translateX(20px); }
.nav-drawer.is-shown .nav-drawer__item, .nav-drawer.is-shown .nav-drawer__foot {
  opacity: 1;
  transform: none;
  transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1), transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: calc(0.2s + var(--i, 5) * 0.06s);
}
.nav-drawer.is-shown .nav-drawer__foot { transition-delay: 0.5s; }

/* ---- Footer --------------------------------------------------------------
   Dark, like the header, so the page is bookended. Three columns on desktop
   (intro + socials / menu / call to action), the two right-hand columns
   unheaded and unruled (her call: they speak for themselves). Below them the NADIA DOLOT wordmark runs the full width and
   fades out into the page, then a thin bottom bar carries the copyright and
   the legal links.
   Mobile and tablet (below 64rem, her call 2026-09-22): two columns instead
   of one stacked column -- intro + social left, menu right -- with the
   "Start a project" button spanning full width in a row of its own below. */
.site-footer {
  padding-block: var(--space-7) var(--space-6);
  border-top: 1px solid var(--secondary); /* full width, unlike the header's */
  background: var(--bg);
  color: var(--muted);
}
@media (min-width: 40rem) { .site-footer { padding-top: var(--space-8); } }
.site-footer a { color: inherit; text-decoration: none; transition: color 0.2s; }
.site-footer a:hover { color: var(--text); }
.site-footer :focus-visible { outline-color: var(--text); }
.site-footer ul { margin: 0; padding: 0; list-style: none; }

.site-footer__grid { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--space-6); row-gap: var(--space-7); }
.site-footer__col--cta { grid-column: 1 / -1; }
/* below desktop the button fills its row, like the mobile menu's (her call, 2026-09-22) */
@media (max-width: 63.99rem) {
  .site-footer__col--cta .nav-cta { display: flex; justify-content: center; padding-block: var(--space-4); }
}
@media (min-width: 64rem) {
  .site-footer__grid { grid-template-columns: 1.3fr 1fr 1fr; gap: var(--space-6); }
  .site-footer__col--cta { grid-column: auto; }
}
.site-footer__intro p { max-width: 22em; color: var(--text); }
/* the selector has to out-rank ".site-footer ul { margin: 0 }" above, or the
   gap above the icons silently collapses to nothing */
.site-footer .site-footer__social { display: flex; gap: var(--space-2); margin-top: var(--space-6); }
.site-footer__social a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border: 1px solid var(--secondary);
  border-radius: 6px;
  transition: background-color 0.2s, border-color 0.2s, color 0.2s;
}
.site-footer__social a:hover { border-color: var(--accent); background: var(--accent); color: var(--bg); }
.site-footer__social .icon { width: 1.125rem; height: 1.125rem; }

/* The two link columns keep their top inset on desktop, where they sit beside
   the intro, so the nav and the button stay put; the rules that used to run
   along the top are gone (her call: they read as clutter). */
@media (min-width: 64rem) {
  .site-footer__col { padding-top: var(--space-5); }
}
.site-footer__col li + li { margin-top: var(--space-2); }
.site-footer__col .nav-cta { color: var(--text); }

/* Full-width wordmark, fading out at the bottom as it meets the bottom bar. */
.site-footer__wordmark {
  margin-top: var(--space-8);
  -webkit-mask-image: linear-gradient(to bottom, var(--text) 50%, transparent 95%);
  mask-image: linear-gradient(to bottom, var(--text) 50%, transparent 95%);
}
.site-footer__wordmark img { display: block; width: 100%; height: auto; }

.site-footer__bottom {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--secondary);
  font-size: var(--step--1);
}
.site-footer__legal { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); }
@media (min-width: 40rem) {
  .site-footer__bottom { flex-direction: row; align-items: center; justify-content: space-between; }
}

/* ---- Labels, buttons, links -------------------------------------------- */
/* Section names are chips: outlined in the accent, lettered in it too (her
   call). inline-block so the pill hugs its text instead of spanning the
   column; the pill radius is the same 999px the buttons use. */
.label {
  display: inline-block;
  margin-bottom: var(--space-5);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--accent);
  border-radius: 999px;
  color: var(--accent);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* the one chip that wraps a link (the blog post template's "Blog" backlink):
   the pill is the affordance, so drop the underline. */
/* plain label: same accent lettering, no pill (the home hero's name line, her call) */
.label--plain { padding: 0; border: 0; border-radius: 0; font-weight: 500; }
.label a { text-decoration: none; }
.label a:hover { text-decoration: underline; }
/* the band's lifted surface is --fill; accent is 7.2:1 on it, so the chip
   needs no override there any more. */

/* .btn / .btn--inverse are gone: every button on the site is .nav-cta now. */

.text-link { font-weight: 500; }

/* ---- Page header (inner pages) ----------------------------------------- */
.page-header { padding-block: var(--space-8) var(--space-7); }
/* Portfolio and Blog show no headline or intro, so the header is just a chip.
   Its bottom padding plus the section's top padding left 168px of dead space
   under it (345px on Blog); with nothing to separate, the header carries none. */
.page-header--bare { padding-bottom: 0; }
.page-header--bare .label { margin-bottom: 0; }
.page-header .label { margin-bottom: var(--space-5); }
.page-header__intro { margin-top: var(--space-5); color: var(--muted); font-size: var(--step-1); }
/* centred variant (Start a project, her call): chip, headline and intro on the page's axis */
.page-header--center { padding-top: calc(var(--space-8) + var(--space-7)); text-align: center; } /* 7.5rem: more air above the H1 (her call) */
.page-header--center h1, .page-header--center .page-header__intro { margin-inline: auto; }
/* case study titles: smaller than the site H1 (her call, 2026-10-06; was --step-4, up to 70px) */
[data-page="portfolio"] article > .page-header h1 { font-size: clamp(1.9rem, 1.4rem + 2.2vw, 3.25rem); }

/* ---- Placeholder image, empty state ------------------------------------ */
.ph-image {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 5;
  border: 1px solid var(--secondary);
  background: var(--fill);
  color: var(--muted);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-align: center;
  text-transform: uppercase;
}
.ph-image--wide { aspect-ratio: 16 / 9; }
/* A real photo dropped into a .ph-image slot (the site's real portrait):
   the placeholder's border/background stay as a loading fallback, the img
   itself fills the box and crops via object-fit, never stretches. */
img.ph-image { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: 8px; }

.empty-state {
  padding: var(--space-8) var(--space-5);
  border: 1px solid var(--secondary);
  background: var(--fill);
  text-align: center;
}
.empty-state h2 { font-size: var(--step-2); }
.empty-state p { margin: var(--space-3) auto 0; color: var(--muted); }

/* ---- Ruled list --------------------------------------------------------- */
.list-ruled { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--secondary); }
.list-ruled li {
  display: grid;
  gap: var(--space-2);
  padding-block: var(--space-5);
  border-bottom: 1px solid var(--secondary);
}
.list-ruled h3 { font-size: var(--step-1); color: var(--accent); }
.list-ruled p { color: var(--text); } /* full text colour, not muted: her call, for readability */
@media (min-width: 48rem) {
  .list-ruled li { grid-template-columns: 1fr 2fr; gap: var(--space-6); }
}

/* ---- Long-form text ----------------------------------------------------- */
/* Reading width is applied to paragraphs and lists (not the .prose box), so a
   .container.prose keeps the same left edge as the page header above it. */
/* Inline links in long-form copy carry the accent so they read as links
   without browser blue; nav, footer, buttons and cards keep colour: inherit. */
.prose a { color: var(--accent); }
.prose h2 { margin-top: var(--space-7); font-size: var(--step-2); }
.prose h3 { margin-top: var(--space-6); font-size: var(--step-1); }
.prose h2 + *, .prose h3 + * { margin-top: var(--space-3); }
.prose ul, .prose ol { max-width: var(--measure); margin-top: var(--space-4); padding-left: 1.25em; }
.prose li + li { margin-top: var(--space-2); }

/* ---- Meta row (case studies, posts) ------------------------------------ */
.meta-row {
  display: grid;
  gap: var(--space-4) var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  margin: var(--space-6) 0 0;
  padding-block: var(--space-5);
  border-block: 1px solid var(--secondary);
}
.meta-row dt {
  color: var(--accent);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.meta-row dd { margin: var(--space-1) 0 0; }

/* ---- Case-study grid and card (used once case studies exist) ----------- */
.case-grid { display: grid; gap: var(--space-7) var(--space-6); }
@media (min-width: 48rem) { .case-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 64rem) { .case-grid { grid-template-columns: repeat(3, 1fr); } }
.case-card { display: block; text-decoration: none; }
.case-card .ph-image { aspect-ratio: 4 / 3; margin-bottom: var(--space-4); border-radius: 8px; }
.case-card img { display: block; width: 100%; height: auto; margin-bottom: var(--space-4); border-radius: 8px; }
.case-card h3 { font-size: var(--step-2); }
.case-card:hover h3 { text-decoration: underline; text-decoration-thickness: 1px; }
.case-card p { color: var(--muted); }

/* ---- Contact list ------------------------------------------------------- */
.contact-list { margin: 0; border-top: 1px solid var(--secondary); }
.contact-list > div {
  display: grid;
  gap: var(--space-1);
  padding-block: var(--space-5);
  border-bottom: 1px solid var(--secondary);
}
.contact-list dt {
  color: var(--muted);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.contact-list dd { margin: 0; font-size: var(--step-1); }
@media (min-width: 48rem) {
  .contact-list > div { grid-template-columns: 1fr 2fr; gap: var(--space-6); }
}

/* ---- Next link (bottom of a case study / post) ------------------------- */
.next-link {
  display: block;
  padding-block: var(--space-7);
  border-top: 1px solid var(--secondary);
  text-decoration: none;
}
.next-link span {
  display: block;
  color: var(--muted);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.next-link strong {
  font-family: var(--font-body);
  font-size: var(--step-3);
  font-weight: var(--weight-heading);
  letter-spacing: -0.02em;
}
.next-link:hover strong { text-decoration: underline; text-decoration-thickness: 1px; }

/* ---- Case study: shared pieces ------------------------------------------
   Case studies show a client's own colours and logos, so anything inside
   [data-client-brand] is the client's palette, not this site's (the QA
   off-palette check skips it). Everything around it stays in the nd palette. */
.case-split { align-items: center; }
.case-split img { display: block; width: 100%; height: auto; }
.case-split--top { align-items: start; }
.case-split .prose h2, .case-split h2 { font-size: var(--step-2); }
.case-split h2 + p, .case-split h3 + p { margin-top: var(--space-3); }

/* ---- Concept + logomark geometry: matched to the original case study design ---------
   Two full-width sections on the client's navy (#0a1735, inline on the
   section), headline #6b9ac4. Concept is a 60/40 split, no .container: the
   photo bleeds to the section's own top/bottom/right edges with no margin.
   The text column can't use % padding (it resolves against its own narrower
   column), so its left inset mirrors .container's edge (viewport units): the larger of the
   gutter and half of what is left beside the 72rem max width. Below 1024px
   the photo is a full-width row, rotated 90deg (3:2 box, scale 1.5 to fill
   it once turned; re-derive the scale if the aspect ratio changes). */
.case-split-60-40 { display: grid; grid-template-columns: 1fr; padding-block: 0; }
.case-split-60-40 .case-split-text {
  display: flex;
  flex-direction: column;
  padding-block: 48px;
  padding-inline: var(--page-inset);
}
.case-split-60-40 .case-split-text h2 { color: #6b9ac4; font-size: var(--step-2); }
.case-split-60-40 .case-split-text p { max-width: 58ch; margin-top: 16px; color: var(--bg); }
.case-split-60-40 .case-split-wordmark { display: block; width: 70%; height: auto; margin: 48px auto 0; }
.case-split-60-40 .case-split-image { overflow: hidden; }
.case-split-60-40 .case-split-image img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  transform: rotate(90deg) scale(1.5);
}
@media (min-width: 40rem) {
  .case-split-60-40 .case-split-text { padding-block: 64px; }
}
@media (min-width: 64rem) {
  .case-split-60-40 { grid-template-columns: 6fr 4fr; align-items: stretch; }
  .case-split-60-40 .case-split-text { padding-right: var(--space-8); }
  /* stretched to the photo's row height: auto margins centre the wordmark in what is left */
  .case-split-60-40 .case-split-wordmark { margin: auto 0; }
  .case-split-60-40 .case-split-image img { height: 100%; aspect-ratio: auto; transform: none; }
}

.case-logomark-text { max-width: var(--measure); margin-inline: auto; text-align: center; color: var(--bg); }
.case-logomark-figure {
  display: block;
  width: 62%;
  height: auto;
  margin: 48px auto 0;
  transform: translateX(10%); /* off-centre on purpose */
}
@media (min-width: 40rem) { .case-logomark-figure { width: 50%; } }
@media (min-width: 64rem) { .case-logomark-figure { width: 38%; } }

/* ---- Full-bleed case-study sections, matched to the original design -------
   None of these sit in .container, so images and colour fields reach the
   viewport edge; only the text keeps the page's inset. That inset is
   --page-inset (tokens.css): the same distance from the viewport edge that
   .container's own content starts at, in viewport units (a % here would
   resolve against the narrower grid column instead of the viewport). */
.case-hero-bleed { padding-block: 0; }
.case-hero-bleed img { display: block; width: 100%; height: clamp(280px, 55vw, 640px); object-fit: cover; }

.brand-hierarchy-split { display: grid; grid-template-columns: 1fr; padding-block: 0; }
.brand-hierarchy-text { padding: 48px var(--page-inset); }
.brand-hierarchy-text h3 { font-size: var(--step-2); }
.brand-hierarchy-text h3 + p { margin-top: 16px; }
/* Three logos of different shapes matched to one height so they read as one
   row, never wrapping: the gap shrinks before the logos do. */
.brand-hierarchy-logos { display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: 16px; width: 100%; margin-top: 48px; }
.brand-hierarchy-logos img { width: auto; height: clamp(58px, 6vw, 86px); }
.brand-hierarchy-image img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
@media (min-width: 40rem) {
  .brand-hierarchy-text { padding-block: 64px; }
  .brand-hierarchy-logos { gap: 40px; width: 80%; }
}
@media (min-width: 64rem) {
  .brand-hierarchy-split { grid-template-columns: 7fr 3fr; align-items: stretch; }
  .brand-hierarchy-text { padding-right: var(--space-8); padding-bottom: 0; }
  .brand-hierarchy-image img { height: 100%; aspect-ratio: auto; }
}

/* Colour hierarchy: rows sized 60 / 25 / 15 against a fixed total height. */
.color-hierarchy { padding-block: 0; }
.color-hierarchy-intro { padding: 48px var(--page-inset); }
.color-hierarchy-intro h3 { font-size: var(--step-2); }
.color-hierarchy-intro-grid { display: grid; gap: 16px; margin-top: 16px; }
.color-hierarchy-intro-grid p { max-width: none; margin: 0; }
@media (min-width: 40rem) {
  .color-hierarchy-intro { padding-block: 64px; }
  .color-hierarchy-intro-grid { grid-template-columns: repeat(3, 1fr); }
}
.color-hierarchy-grid { display: flex; flex-direction: column; height: clamp(640px, 42vw, 820px); border-top: 1px solid var(--secondary); }
.color-hierarchy-row-primary { --row-share: 60; }
.color-hierarchy-row-secondary { --row-share: 25; }
.color-hierarchy-row-accent { --row-share: 15; }
/* Mobile: name / descriptor / HEX / CMYK take four lines, so it gets its own
   taller total (Primary 42 + Secondary 25 + Accent 15 = 82 shares, 738px). */
@media (max-width: 39.99rem) {
  .color-hierarchy-grid { height: 738px; }
  .color-hierarchy-row-primary { --row-share: 42; }
}
.color-hierarchy-row { display: flex; flex: var(--row-share) 1 0; min-height: 0; }
.color-hierarchy-cell { display: flex; flex: 1 1 0; flex-direction: column; justify-content: flex-end; min-width: 0; padding: 0 40px 24px; }
.color-hierarchy-cell:first-child { padding-left: var(--page-inset); }
.color-hierarchy-cell:last-child { padding-right: var(--page-inset); }
.color-hierarchy-name { font-family: var(--font-body); font-weight: var(--weight-heading); font-size: var(--step-1); }
.color-hierarchy-descriptor { font-weight: 400; font-size: var(--step--1); white-space: nowrap; opacity: 0.8; }
.color-hierarchy-codes { margin-top: 4px; font-size: var(--step--1); opacity: 0.8; }
@media (max-width: 39.99rem) {
  .color-hierarchy-descriptor, .color-hierarchy-cmyk { display: block; }
  .color-hierarchy-sep { display: none; }
}

/* Type specimen, in the client's real typefaces. */
.specimen { padding: var(--space-5); background: var(--fill); }
.specimen + .specimen { margin-top: var(--space-4); }
.specimen__label { color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.specimen__note { margin-top: var(--space-1); }
.specimen__sample { margin-top: var(--space-4); color: var(--primary); }
.specimen__sample--headline { font-family: "Plus Jakarta Sans", var(--font-body); font-weight: 800; font-size: var(--step-2); line-height: 1.15; }
.specimen__sample--body { font-family: "Inter", var(--font-body); font-weight: 400; font-size: var(--step-1); line-height: 1.4; }

/* Mockup gallery: full-bleed, no captions, no gaps. Mobile/tablet and desktop
   group the five images differently, so the column wrappers are display:contents
   below 1024px (children become grid items and `order` regroups them: presentation
   first, then card + folder, then signage + pin) and real flex columns above it. */
.mockup-gallery { display: grid; grid-template-columns: 1fr 1fr; padding-block: 0; }
.mockup-gallery-col { display: contents; }
.mockup-gallery img { display: block; width: 100%; height: auto; }
.mockup-gallery-presentation { grid-column: 1 / -1; order: -1; }
.mockup-gallery-card { order: 1; }
.mockup-gallery-folder { order: 2; }
.mockup-gallery-signage { order: 3; }
.mockup-gallery-pin { order: 4; }
/* paired images are different shapes; crop them square so pairs share a height */
.mockup-gallery-card, .mockup-gallery-folder, .mockup-gallery-signage, .mockup-gallery-pin { aspect-ratio: 1 / 1; object-fit: cover; }
@media (min-width: 64rem) {
  .mockup-gallery-card, .mockup-gallery-folder, .mockup-gallery-signage, .mockup-gallery-pin { aspect-ratio: auto; }
  /* 1.636 : 1 gives both columns the same total height at the images' real sizes
     (card 1200x1200, presentation 1200x800; folder 1200x1500, signage 1200x967,
     pin 1200x805). Re-derive if the image set changes. */
  .mockup-gallery { grid-template-columns: 1.636fr 1fr; }
  .mockup-gallery-col { display: flex; flex-direction: column; }
  .mockup-gallery-presentation { grid-column: auto; order: 0; }
  .mockup-gallery-card, .mockup-gallery-folder, .mockup-gallery-signage, .mockup-gallery-pin { order: 0; }
}

/* Client testimonial: a quiet card. No accent here (the page's one accent
   moment is the bar above the H1). */
/* Softer, minimal treatment (2026-09-24): no fill, a quieter rounded
   border, and the quote mark in the page\'s own dark text colour instead of
   the accent (the accent moment on a case-study page is elsewhere, e.g. the
   About pull-quote which has its own .quote-title scope). */
.testimonial { max-width: var(--measure); padding: var(--space-6); border: 1px solid var(--secondary); border-radius: 16px; }
.testimonial p { max-width: none; }
.testimonial .quote-mark { color: var(--text); font-family: "Google Sans Flex", var(--font-body); font-weight: 600; }
.testimonial__by { margin-top: var(--space-5); color: var(--muted); font-size: var(--step--1); }
.testimonial__by strong { display: block; color: var(--text); font-size: var(--step-0); font-weight: 500; }
.testimonial p + p { margin-top: var(--space-3); }

/* ---- Case study: Lux Flowers ---------------------------------------------
   Page-scoped pieces (article.cs-lux). The client's navy #1D1D56, seasalt
   #FAFAFD, lavender #EBE1F9, periwinkle #A2A2EA and pink #FFB3C6 only appear
   inside [data-client-brand]; the rest of the page stays in the nd palette. */
.lux-prose p + p { margin-top: var(--space-3); }
.cs-lux .case-split h2 { font-size: var(--step-3); }

/* Research: three figures pulled straight from the copy */
.lux-stats { display: grid; gap: var(--space-5); margin-block: var(--space-7); padding-block: var(--space-6); border-block: 1px solid var(--secondary); }
.lux-stats dt { font-family: var(--font-body); font-weight: 600; font-size: var(--step-4); line-height: 1; letter-spacing: -0.02em; }
.lux-stats dd { margin: var(--space-2) 0 0; color: var(--muted); max-width: 24ch; }
@media (min-width: 48rem) { .lux-stats { grid-template-columns: repeat(3, 1fr); } }

/* Insight: the one big line, in the client's headline face */
.lux-insight { padding-block: 72px; color: #FAFAFD; }
.lux-insight h2 { color: #A2A2EA; font-size: var(--step-2); }
.lux-insight__lead { max-width: var(--measure); margin-top: var(--space-3); }
.lux-insight__quote {
  max-width: 18ch;
  margin-top: var(--space-6);
  font-family: "Marcellus", var(--font-display);
  font-size: clamp(2.4rem, 1.4rem + 4.4vw, 5.6rem);
  line-height: 1.05;
}
.lux-insight__quote span { display: block; color: #FFB3C6; }
@media (min-width: 40rem) { .lux-insight { padding-block: 112px; } }

.lux-strategy { padding-bottom: var(--space-6); }
.lux-strategy h2 { font-size: var(--step-3); }
.lux-strategy__intro { margin-top: var(--space-3); }
.lux-promise { padding-block: 64px; color: #1D1D56; text-align: center; }
.lux-promise__label { max-width: none; font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.lux-promise__line { max-width: none; margin-top: var(--space-3); font-family: "Marcellus", var(--font-display); font-size: clamp(2rem, 1.2rem + 3.6vw, 4.4rem); line-height: 1.1; }
@media (min-width: 40rem) { .lux-promise { padding-block: 96px; } }

/* Identity: the shared 60/40 band, with Lux's colours and a square photo
   (so no rotated crop on mobile, unlike the SGS stairwell) */
.lux-identity .case-split-text h2 { color: #A2A2EA; }
.lux-identity .case-split-text p { color: #FAFAFD; }
.lux-identity .case-split-image img { aspect-ratio: 1 / 1; transform: none; }
@media (min-width: 64rem) { .lux-identity .case-split-image img { aspect-ratio: auto; } }

.lux-structure h3 { color: #1D1D56; font-size: var(--step-2); }
.lux-structure__figure { display: block; width: 100%; max-width: 720px; height: auto; margin: 48px auto 0; }

/* Logo variations: no paragraph under the heading here, so on desktop the
   logo row centres in the height the square hangtag photo gives the band */
@media (min-width: 64rem) {
  .cs-lux .brand-hierarchy-text { display: flex; flex-direction: column; }
  .cs-lux .brand-hierarchy-logos { margin-block: auto; }
}
/* bottom-aligned; full logo twice the shared height, icon a little smaller */
.cs-lux .brand-hierarchy-logos { align-items: flex-end; }
.cs-lux .brand-hierarchy-logos .lux-logo-full { height: clamp(116px, 12vw, 172px); }
.cs-lux .brand-hierarchy-logos .lux-logo-icon { height: clamp(46px, 4.8vw, 69px); }
.lux-research-more > div:empty { display: none; }
@media (min-width: 48rem) { .lux-research-more .lux-prose { grid-column: 2; } }

.lux-colors-text { max-width: var(--measure); margin-top: 16px; }

/* Colour directions explored: full-bleed, three equal-height columns (each a
   dark half over a light half with that direction's own logo). Mobile: one
   row per direction, halves side by side. */
.lux-options { display: grid; margin: 0; padding: 0; list-style: none; }
.lux-option { position: relative; display: grid; grid-template-columns: 1fr 1fr; }
.lux-option__half { display: grid; place-items: center; height: 200px; }
.lux-option__half img { width: auto; height: 96px; }
.lux-option__tag {
  position: absolute; top: 16px; left: var(--page-inset);
  padding: 4px 12px; border-radius: 999px;
  background: #FAFAFD; color: #1D1D56;
  font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase;
}
@media (min-width: 48rem) {
  .lux-options { grid-template-columns: repeat(3, 1fr); }
  .lux-option { grid-template-columns: 1fr; }
  .lux-option__half { height: 280px; }
  .lux-option__half img { height: 120px; }
  .lux-option__tag { left: 24px; }
  .lux-option:first-child .lux-option__tag { left: var(--page-inset); }
}

/* gap + heading between the explored directions and the final palette */
.lux-palette-intro { padding-top: 64px; padding-bottom: 32px; }
@media (min-width: 40rem) { .lux-palette-intro { padding-top: 96px; padding-bottom: 40px; } }

/* Final palette: primary row 60%, four secondaries 40%, no accent row */
.lux-palette { height: clamp(560px, 38vw, 720px); border-top: 0; }
.lux-palette .color-hierarchy-row-secondary { --row-share: 40; }
.lux-palette .color-hierarchy-row-secondary .color-hierarchy-cell { padding-inline: 24px; }
.lux-palette .color-hierarchy-row-secondary .color-hierarchy-cell:first-child { padding-left: var(--page-inset); }
.lux-palette .color-hierarchy-row-secondary .color-hierarchy-cell:last-child { padding-right: var(--page-inset); }
@media (max-width: 39.99rem) {
  .lux-palette { height: 640px; }
  .lux-palette .color-hierarchy-row-primary { --row-share: 60; }
  /* four across is too narrow on a phone: two rows of two */
  .lux-palette .color-hierarchy-row-secondary { flex-wrap: wrap; }
  .lux-palette .color-hierarchy-row-secondary .color-hierarchy-cell { flex-basis: 50%; }
  .lux-palette .color-hierarchy-row-secondary .color-hierarchy-cell:nth-child(odd) { padding-left: var(--page-inset); }
  .lux-palette .color-hierarchy-name { font-size: var(--step-0); }
}

.lux-sample--headline { font-family: "Marcellus", var(--font-body); font-weight: 400; font-size: var(--step-2); line-height: 1.15; }
.lux-sample--body { font-family: "Mulish", var(--font-body); font-weight: 400; font-size: var(--step-1); line-height: 1.4; }

/* Result gallery: two mockups, full-bleed, no gaps, cropped to one height */
.lux-gallery { display: grid; }
.lux-gallery img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }
@media (min-width: 48rem) { .lux-gallery { grid-template-columns: 1fr 1fr; } }

/* Lux motion + hover (2026-10-06 touch-up, structure unchanged). Same
   contract as the other case studies: hidden "before" states only behind
   .has-reveal, everything static under prefers-reduced-motion. Shared
   cells/text blocks are only clipped, never moved, so the edge-alignment QA
   still measures their resting position. */
.lux-promise__butterfly { display: block; width: 34px; aspect-ratio: 302 / 381; margin: 0 auto var(--space-4); background: #1D1D56; -webkit-mask: url("../assets/images/work/lux-flowers/logo-icon-navy.svg") center / contain no-repeat; mask: url("../assets/images/work/lux-flowers/logo-icon-navy.svg") center / contain no-repeat; }
@media (prefers-reduced-motion: no-preference) {
  .cs-lux .case-hero-bleed { overflow: hidden; }
  .cs-lux .case-hero-bleed img { animation: is-settle 1.8s cubic-bezier(0.2, 0.7, 0.2, 1) both; }

  /* "They buy guaranteed happiness." warms into pink after the first line */
  .has-reveal .lux-insight__quote span { color: color-mix(in srgb, #FFB3C6 25%, #1D1D56); transition: color 1.4s ease 0.6s; }
  .has-reveal .lux-insight__quote.is-in span { color: #FFB3C6; }

  /* the butterfly from the logo, fluttering above the promise */
  .lux-promise__butterfly { animation: lux-flutter 3.6s ease-in-out infinite; transform-origin: 50% 70%; }

  .has-reveal .cs-lux .case-split-image { overflow: hidden; }
  .has-reveal .lux-identity .case-split-image[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .lux-identity .case-split-image img { transform: scale(1.1); transition: transform 1.6s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .lux-identity .case-split-image.is-in img { transform: none; }

  /* logo variations arrive one by one */
  .has-reveal .lux-variations[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .lux-variations img { opacity: 0; transform: translateY(14px); transition: opacity 0.6s ease, transform 0.6s ease; }
  .has-reveal .lux-variations img:nth-child(2) { transition-delay: 0.15s; }
  .has-reveal .lux-variations img:nth-child(3) { transition-delay: 0.3s; }
  .has-reveal .lux-variations.is-in img { opacity: 1; transform: none; }

  /* the three directions open in turn; "Chosen" pops in last */
  .has-reveal .lux-options[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .lux-option { clip-path: inset(0 0 100% 0); transition: clip-path 0.8s cubic-bezier(0.6, 0, 0.2, 1); transition-delay: calc(var(--i) * 140ms); }
  .has-reveal .lux-options.is-in .lux-option { clip-path: inset(0); }
  .has-reveal .lux-option__tag { opacity: 0; transform: scale(0.6); transition: opacity 0.3s ease 0.9s, transform 0.45s cubic-bezier(0.3, 1.8, 0.5, 1) 0.9s; }
  .has-reveal .lux-options.is-in .lux-option__tag { opacity: 1; transform: none; }

  /* final palette paints upward swatch by swatch */
  .has-reveal .lux-palette[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .lux-palette .color-hierarchy-cell { clip-path: inset(100% 0 0 0); transition: clip-path 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), flex-grow 0.5s ease; transition-delay: calc(var(--i, 0) * 90ms), 0s; }
  .has-reveal .lux-palette.is-in .color-hierarchy-cell { clip-path: inset(0); }

  /* gallery opens top to bottom */
  .has-reveal .lux-gallery img[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 0 100% 0); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .lux-gallery img[data-reveal-delay="1"] { transition-delay: 0.12s; }
  .has-reveal .lux-gallery img.is-in { clip-path: inset(0); }

  @media (hover: hover) and (min-width: 40rem) {
    .lux-option__half img { transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.2, 1); }
    .lux-option:hover .lux-option__half img { transform: scale(1.08) translateY(-4px); }
    .lux-palette .color-hierarchy-cell { transition: flex-grow 0.5s ease; }
    .lux-palette .color-hierarchy-cell:hover { flex-grow: 1.6; }
    .lux-stats > div { transition: transform 0.35s ease; }
    .lux-stats > div:hover { transform: translateY(-4px); }
  }
}
@keyframes lux-flutter {
  0%, 100% { transform: translateY(0) scaleX(1) rotate(0deg); }
  25% { transform: translateY(-5px) scaleX(0.82) rotate(-4deg); }
  50% { transform: translateY(-8px) scaleX(1) rotate(0deg); }
  75% { transform: translateY(-4px) scaleX(0.86) rotate(4deg); }
}

/* ---- Case study: WCFF -----------------------------------------------------
   Page-scoped pieces (article.cs-wcff). Only two mockups exist, so the page
   leans on typographic structure instead of a photo gallery: a dark logo
   hero, the brief as four colour-coded requirements, the idea as one big
   bold Onest line, an annotated construction drawing, and three logo-version
   columns. The client's colours (#0096EA blue, #F4003A red, #00A532 green,
   #FFB000 yellow, plus the brand's two untinted neutrals, declared inline
   on the article as --wcff-black / --wcff-white) only appear inside [data-client-brand]. */
.wcff-prose p + p { margin-top: var(--space-3); }
.cs-wcff .case-split h2 { font-size: var(--step-3); }

/* Hero: the primary logo alone on the brand's dark */
.wcff-hero { display: grid; place-items: center; padding-block: clamp(64px, 11vw, 168px); background: var(--wcff-black); }
.wcff-hero img { display: block; width: clamp(220px, 38vw, 540px); height: auto; }

/* Challenge: the four requirements, one brand colour each, numbered */
.wcff-brief { display: grid; gap: var(--space-6); margin: var(--space-8) 0 0; padding: 0; list-style: none; counter-reset: wcff-brief; }
.wcff-brief li { padding-top: var(--space-4); border-top: 4px solid var(--bar); counter-increment: wcff-brief; }
.wcff-brief li::before {
  content: counter(wcff-brief, decimal-leading-zero);
  display: block;
  margin-bottom: var(--space-3);
  color: var(--muted);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
}
@media (min-width: 48rem) { .wcff-brief { grid-template-columns: 1fr 1fr; gap: var(--space-7) var(--space-6); } }
@media (min-width: 64rem) { .wcff-brief { grid-template-columns: repeat(4, 1fr); } }
.wcff-statement { max-width: 34ch; margin-top: var(--space-8); color: var(--muted); font-size: var(--step-2); line-height: 1.3; letter-spacing: -0.01em; }
.wcff-statement span { display: block; margin-top: var(--space-3); color: var(--text); }
.wcff-start { padding-top: 0; }

/* Idea: one line in the client's body face, bold (her call); the second half, on its
   own line, carries the logo's gradient */
.wcff-idea { padding-block: 80px; background: var(--wcff-black); color: var(--wcff-white); }
.wcff-idea h2 { color: #0096EA; font-size: var(--step-2); }
.wcff-idea__lead { max-width: var(--measure); margin-top: var(--space-3); }
.wcff-idea__line {
  max-width: 24ch;
  margin-top: var(--space-7);
  font-family: "Onest", var(--font-body);
  font-weight: 700;
  font-size: clamp(1.8rem, 1rem + 3.4vw, 4.4rem);
  letter-spacing: -0.02em;
  line-height: 1.15;
}
.wcff-idea__line span {
  display: block;
  width: fit-content;
  background: linear-gradient(90deg in oklch, #F4003A, #0096EA, #00A532, #FFB000);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
@media (min-width: 40rem) { .wcff-idea { padding-block: 128px; } }

/* Logo structure: heading, drawing and notes together on the brand's dark
   (no separate "The Identity" title, her call 2026-09-25) */
.wcff-anatomy h3 { margin-bottom: 64px; color: var(--wcff-white); }
.wcff-anatomy { padding-block: 64px; background: var(--wcff-black); color: var(--wcff-white); }
.wcff-anatomy__figure { display: block; width: 100%; height: auto; }
.wcff-anatomy__notes { display: grid; gap: var(--space-6); margin: 64px 0 0; padding: 0; list-style: none; }
.wcff-anatomy__notes li { padding-top: var(--space-4); border-top: 1px solid color-mix(in srgb, var(--wcff-white) 30%, transparent); }
.wcff-anatomy__label { margin-bottom: var(--space-2); color: #FFB000; font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
@media (min-width: 48rem) {
  .wcff-anatomy { padding-block: 96px; }
  .wcff-anatomy__notes { grid-template-columns: repeat(3, 1fr); gap: var(--space-7); margin-top: 96px; }
}

/* Logo versions: full-bleed columns, each a light half over a dark half
   (the reverse test from the brief); the wide secondary logo gets more room */
.wcff-versions__intro { padding-block: 64px 32px; font-size: var(--step-1); }
.wcff-versions { display: grid; margin: 0; padding: 0; list-style: none; }
.wcff-version__half { display: grid; place-items: center; height: 180px; padding-inline: var(--page-inset); }
.wcff-version__half--light { background: var(--wcff-white); }
.wcff-version__half--dark { background: var(--wcff-black); }
.wcff-version__half--full { height: 360px; }
.wcff-version__half img { width: auto; max-width: 100%; height: 96px; }
.wcff-version--wide .wcff-version__half img { height: auto; width: min(100%, 380px); }
.wcff-version__half--full img { height: 150px; }
.wcff-version__text { padding: var(--space-5) var(--page-inset) var(--space-7); }
.wcff-version__name { color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.wcff-version__text p + p { margin-top: var(--space-2); }
@media (min-width: 64rem) {
  .wcff-versions { grid-template-columns: 1fr 1.5fr 1fr; }
  .wcff-version__half { height: 240px; padding-inline: 32px; }
  .wcff-version__half--full { height: 480px; }
  .wcff-version__half img { height: 110px; }
  .wcff-version--wide .wcff-version__half img { width: min(90%, 440px); }
  .wcff-version__half--full img { height: 170px; }
  .wcff-version__text { padding: var(--space-5) 32px 0; }
  .wcff-version:first-child .wcff-version__text { padding-left: var(--page-inset); }
  .wcff-version:last-child .wcff-version__text { padding-right: var(--page-inset); }
}

/* Colours: the two neutrals 60%, the four colours 40%, then the logo's gradient.
   Every tile carries the ball outline at 25% (her call), in the tile's text colour. */
.wcff-colors { padding-top: var(--space-8); }
.wcff-colors__text { display: grid; gap: var(--space-4); margin-top: 16px; }
.wcff-colors__text p { margin: 0; }
@media (min-width: 64rem) { .wcff-colors__text { grid-template-columns: 1fr 1fr; gap: var(--space-7); } }
.wcff-palette { height: clamp(600px, 40vw, 760px); border-top: 0; }
.wcff-palette .color-hierarchy-row-secondary { --row-share: 40; }
.wcff-palette .color-hierarchy-cell { position: relative; overflow: hidden; }
.wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell { padding-inline: 24px; }
.wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell:first-child { padding-left: var(--page-inset); }
.wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell:last-child { padding-right: var(--page-inset); }
.wcff-palette .color-hierarchy-cell::after {
  content: "";
  position: absolute;
  top: 24px;
  right: 24px;
  width: clamp(72px, 9vw, 150px);
  aspect-ratio: 245 / 158;
  background: currentColor;
  opacity: 0.25;
  -webkit-mask: url("../assets/images/work/wcff/palette-icon.svg") center / contain no-repeat;
  mask: url("../assets/images/work/wcff/palette-icon.svg") center / contain no-repeat;
}
.wcff-palette .color-hierarchy-cell:last-child::after { right: var(--page-inset); }
.wcff-palette .color-hierarchy-row-primary .color-hierarchy-cell::after { width: clamp(120px, 16vw, 260px); }
.wcff-gradient { flex: 0 0 clamp(28px, 3.4vw, 48px); background: linear-gradient(90deg in oklch, #F4003A, #0096EA, #00A532, #FFB000); }
@media (max-width: 39.99rem) {
  .wcff-palette { height: 660px; }
  .wcff-palette .color-hierarchy-row-primary { --row-share: 55; }
  /* four across is too narrow on a phone: two rows of two */
  .wcff-palette .color-hierarchy-row-secondary { --row-share: 45; flex-wrap: wrap; }
  .wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell { flex-basis: 50%; }
  .wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell:nth-child(odd) { padding-left: var(--page-inset); }
  .wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell:nth-child(odd)::after { right: 24px; }
  .wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell:nth-child(2)::after { right: var(--page-inset); }
  .wcff-palette .color-hierarchy-row-secondary .color-hierarchy-cell::after { top: 16px; width: 64px; }
  .wcff-palette .color-hierarchy-name { font-size: var(--step-0); }
}

/* Typefaces: two specimens side by side, each with a big glyph pair */
.wcff-type__grid { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.wcff-type__grid .specimen + .specimen { margin-top: 0; }
@media (min-width: 64rem) { .wcff-type__grid { grid-template-columns: 1fr 1fr; } }
.wcff-type__glyphs { margin-block: var(--space-4); font-size: clamp(4rem, 3rem + 4vw, 7rem); line-height: 1; color: var(--wcff-black); }
.cs-wcff .specimen__sample { color: var(--wcff-black); }
.wcff-sample--headline { font-family: "Orbitron", var(--font-body); font-weight: 700; }
.specimen__sample.wcff-sample--headline { font-size: var(--step-1); line-height: 1.3; }
.wcff-sample--body { font-family: "Onest", var(--font-body); font-weight: 400; }
.specimen__sample.wcff-sample--body { font-size: var(--step-1); line-height: 1.4; }

/* Into production: the shared 60/40 band on the brand's dark, app-icon photo beside it */
.wcff-production { background: var(--wcff-black); }
.wcff-production .case-split-text { justify-content: center; }
.wcff-production .case-split-text h3 { color: var(--wcff-white); }
.wcff-production .case-split-text p { color: var(--wcff-white); }
.wcff-production .case-split-image img { transform: none; }

.wcff-result__line { font-size: var(--step-1); line-height: 1.4; }
.wcff-final img { height: clamp(320px, 62vw, 880px); }
.wcff-disclaimer { margin-top: var(--space-7); color: var(--muted); font-size: var(--step--1); }
.wcff-disclaimer + .next-link { margin-top: var(--space-5); }

/* ---- Case study: The Digital Her ------------------------------------------
   Page-scoped pieces (article.cs-dh). Only two real photos exist (the hero
   and one social mockup), so -- like WCFF -- the page leans on typographic
   structure and the brand's own palette instead of a mockup gallery. The
   client's rosewood #904961, ivory #FCF5EF and dusty rose #C18B8F only
   appear inside [data-client-brand]. */
.dh-prose p + p { margin-top: var(--space-3); }
.cs-dh .case-split h2 { font-size: var(--step-3); }
/* the wordmark sits low in the hero photo; bias the crop down so it never
   loses its bottom half to the fixed-height bleed */
.cs-dh .case-hero-bleed img { object-position: center 88%; }

.dh-structure h3 { color: #FCF5EF; font-size: var(--step-2); }
.dh-structure__figure { display: block; width: 100%; max-width: 640px; height: auto; margin: 48px auto 0; }

.dh-variations-intro { max-width: var(--measure); margin-top: var(--space-3); }

/* Logo variations: one column per version on a phone, three side by side
   from tablet up. Each name sits in a fixed-height figure area so "Primary
   (stacked)", "Horizontal" and "Icon" line up regardless of how tall their
   own logo or how long their own caption is. */
.dh-versions { display: grid; gap: var(--space-7); margin: var(--space-6) 0 0; padding: 0; list-style: none; }
.dh-version { display: flex; flex-direction: column; align-items: center; text-align: center; }
.dh-version__figure { display: flex; align-items: flex-end; justify-content: center; height: clamp(140px, 14vw, 200px); }
.dh-version__img { display: block; width: auto; }
.dh-version__img--primary { height: clamp(116px, 12vw, 172px); }
.dh-version__img--horizontal { height: clamp(34.8px, 3.6vw, 51.6px); }
.dh-version__img--icon { height: clamp(58px, 6vw, 86px); }
.dh-version__name { margin-top: var(--space-5); color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.dh-version__desc { max-width: 30ch; margin-top: var(--space-2); }
@media (min-width: 48rem) { .dh-versions { grid-template-columns: repeat(3, 1fr); } }

/* Final palette: two primaries (60%) + three secondaries (40%), no accent row */
.dh-palette { height: clamp(420px, 30vw, 560px); }
.dh-palette .color-hierarchy-row-secondary { --row-share: 40; }
.dh-palette .color-hierarchy-row-secondary .color-hierarchy-cell { padding-inline: 24px; }
.dh-palette .color-hierarchy-row-secondary .color-hierarchy-cell:first-child { padding-left: var(--page-inset); }
.dh-palette .color-hierarchy-row-secondary .color-hierarchy-cell:last-child { padding-right: var(--page-inset); }
@media (max-width: 39.99rem) {
  .dh-palette { height: 640px; }
  .dh-palette .color-hierarchy-row-primary { --row-share: 60; }
  .dh-palette .color-hierarchy-row-secondary { --row-share: 40; flex-wrap: wrap; }
  .dh-palette .color-hierarchy-row-secondary .color-hierarchy-cell { flex-basis: 50%; }
  .dh-palette .color-hierarchy-row-secondary .color-hierarchy-cell:nth-child(odd) { padding-left: var(--page-inset); }
  .dh-palette .color-hierarchy-row-secondary .color-hierarchy-cell:nth-child(3) { flex-basis: 100%; padding-right: var(--page-inset); }
  .dh-palette .color-hierarchy-name { font-size: var(--step-0); }
}

/* Typefaces: the icon mark sits inline before the Cormorant sample, echoing
   the bracket that frames "her" in the wordmark itself. */
.dh-type-mark { width: 24px; height: auto; margin-right: var(--space-2); vertical-align: -4px; }
.dh-sample--headline { font-family: "LINE Seed Sans", var(--font-body); font-weight: 700; font-size: var(--step-2); line-height: 1.15; color: #904961; }
.dh-sample--body { font-family: "LINE Seed Sans", var(--font-body); font-weight: 400; font-size: var(--step-1); line-height: 1.4; color: #904961; }
.dh-sample--her { font-family: "Cormorant Infant", var(--font-display); font-style: italic; font-weight: 600; font-size: var(--step-2); line-height: 1.2; color: #904961; }

/* Merch gallery: two photos, full-bleed, no gaps, cropped to one height */
.dh-gallery { display: grid; }
.dh-gallery img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }
@media (min-width: 48rem) { .dh-gallery { grid-template-columns: 1fr 1fr; } }

/* ---- Case study: Pairwise --------------------------------------------------
   Page-scoped pieces (article.cs-pw). The client's charcoal #060400, ivory
   #FFFBF7, beige #F7E4D3, tan #CFAE80 and terracotta #B72F26 only appear
   inside [data-client-brand]. */
.pw-prose { max-width: var(--measure); }
.pw-prose p + p { margin-top: var(--space-3); }
.cs-pw .case-split h2 { font-size: var(--step-3); }

/* Insight: the thesis line, in the client's headline face */
.pw-insight { padding-block: 72px; background: #060400; color: #FFFBF7; }
.pw-insight h2 { color: #B72F26; font-size: var(--step-2); }
.pw-insight__grid { display: grid; gap: var(--space-6); margin-top: var(--space-6); }
.pw-insight__quote {
  max-width: 22ch;
  font-family: "Space Grotesk", var(--font-display);
  font-weight: 700;
  font-size: clamp(1.7rem, 1rem + 3vw, 3.6rem);
  line-height: 1.15;
}
.pw-insight__note { max-width: 32ch; color: color-mix(in srgb, #FFFBF7 85%, transparent); }
@media (min-width: 64rem) { .pw-insight__grid { grid-template-columns: 7fr 3fr; } }
@media (min-width: 40rem) { .pw-insight { padding-block: 112px; } }

/* The tagline, standing alone */
.pw-promise { padding-block: 64px; background: #F7E4D3; color: #060400; text-align: center; }
.pw-promise__label { max-width: none; font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.pw-promise__line { max-width: none; margin-top: var(--space-3); font-family: "Space Grotesk", var(--font-display); font-weight: 700; font-size: clamp(2rem, 1.2rem + 3.6vw, 4.4rem); line-height: 1.1; }
@media (min-width: 40rem) { .pw-promise { padding-block: 96px; } }

/* The Logo: dark, matching the construction diagram right after it */
.pw-logo-section { background: #060400; }
.pw-logo-section h2, .pw-logo-section .pw-prose, .pw-logo-section .pw-meanings li { color: #FFFBF7; }

/* The three meanings behind the icon, numbered in the accent */
.pw-meanings { display: grid; gap: var(--space-5); margin: var(--space-6) 0 0; padding: 0; list-style: none; counter-reset: pw-meaning; }
.pw-meanings li { padding-top: var(--space-4); border-top: 1px solid color-mix(in srgb, #FFFBF7 30%, transparent); counter-increment: pw-meaning; }
.pw-meanings li::before {
  content: counter(pw-meaning);
  display: block;
  margin-bottom: var(--space-2);
  color: #B72F26;
  font-family: "Space Grotesk", var(--font-display);
  font-weight: 700;
  font-size: var(--step-2);
}
@media (min-width: 48rem) { .pw-meanings { grid-template-columns: repeat(3, 1fr); gap: var(--space-6); } }

/* Construction diagram: ivory line art, so it needs the brand's dark */
.pw-structure { padding-block: 48px; background: #060400; }
.pw-structure__figure { display: block; width: 100%; max-width: 640px; height: auto; margin: 0 auto; }
@media (min-width: 40rem) { .pw-structure { padding-block: 80px; } }

/* Color flexibility: one lockup shown one-color and two-color, each half
   on dark and on light, so both hold up either way */
.pw-flex { display: grid; gap: var(--space-6); margin: var(--space-6) 0 0; padding: 0; list-style: none; }
.pw-flex-tile { position: relative; display: grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--secondary); border-radius: 12px; overflow: hidden; box-shadow: 0 2px 12px rgba(6, 4, 0, 0.12); }
.pw-flex-tile__half { display: grid; place-items: center; height: 160px; }
.pw-flex-tile__half img { width: 80%; max-width: 230px; height: auto; }
.pw-flex-tile__label {
  position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);
  padding: 4px 12px; border-radius: 999px;
  background: #B72F26; color: #FFFBF7;
  font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase;
}
@media (min-width: 48rem) { .pw-flex-tile__half { height: 220px; } }
@media (min-width: 64rem) { .pw-flex { grid-template-columns: repeat(2, 1fr); } }

/* Logo versions: fixed-height figure area so the four names line up
   regardless of how tall or wide each lockup actually is */
.pw-versions { display: grid; gap: var(--space-6); margin: var(--space-6) 0 0; padding: 0; list-style: none; }
.pw-version { display: flex; flex-direction: column; align-items: center; text-align: center; }
.pw-version__figure { display: flex; align-items: flex-end; justify-content: center; height: clamp(90px, 9vw, 120px); }
.pw-version__img { display: block; width: auto; }
.pw-version__img--full { height: clamp(56px, 6vw, 78px); }
.pw-version__img--icon-wordmark { height: clamp(48px, 5vw, 68px); }
.pw-version__img--wordmark { height: clamp(34px, 3.6vw, 48px); }
.pw-version__img--icon { height: clamp(72px, 7.4vw, 100px); }
.pw-version__name { margin-top: var(--space-5); color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.pw-version__desc { max-width: 26ch; margin-top: var(--space-2); }
@media (min-width: 40rem) { .pw-versions { grid-template-columns: repeat(2, 1fr); row-gap: var(--space-7); } }
@media (min-width: 64rem) { .pw-versions { grid-template-columns: repeat(4, 1fr); } }

.pw-type-section { padding-block: 80px 48px; }
.pw-type-section .specimen { background: #FFFFFF; } /* true white, her explicit call (2026-09-27) -- see NoPureBlackOrWhiteTests.ALLOWED */
.pw-sample--headline { font-family: "Space Grotesk", var(--font-body); font-weight: 700; font-size: var(--step-2); line-height: 1.15; color: #CFAE80; }
.pw-sample--body-light { font-family: "Pretendard", var(--font-body); font-weight: 300; font-size: var(--step-1); line-height: 1.5; color: #CFAE80; }
.pw-sample--body-semibold { font-family: "Pretendard", var(--font-body); font-weight: 600; font-size: var(--step-1); line-height: 1.4; color: #CFAE80; }

/* Packaging gallery: the two remaining photos, full-bleed, side by side
   (the hero shot sits above, paired with the intro text) */
.pw-packaging { display: grid; }
.pw-packaging img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }
@media (min-width: 48rem) { .pw-packaging { grid-template-columns: repeat(2, 1fr); } }

/* Packaging hero: full-bleed, no container inset around the photo (the
   default case-split-60-40 behaviour already does this: 3:2 on a phone,
   stretched to fill the row with no gaps from tablet up) */
.pw-packaging-hero .case-split-text h3 { font-size: var(--step-2); color: var(--text); }
.pw-packaging-hero .case-split-text p { color: var(--text); }
@media (min-width: 64rem) { .pw-packaging-hero { grid-template-columns: 1fr 1fr; } }

/* No gap between the hero photo above and this gallery: same photo width,
   flush against each other */
.pw-packaging-more { padding-block: 0 var(--space-8); }
@media (min-width: 64rem) { .pw-packaging-more { padding-block: 0 var(--space-9); } }

/* Result: the shared 60/40 band, on the brand's dark, sleeve mockup beside it */
.pw-result { background: #060400; }
.pw-result .case-split-text h2 { color: #B72F26; }
.pw-result .case-split-text p { color: #FFFBF7; }
.pw-result .case-split-image img { aspect-ratio: 3 / 2; transform: none; }
@media (min-width: 64rem) { .pw-result .case-split-image img { aspect-ratio: auto; } }

/* Pairwise motion + hover (2026-10-07): the insight settles in, the tagline
   tightens its tracking, each meaning's rule draws in, the construction
   drawing draws left to right, each colour-flexibility pair opens outward
   from the seam, logo versions rise in sequence, the palette paints row by
   row, packaging photos clip open. Shared cells are only clipped, never
   translated. Same reduced-motion contract as the rest of the site. */
@media (prefers-reduced-motion: no-preference) {
  .has-reveal .pw-insight__quote[data-reveal] { transition-duration: 0.8s; }

  @media (min-width: 48rem) {
    .has-reveal .pw-promise__line[data-reveal] { transform: none; letter-spacing: 0.14em; transition: opacity 0.9s ease, letter-spacing 1.2s cubic-bezier(0.2, 0.7, 0.2, 1); }
    .has-reveal .pw-promise__line.is-in { letter-spacing: normal; }
  }

  .has-reveal .pw-meanings li[data-reveal] {
    border-top-color: transparent;
    background: linear-gradient(color-mix(in srgb, #FFFBF7 30%, transparent), color-mix(in srgb, #FFFBF7 30%, transparent)) 0 0 / 0% 1px no-repeat;
    transition: opacity 0.4s ease, transform 0.4s ease, background-size 0.9s cubic-bezier(0.6, 0, 0.2, 1);
  }
  .has-reveal .pw-meanings li[data-reveal-delay="1"] { transition-delay: 0.15s; }
  .has-reveal .pw-meanings li[data-reveal-delay="2"] { transition-delay: 0.3s; }
  .has-reveal .pw-meanings li.is-in { background-size: 100% 1px; }

  .has-reveal .pw-structure__figure[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 100% 0 0); transition: clip-path 1.4s cubic-bezier(0.6, 0, 0.2, 1) 0.15s; }
  .has-reveal .pw-structure__figure.is-in { clip-path: inset(0); }

  /* each pair opens outward from the seam, then its label pops in */
  .has-reveal .pw-flex-tile .pw-flex-tile__half { transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1) 0.2s; }
  .has-reveal .pw-flex-tile .pw-flex-tile__half:first-child { clip-path: inset(0 0 0 100%); }
  .has-reveal .pw-flex-tile .pw-flex-tile__half:nth-child(2) { clip-path: inset(0 100% 0 0); }
  .has-reveal .pw-flex-tile.is-in .pw-flex-tile__half { clip-path: inset(0); }
  .has-reveal .pw-flex-tile .pw-flex-tile__label { opacity: 0; transition: opacity 0.4s ease 0.9s; }
  .has-reveal .pw-flex-tile.is-in .pw-flex-tile__label { opacity: 1; }

  .has-reveal .pw-palette[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .pw-palette .color-hierarchy-row { clip-path: inset(0 100% 0 0); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .pw-palette .color-hierarchy-row-secondary { transition-delay: 0.15s; }
  .has-reveal .pw-palette .color-hierarchy-row-accent { transition-delay: 0.3s; }
  .has-reveal .pw-palette.is-in .color-hierarchy-row { clip-path: inset(0); }

  .has-reveal .cs-pw .case-split-image[data-reveal],
  .has-reveal .pw-packaging img[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 0 100% 0); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .pw-packaging img[data-reveal-delay="1"] { transition-delay: 0.12s; }
  .has-reveal .cs-pw .case-split-image.is-in,
  .has-reveal .pw-packaging img.is-in { clip-path: inset(0); }

  @media (hover: hover) and (min-width: 40rem) {
    .pw-flex-tile__half img, .pw-version__img { transition: transform 0.35s ease; }
    .pw-flex-tile:hover .pw-flex-tile__half img { transform: scale(1.05); }
    .pw-version:hover .pw-version__img { transform: translateY(-6px); }
  }
}

/* ---- Case study: Chervona Ruta UK Foundation ------------------------------
   Page-scoped pieces (article.cs-cr). The client's red #DD0010 and deep red
   #A00013, plus its two pure neutrals, are declared inline on the article as
   --cr-red / --cr-deep / --cr-black / --cr-white (the site's CSS never spells
   out the pure extremes), and only appear inside [data-client-brand]. Signature
   moves (kept quiet, her call): the red Venn finding, the one big insight line
   on the brand's dark, and a palette whose primary colours grow on hover. Typefaces e-Ukraine Head + e-Ukraine live in assets/fonts/client/. */
.cr-prose p + p { margin-top: var(--space-3); }
.cs-cr .case-split h2 { font-size: var(--step-3); }

/* Challenge: the two jobs, two quiet columns on the page tone */
.cr-jobs { display: grid; gap: var(--space-5) var(--space-7); margin: var(--space-7) 0 0; padding: 0; list-style: none; }
.cr-job { padding-top: var(--space-4); border-top: 1px solid var(--secondary); }
.cr-job__no { color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.cr-job__line { max-width: 22ch; margin-top: var(--space-2); font-family: "e-Ukraine Head", var(--font-body); font-weight: 500; font-size: var(--step-2); line-height: 1.2; }
@media (min-width: 40rem) { .cr-jobs { grid-template-columns: 1fr 1fr; } }

/* Research: the finding on the brand's red, a Venn beside the words */
.cr-research { padding-bottom: var(--space-7); }
.cr-finding { display: grid; gap: var(--space-7); align-items: center; padding: var(--space-8) var(--page-inset); background: var(--bg); color: var(--cr-black); }
.cr-finding__venn { position: relative; width: min(100%, 28rem); aspect-ratio: 1.6; margin-inline: auto; }
.cr-finding__circle {
  position: absolute;
  top: 0;
  display: grid;
  width: 62%;
  aspect-ratio: 1;
  place-items: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--cr-red) 45%, transparent);
  color: var(--cr-black);
  font-size: var(--step--1);
  line-height: 1.25;
  text-align: center;
}
.cr-finding__circle span { max-width: 10ch; }
.cr-finding__circle--a { left: 0; padding-right: 30%; }
.cr-finding__circle--b { right: 0; padding-left: 30%; }
.cr-finding__both {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: var(--cr-white);
  font-family: "e-Ukraine Head", var(--font-body);
  font-weight: 500;
  font-size: var(--step-2);
  line-height: 1;
}
.cr-finding__label { color: var(--cr-black); font-size: var(--step--1); letter-spacing: 0.12em; text-transform: uppercase; opacity: 0.6; }
.cr-finding__body { margin-top: var(--space-3); }
.cr-finding__line { max-width: 20ch; margin-top: var(--space-5); font-family: "e-Ukraine Head", var(--font-body); font-weight: 500; font-size: var(--step-2); line-height: 1.1; letter-spacing: -0.01em; }
@media (min-width: 64rem) { .cr-finding { grid-template-columns: 5fr 6fr; gap: var(--space-9); padding-block: 112px; } }

/* Insight: one big sentence on the brand's dark */
.cr-insight { padding-block: 88px; background: var(--cr-black); color: var(--cr-white); }
.cr-insight h2 { color: var(--cr-white); font-size: var(--step-2); }
.cr-insight__line { max-width: 22ch; margin-top: var(--space-7); font-family: "e-Ukraine Head", var(--font-body); font-weight: 500; font-size: clamp(1.6rem, 0.9rem + 2.9vw, 3.6rem); line-height: 1.1; letter-spacing: -0.015em; }
.cr-insight__line span { display: block; margin-bottom: 0.15em; color: color-mix(in srgb, var(--cr-white) 62%, var(--cr-black)); }
.cr-insight__note { max-width: 34rem; margin-top: var(--space-7); font-size: var(--step-1); line-height: 1.45; }
@media (min-width: 40rem) { .cr-insight { padding-block: 136px; } }

/* Direction: the rule, two quiet columns */
.cr-direction__lead { font-size: var(--step-1); }
.cr-rule { display: grid; gap: var(--space-5) var(--space-7); margin-top: var(--space-6); }
.cr-rule__item { padding-top: var(--space-4); border-top: 1px solid var(--secondary); }
.cr-rule__title { max-width: 18ch; font-family: "e-Ukraine Head", var(--font-body); font-weight: 500; font-size: var(--step-2); line-height: 1.2; }
.cr-rule__item p { margin-top: var(--space-3); }
@media (min-width: 40rem) { .cr-rule { grid-template-columns: 1fr 1fr; } }

/* Identity: the logo story on the brand's dark. The two reference photos sit side by side,
   cropped to one shape so they align; the construction drawing runs the full width beneath. */
.cr-logo { padding-block: 88px 0; background: var(--cr-black); color: var(--cr-white); }
.cr-logo h2, .cr-logo h3 { color: var(--cr-white); }
.cr-logo h2 { font-size: var(--step-2); }
.cr-logo__top { display: grid; gap: var(--space-7); align-items: start; margin-top: var(--space-7); }
.cr-logo__text p { max-width: 36rem; margin-top: var(--space-3); }
.cr-logo__photos { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); margin: 0; padding: 0; list-style: none; max-width: 30rem; }
.cr-logo__photos img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; border-radius: 8px; }
.cr-logo__photos li:first-child img { object-position: 50% 68%; }
.cr-logo__photos li:last-child img { object-position: 55% 45%; }
.cr-logo__photos p { margin-top: var(--space-2); font-size: var(--step--1); line-height: 1.4; opacity: 0.8; }
@media (min-width: 48rem) { .cr-logo__top { grid-template-columns: 1fr 1fr; gap: var(--space-8); } .cr-logo__photos { justify-self: end; } }
.cr-logo__structure { display: block; width: 100%; height: auto; margin-top: var(--space-8); }
@media (min-width: 40rem) { .cr-logo { padding-top: 120px; } }

/* Logo versions: three columns on the client's own pure light field, with air above them */
.cr-versions-section { padding-block: var(--space-8) 0; background: var(--cr-white); color: var(--cr-black); }
.cr-versions { display: grid; margin: 0; padding: 0; list-style: none; }
.cr-version__tile { display: grid; place-items: center; height: 150px; padding-inline: var(--page-inset); background: var(--cr-white); }
.cr-version__tile img { width: auto; max-width: 100%; height: 84px; }
.cr-version--wide .cr-version__tile img { height: auto; width: min(100%, 360px); }
.cr-version__name, .cr-version__desc { padding-inline: var(--page-inset); }
.cr-version__name { margin-top: var(--space-5); color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.cr-version__desc { padding-bottom: var(--space-6); }
@media (min-width: 64rem) {
  .cr-versions { grid-template-columns: 1fr 1.7fr 1.15fr; }
  .cr-version__tile { height: 200px; padding-inline: 32px; }
  .cr-version__tile img { height: 100px; }
  .cr-version--wide .cr-version__tile img { width: min(90%, 420px); }
  .cr-version__name, .cr-version__desc { padding-inline: 32px; }
  .cr-version:first-child .cr-version__name, .cr-version:first-child .cr-version__desc { padding-left: var(--page-inset); }
  .cr-version:last-child .cr-version__name, .cr-version:last-child .cr-version__desc { padding-right: var(--page-inset); }
}

/* Colours: the three primaries (60%), the flag pair, then the extended range */
.cr-colors { padding-top: var(--space-8); }
.cr-colors__text { display: grid; gap: var(--space-4); margin-top: 16px; }
.cr-colors__text p { margin: 0; }
@media (min-width: 64rem) { .cr-colors__text { grid-template-columns: 1fr 1fr; gap: var(--space-7); } }
.cr-palette { height: auto; border-top: 0; }
.cr-palette .color-hierarchy-row-primary { flex: none; min-height: clamp(360px, 30vw, 520px); }
.cr-palette .color-hierarchy-row-primary .color-hierarchy-cell { transition: flex-grow 0.5s ease; }
.cr-palette .cr-symbols { flex: none; min-height: 150px; }
.cr-palette .color-hierarchy-cell { padding-top: var(--space-5); }
.cr-codes p { max-width: none; margin: 0; font-size: var(--step--1); line-height: 1.55; opacity: 0.85; }
.cr-codes p + p { margin: 0; }
@media (hover: hover) and (min-width: 40rem) {
  .cr-palette .color-hierarchy-row-primary .color-hierarchy-cell:hover { flex-grow: 1.7; }
}
@media (max-width: 39.99rem) {
  .cr-palette .color-hierarchy-row-primary { flex-direction: column; min-height: 0; }
  .cr-palette .color-hierarchy-row-primary .color-hierarchy-cell { min-height: 220px; }
  .cr-palette .cr-symbols { flex-direction: column; }
  .cr-palette .color-hierarchy-cell { padding-inline: var(--page-inset); }
}
.cr-extended { padding-top: 0; background: var(--bg); }
.cr-extended__label { padding: var(--space-6) var(--page-inset) var(--space-4); color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.cr-chips { display: grid; grid-template-columns: repeat(2, 1fr); margin: 0; padding: 0; list-style: none; }
.cr-chip { display: flex; flex-direction: column; justify-content: flex-end; min-height: 112px; padding: var(--space-3) var(--space-4); transition: transform 0.25s ease; }
.cr-chip p { max-width: none; margin: 0; font-size: var(--step--1); line-height: 1.4; }
.cr-chip p + p { margin: 0; opacity: 0.8; }
@media (min-width: 40rem) { .cr-chips { grid-template-columns: repeat(4, 1fr); } .cr-chip { min-height: 128px; } }
@media (min-width: 64rem) { .cr-chips { grid-template-columns: repeat(8, 1fr); } .cr-chip { min-height: 160px; } }
@media (hover: hover) { .cr-chip:hover { position: relative; z-index: 1; transform: translateY(-8px); } }

/* Typography: two specimens with a Latin + Ukrainian glyph pair each */
.cr-type__grid { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.cr-type__grid .specimen + .specimen { margin-top: 0; }
@media (min-width: 64rem) { .cr-type__grid { grid-template-columns: 1fr 1fr; } }
.cr-type__glyphs { margin-block: var(--space-4); font-size: clamp(4rem, 3rem + 4vw, 7rem); line-height: 1; color: var(--cr-black); }
.cs-cr .specimen__sample { color: var(--cr-black); }
.cr-sample--head { font-family: "e-Ukraine Head", var(--font-body); font-weight: 500; }
.specimen__sample.cr-sample--head { font-size: var(--step-2); line-height: 1.2; }
.cr-sample--body { font-family: "e-Ukraine", var(--font-body); font-weight: 400; }
.specimen__sample.cr-sample--body { font-size: var(--step-1); line-height: 1.4; }

/* Graphic element and grid: text on a clean field, the poster wall running full-bleed beneath */
.cr-graphic { padding-block: 0; background: var(--cr-white); color: var(--cr-black); }
.cr-graphic .container { padding-block: 88px 64px; }
.cr-graphic h3 { color: var(--cr-black); }
.cr-graphic__cols { display: grid; gap: var(--space-4) var(--space-7); margin-top: var(--space-5); }
.cr-graphic__cols p { max-width: 34rem; margin: 0; }
.cr-graphic__photo { display: block; width: 100%; height: auto; }
@media (min-width: 48rem) { .cr-graphic__cols { grid-template-columns: 1fr 1fr; } }

/* Result: tickets and wristbands, full-bleed side by side, no gaps */
.cr-gallery { display: grid; grid-template-columns: 1fr; padding-block: 0; }
.cr-gallery img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
@media (min-width: 40rem) { .cr-gallery { grid-template-columns: 1fr 1fr; } }

.cr-credit { margin-top: var(--space-7); color: var(--muted); font-size: var(--step--1); }
.cr-credit + .next-link { margin-top: var(--space-5); }

/* Chervona Ruta responsive touch-ups (2026-10-07): below desktop the logo
   versions centre their labels under the centred logos (icon and vertical
   side by side on tablets, horizontal across beneath), and the extended
   chips are shorter on phones. */
@media (max-width: 63.99rem) {
  .cr-version { text-align: center; }
  .cr-version__name, .cr-version__desc { max-width: none; }
  .cr-version__tile { height: 130px; }
}
@media (min-width: 40rem) and (max-width: 63.99rem) {
  .cr-versions { grid-template-columns: 1fr 1fr; }
  .cr-version--wide { grid-column: 1 / -1; order: 3; }
}
@media (max-width: 39.99rem) { .cr-chip { min-height: 88px; } }

/* Chervona Ruta motion (2026-10-07), quiet like the page: the two circles of
   the finding slide together until "Red" appears where they meet, the rules
   above the jobs and the direction draw in, the construction drawing opens
   from its centre, the palette paints in row by row and the extended chips
   ripple in, photos clip open. Shared cells are only clipped, never
   translated. Same reduced-motion contract as the rest of the site. */
@media (prefers-reduced-motion: no-preference) {
  .has-reveal .cr-job[data-reveal],
  .has-reveal .cr-rule__item[data-reveal] {
    border-top-color: transparent;
    background: linear-gradient(var(--secondary), var(--secondary)) 0 0 / 0% 1px no-repeat;
    transition: opacity 0.4s ease, transform 0.4s ease, background-size 0.9s cubic-bezier(0.6, 0, 0.2, 1);
  }
  .has-reveal .cr-job[data-reveal-delay="1"],
  .has-reveal .cr-rule__item[data-reveal-delay="1"] { transition-delay: 0.15s; }
  .has-reveal .cr-job.is-in,
  .has-reveal .cr-rule__item.is-in { background-size: 100% 1px; }

  .has-reveal .cr-finding { overflow-x: clip; }
  .has-reveal .cr-finding__venn[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .cr-finding__circle { transition: transform 1.1s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .cr-finding__circle--a { transform: translateX(-16%); }
  .has-reveal .cr-finding__circle--b { transform: translateX(16%); }
  .has-reveal .cr-finding__venn.is-in .cr-finding__circle { transform: none; }
  .has-reveal .cr-finding__both { opacity: 0; transition: opacity 0.5s ease 0.9s; }
  .has-reveal .cr-finding__venn.is-in .cr-finding__both { opacity: 1; }

  .has-reveal .cr-insight__line[data-reveal] { transition-duration: 0.8s; }

  .has-reveal .cr-logo__structure[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 50% 0 50%); transition: clip-path 1.4s cubic-bezier(0.6, 0, 0.2, 1) 0.15s; }
  .has-reveal .cr-logo__structure.is-in { clip-path: inset(0); }

  .has-reveal .cr-palette[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .cr-palette .color-hierarchy-row { clip-path: inset(0 100% 0 0); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .cr-palette .cr-symbols { transition-delay: 0.2s; }
  .has-reveal .cr-palette.is-in .color-hierarchy-row { clip-path: inset(0); }

  .has-reveal .cr-chips[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .cr-chips .cr-chip { clip-path: inset(100% 0 0 0); transition: clip-path 0.6s cubic-bezier(0.2, 0.7, 0.2, 1), transform 0.25s ease; }
  .has-reveal .cr-chips .cr-chip:nth-child(8n+2) { transition-delay: 0.05s; }
  .has-reveal .cr-chips .cr-chip:nth-child(8n+3) { transition-delay: 0.10s; }
  .has-reveal .cr-chips .cr-chip:nth-child(8n+4) { transition-delay: 0.15s; }
  .has-reveal .cr-chips .cr-chip:nth-child(8n+5) { transition-delay: 0.20s; }
  .has-reveal .cr-chips .cr-chip:nth-child(8n+6) { transition-delay: 0.25s; }
  .has-reveal .cr-chips .cr-chip:nth-child(8n+7) { transition-delay: 0.30s; }
  .has-reveal .cr-chips .cr-chip:nth-child(8n+8) { transition-delay: 0.35s; }
  .has-reveal .cr-chips.is-in .cr-chip { clip-path: inset(0); }

  .has-reveal .cr-graphic__photo[data-reveal],
  .has-reveal .cr-gallery img[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 0 100% 0); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .cr-gallery img[data-reveal-delay="1"] { transition-delay: 0.12s; }
  .has-reveal .cr-graphic__photo.is-in,
  .has-reveal .cr-gallery img.is-in { clip-path: inset(0); }

  @media (hover: hover) and (min-width: 40rem) {
    .cr-type__glyphs { transition: transform 0.35s ease; }
    .cs-cr .specimen:hover .cr-type__glyphs { transform: translateY(-6px); }
  }
}

/* ==========================================================================
   Intelligible & Summand case study
   ========================================================================== */
.is-prose p + p { margin-top: var(--space-3); }
.is-second-brief { max-width: var(--measure); margin-top: var(--space-6); color: var(--muted); }

.is-request { margin-top: var(--space-7); padding: var(--space-6); border-radius: 16px; background: var(--is-mist); color: var(--is-black); }
.is-request__label { color: var(--is-steel); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.is-request__line { margin-top: var(--space-3); font-family: "Sora", var(--font-body); font-weight: 700; font-size: var(--step-3); line-height: 1.1; color: var(--is-teal); }
.is-request__note { max-width: 44rem; margin-top: var(--space-4); }

/* Research: an abstract category-palette comparison (generic swatches, not
   real competitor logos) next to Intelligible's one teal. */
.is-category { display: grid; gap: var(--space-6); margin-top: var(--space-8); padding-top: var(--space-6); border-top: 1px solid var(--secondary); }
.is-category__row { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--space-3); margin: 0 0 var(--space-3); padding: 0; list-style: none; }
.is-category__row li { height: 64px; border-radius: 8px; background: var(--c); }
.is-category__row--one { grid-template-columns: 1fr; max-width: 9rem; }
.is-category__caption { color: var(--muted); font-size: var(--step--1); }
.is-category__vs { justify-self: start; color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }

.is-insight { padding-block: 88px; background: var(--is-teal); color: var(--is-smoke); }
.is-insight__line { max-width: 26ch; margin-top: var(--space-7); font-family: "Sora", var(--font-body); font-weight: 700; font-size: clamp(1.25rem, 1rem + 1.1vw, 2rem); line-height: 1.25; letter-spacing: -0.01em; }
.is-insight__line span { display: block; margin-top: 0.3em; }
.is-insight__note { max-width: 34rem; margin-top: var(--space-7); font-size: var(--step-1); line-height: 1.45; color: color-mix(in srgb, var(--is-smoke) 85%, var(--is-teal)); }

/* Logo bands (Intelligible's and, via --summand, the Summand structure
   section) sit on the page's own bg tone, matching the plain band above. */
.is-logo { padding-block: var(--space-8); background: var(--bg); color: var(--is-black); }
.is-logo h3 { font-size: var(--step-2); color: var(--is-black); }
.is-logo__text { max-width: 44rem; margin-top: var(--space-4); }
.is-logo__structure { display: block; width: 100%; max-width: 34rem; height: auto; margin: var(--space-7) auto 0; }
.is-logo__structure--wide { max-width: 56rem; }
.is-logo--summand { padding-block: var(--space-7) var(--space-8); background: var(--is-smoke); }

/* One shared chip recipe outside the sitewide .label/--accent contract
   (QA pins every .label to the site accent colour): border is currentColor,
   so text and outline always match whatever colour variant is applied. */
.is-chip { display: inline-block; width: fit-content; margin-bottom: var(--space-5); padding: var(--space-1) var(--space-3); border: 1px solid currentColor; border-radius: 999px; font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.is-chip--teal { color: var(--is-teal); }
.is-chip--light { color: var(--is-smoke); }

/* The grid sits in the page container (band stays full-bleed). Each logo
   sits bottom-left in its column so the lockups share a baseline and the
   caption starts exactly under the logo's left edge. Sizing is width-only
   (height from the file's own ratio): a fixed height fighting max-width is
   what squashed the wordmark/vertical lockups once. */
.is-versions-section { padding-block: var(--space-7) var(--space-8); background: var(--is-smoke); }
.is-versions { display: grid; gap: 0 var(--space-6); margin-block: 0; list-style: none; }
.is-version__tile { display: grid; align-items: end; justify-items: start; padding-top: var(--space-6); }
.is-version__tile img { display: block; width: min(100%, 160px); height: auto; }
.is-version__name { padding: var(--space-4) 0 var(--space-7); color: var(--is-steel); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }

.is-version--primary .is-version__tile img { width: min(100%, 400px); }
.is-version--wordmark .is-version__tile img { width: min(100%, 230px); }
.is-version--vertical .is-version__tile img { width: min(100%, 215px); }
.is-version--icon .is-version__tile img { width: min(100%, 67px); }
/* Summand's own 3-up grid has more room per tile, but she didn't ask for
   these any bigger/smaller than a sane default -- only fixing the squash. */
.is-versions--three .is-version--primary .is-version__tile img { width: min(100%, 300px); }
.is-versions--three .is-version--wordmark .is-version__tile img { width: min(100%, 220px); }
.is-versions--three .is-version--icon .is-version__tile img { width: min(100%, 90px); }

.is-summand-bleed { background: var(--is-black); }
/* The shared 60/40 split rotates its photo 90deg below 64rem (made for a
   portrait shot elsewhere); this one is a square-ish render, keep it upright. */
.is-summand-bleed .case-split-image img { transform: none; }
.is-summand-bleed .case-split-text h3 { color: var(--is-smoke); font-size: var(--step-2); }
.is-summand-bleed .case-split-text p { max-width: 58ch; color: var(--is-smoke); opacity: 0.85; }

.is-strategy-band { background: var(--is-mist); }
.is-strategy__key { color: var(--is-teal); font-weight: 600; }

/* Brief: primary covers at least 70%, accent stays sparing -- shift the
   shared 60/25/15 split instead of the default. The accent row only holds
   one cell's worth of text (name + hex), so it needs a min-height floor:
   at 8% share its flex-basis undercuts the text and the two rows overlap. */
.is-palette .color-hierarchy-row-primary { --row-share: 68; }
.is-palette .color-hierarchy-row-secondary { --row-share: 20; }
.is-palette .color-hierarchy-row-accent { --row-share: 12; min-height: 96px; }

.is-gradient-block { padding: var(--space-7) var(--page-inset); background: var(--is-smoke); }
.is-gradient { height: clamp(120px, 18vw, 220px); border-radius: 16px; background: linear-gradient(135deg, var(--is-black) 0%, var(--is-teal) 55%, var(--is-cyan) 100%); }
.is-gradient__caption { max-width: var(--measure); margin-top: var(--space-4); color: var(--muted); }

.is-type__grid { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.is-type__grid .specimen + .specimen { margin-top: 0; }
.is-type__glyphs { margin-block: var(--space-4); font-size: clamp(3.5rem, 2.6rem + 3.6vw, 6rem); line-height: 1; color: var(--is-teal); }
.is-sample--headline { font-family: "Sora", var(--font-body); font-weight: 700; }
.is-sample--subhead { font-family: "Nunito Sans", var(--font-body); font-weight: 700; }
.is-sample--body { font-family: "Nunito Sans", var(--font-body); font-weight: 400; }
.is-sample--mono { font-family: "Red Hat Mono", monospace; font-weight: 600; letter-spacing: 0.04em; }

.is-result__links { margin-top: var(--space-4); }

.is-gallery { display: grid; grid-template-columns: 1fr; padding-block: 0; }
.is-gallery img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }


@media (min-width: 40rem) { .is-request__line { font-size: var(--step-4); } }
@media (min-width: 48rem) {
  .is-category { grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--space-5); }
  .is-category__side--one { justify-self: center; }
}
@media (min-width: 40rem) { .is-insight { padding-block: 136px; } }
@media (min-width: 40rem) { .is-logo { padding-top: 120px; } }
@media (min-width: 48rem) {
  /* Unequal shares (not repeat(4,1fr)): the primary lockup and wordmark
     need more room than the compact vertical lockup and icon. */
  .is-versions { grid-template-columns: 2.2fr 1.4fr 1.3fr 0.7fr; }
  .is-versions--three { grid-template-columns: 1.6fr 1.2fr 0.8fr; }
}
/* One paragraph, so the shared 3-track intro grid squeezed it into a third:
   give it a left-aligned half instead. */
@media (min-width: 48rem) { .is-colors .color-hierarchy-intro-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 48rem) { .is-type__grid { grid-template-columns: 1fr 1fr; } }
/* Bottom-aligned logos only matter side by side; stacked on a phone a fixed
   tile height just left big empty gaps between them. */
@media (min-width: 48rem) { .is-version__tile { height: 150px; padding-top: 0; } }
@media (max-width: 39.99rem) { .is-palette .color-hierarchy-row-accent { min-height: 128px; } }

/* ---- Motion ----------------------------------------------------------------
   Only with motion allowed; base.css switches every animation and transition
   off under prefers-reduced-motion, and the hidden "before" states only exist
   behind .has-reveal (added by reveal.js), so without JS nothing is hidden. */
@media (prefers-reduced-motion: no-preference) {
  .cs-is .case-hero-bleed { overflow: hidden; }
  .cs-is .case-hero-bleed img { animation: is-settle 1.8s cubic-bezier(0.2, 0.7, 0.2, 1) both; }

  /* Research: the crowded blue/purple/pink row rises in, then the one teal
     swatch lands last with a small overshoot -- the insight, acted out. */
  .has-reveal .is-category li { opacity: 0; transform: translateY(14px); transition: opacity 0.45s ease, transform 0.45s ease; transition-delay: calc(var(--i, 0) * 80ms + 150ms); }
  .has-reveal .is-category__row--one li { transform: scale(0.5); transition: opacity 0.4s ease, transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); transition-delay: 0.75s; }
  .has-reveal .is-category.is-in li { opacity: 1; transform: none; }

  .has-reveal .is-insight__line span { opacity: 0; transform: translateY(10px); transition: opacity 0.5s ease 0.35s, transform 0.5s ease 0.35s; }
  .has-reveal .is-insight__line.is-in span { opacity: 1; transform: none; }

  .has-reveal .is-summand-bleed .case-split-image img { transform: scale(1.12); transition: transform 1.6s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .is-summand-bleed .case-split-image.is-in img { transform: none; }

  /* Palette: the block itself stays put; each swatch paints upward in turn.
     Two delays because flex-grow (hover) must not wait for the stagger. */
  .has-reveal .is-palette[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .is-palette .color-hierarchy-cell { clip-path: inset(100% 0 0 0); transition: clip-path 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), flex-grow 0.5s ease; transition-delay: calc(var(--i, 0) * 90ms), 0s; }
  .has-reveal .is-palette.is-in .color-hierarchy-cell { clip-path: inset(0); }

  .is-gradient { background-size: 200% 200%; animation: is-drift 14s ease-in-out infinite alternate; }

  @media (hover: hover) {
    .is-version__tile img { transition: transform 0.35s ease; }
    .is-version:hover .is-version__tile img { transform: translateY(-4px); }
  }
  @media (hover: hover) and (min-width: 40rem) {
    .is-palette .color-hierarchy-cell { transition: flex-grow 0.5s ease; }
    .is-palette .color-hierarchy-row-primary .color-hierarchy-cell:hover,
    .is-palette .color-hierarchy-row-secondary .color-hierarchy-cell:hover { flex-grow: 1.6; }
  }
}
@keyframes is-settle { from { opacity: 0.4; transform: scale(1.08); } to { opacity: 1; transform: none; } }
@keyframes is-drift { from { background-position: 0% 50%; } to { background-position: 100% 50%; } }
@media (min-width: 40rem) { .is-gallery { grid-template-columns: 1fr 1fr; } }

/* Intelligible & Summand touch-up (2026-10-07): the cube beside "Keep the
   cube.", a soft Dark Cyan highlight on the strategy's key line, construction drawings
   drawn left to right, version tiles in sequence, a code caret after the mono
   sample, gallery clip reveals, a hover lift on the type glyphs. Same reduced-motion contract. */
.is-request__cube { display: inline-block; width: 0.62em; aspect-ratio: 115 / 130; margin-left: 0.3em; vertical-align: -0.04em; background: var(--is-teal); -webkit-mask: url("../assets/images/work/intelligible-summand/intelligible-logo-icon.svg") center / contain no-repeat; mask: url("../assets/images/work/intelligible-summand/intelligible-logo-icon.svg") center / contain no-repeat; }
.is-strategy__key { background: linear-gradient(color-mix(in srgb, var(--is-cyan) 32%, transparent), color-mix(in srgb, var(--is-cyan) 32%, transparent)) 0 88% / 100% 0.42em no-repeat; }
.is-caret { display: inline-block; width: 0.55em; height: 1em; margin-left: 0.15em; vertical-align: -0.12em; background: currentColor; }
@media (prefers-reduced-motion: no-preference) {
  .is-request__cube { animation: is-float 4s ease-in-out infinite; }

  .has-reveal .is-strategy__key[data-reveal] { opacity: 1; transform: none; background-size: 0% 0.42em; transition: background-size 1s cubic-bezier(0.6, 0, 0.2, 1) 0.3s; }
  .has-reveal .is-strategy__key.is-in { background-size: 100% 0.42em; }

  /* construction drawings draw in, left to right */
  .has-reveal .is-logo__structure[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 100% 0 0); transition: clip-path 1.4s cubic-bezier(0.6, 0, 0.2, 1) 0.15s; }
  .has-reveal .is-logo__structure.is-in { clip-path: inset(0); }

  /* version tiles open in sequence */
  .has-reveal .is-version[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .is-version .is-version__tile { clip-path: inset(100% 0 0 0); transition: clip-path 0.7s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .is-version[data-reveal-delay="1"] .is-version__tile { transition-delay: 0.12s; }
  .has-reveal .is-version[data-reveal-delay="2"] .is-version__tile { transition-delay: 0.24s; }
  .has-reveal .is-version[data-reveal-delay="3"] .is-version__tile { transition-delay: 0.36s; }
  .has-reveal .is-version.is-in .is-version__tile { clip-path: inset(0); }

  .is-caret { animation: is-blink 1.1s steps(1) infinite; }

  .has-reveal .is-gallery img[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 0 100% 0); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .is-gallery img[data-reveal-delay="1"] { transition-delay: 0.12s; }
  .has-reveal .is-gallery img.is-in { clip-path: inset(0); }

  @media (hover: hover) and (min-width: 40rem) {
    .is-type__glyphs { transition: transform 0.35s ease; }
    .specimen:hover .is-type__glyphs { transform: translateY(-6px); }
  }
}
@keyframes is-float { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-0.08em) rotate(-6deg); } }
@keyframes is-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* ==========================================================================
   Ellswood case study (article.cs-el)
   Client colours are declared inline on the article (--el-*). The idea is
   Golden Hour, so the page moves from daylight (Silk, Linen) into the low,
   warm end of the day (Sand, Bronze, Dark Chocolate). Headline samples use
   the client's Bricolage Grotesque, body samples Commissioner; the page's own
   H1/H2 stay in the site's Syne.
   ========================================================================== */
.el-prose p + p { margin-top: var(--space-3); }

/* Challenge: her "not anti-aging, but pro-skin" acted out (decorative,
   aria-hidden; the sentence itself is in the prose). */
.el-stance { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2em 0.5em; margin-top: var(--space-8); font-family: "Bricolage Grotesque", var(--font-body); font-weight: 600; font-size: clamp(2.6rem, 1.4rem + 6vw, 7rem); line-height: 1; letter-spacing: -0.02em; }
.el-stance__no { position: relative; color: var(--el-dsand); text-decoration: none; }
.el-stance__no::after { content: ""; position: absolute; left: -2%; right: -2%; top: 54%; height: 0.07em; background: var(--el-bronze); transform-origin: left; }
.el-stance__yes { color: var(--el-bronze); }

/* "Truly seen": 60/40 band on Linen, beside the portrait. The shared split
   rotates its photo below 64rem (made for a portrait-orientation shot
   elsewhere); this one is square, so it stays upright. */
.el-seen { background: var(--el-linen); color: var(--el-choc); }
.el-seen .case-split-text { justify-content: center; }
.el-seen .case-split-text p { color: var(--el-choc); }
.el-seen__line { max-width: 22ch; margin-top: 0; font-family: "Bricolage Grotesque", var(--font-body); font-weight: 500; font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.4rem); line-height: 1.2; letter-spacing: -0.01em; }
.el-seen__line + p { margin-top: var(--space-6); }
.el-seen .case-split-image img { transform: none; object-position: 50% 30%; }

/* Naming: three numbered steps, from the struck-out LuxManor to the wordmark. */
.el-steps { display: grid; gap: var(--space-6); margin: var(--space-8) 0 0; padding: 0; list-style: none; counter-reset: none; }
.el-step { position: relative; padding-top: var(--space-5); border-top: 1px solid var(--el-dsand); color: var(--el-choc); }
.el-step__num { display: block; font-family: "Bricolage Grotesque", var(--font-body); font-weight: 700; font-size: clamp(3.5rem, 2.5rem + 4vw, 6rem); line-height: 1; color: transparent; -webkit-text-stroke: 1.5px var(--el-dsand); }
.el-step__label { margin-top: var(--space-3); color: var(--el-bronze); font-size: var(--step--1); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; }
.el-step__label + p { margin-top: var(--space-3); }
.el-step__old { font-family: "Bricolage Grotesque", var(--font-body); font-weight: 500; font-size: var(--step-2); color: var(--el-dsand); }
.el-step__old s { position: relative; text-decoration: none; }
.el-step__old s::after { content: ""; position: absolute; left: -3%; right: -3%; top: 52%; height: 2px; background: var(--el-bronze); transform-origin: left; }
.el-step__mark { width: min(100%, 260px); margin-top: var(--space-4); }

.el-reasons { display: grid; gap: var(--space-5); margin: var(--space-8) 0 0; padding: var(--space-7) var(--space-6); border-radius: 16px; background: var(--el-silk); color: var(--el-choc); list-style: none; }
.el-reasons li { padding-top: var(--space-3); border-top: 2px solid var(--el-bronze); font-size: var(--step-1); line-height: 1.4; }

/* Logos are coloured with a mask, so one SVG serves every colour pair:
   --mark is the logo colour, the tile sets the background. */
.el-mark { display: block; background: var(--mark, var(--el-bronze)); }
.el-mark--wordmark { aspect-ratio: 915.64 / 170.96; -webkit-mask: url("../assets/images/work/ellswood/logo-wordmark.svg") center / contain no-repeat; mask: url("../assets/images/work/ellswood/logo-wordmark.svg") center / contain no-repeat; }
.el-mark--icon { aspect-ratio: 226.7 / 244.21; -webkit-mask: url("../assets/images/work/ellswood/logo-icon.svg") center / contain no-repeat; mask: url("../assets/images/work/ellswood/logo-icon.svg") center / contain no-repeat; }

/* Insight: Dark Chocolate, with a slow warm light that passes behind the
   line, like low sun. The second sentence is the one "in good light". */
.el-insight { position: relative; overflow: hidden; padding-block: 88px; background: var(--el-choc); color: var(--el-silk); }
.el-insight::before { content: ""; position: absolute; inset: -40% -20%; background: radial-gradient(closest-side, color-mix(in srgb, var(--el-bronze) 55%, transparent), transparent); opacity: 0.55; pointer-events: none; transform: translateX(-30%); }
.el-insight .container { position: relative; }
.el-chip { display: inline-block; width: fit-content; margin-bottom: var(--space-5); padding: var(--space-1) var(--space-3); border: 1px solid currentColor; border-radius: 999px; font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.el-chip--light { color: var(--el-sand); }
.el-insight__lead { max-width: 40rem; font-size: var(--step-1); line-height: 1.45; color: color-mix(in srgb, var(--el-silk) 82%, var(--el-choc)); }
.el-insight__line { max-width: 22ch; margin-top: var(--space-7); font-family: "Bricolage Grotesque", var(--font-body); font-weight: 600; font-size: clamp(1.9rem, 1.2rem + 3vw, 4rem); line-height: 1.1; letter-spacing: -0.02em; }
.el-insight__line span { display: block; margin-top: 0.25em; color: var(--el-sand); }

/* Strategy: a sky that runs from Silk down to Dark Sand, the sun setting
   behind the "Golden Hour" word, and the strategy text on the Bronze ground
   below the horizon. Static by default; the sun only sinks with motion on. */
.el-sky { position: relative; overflow: hidden; height: clamp(260px, 38vw, 520px); background: linear-gradient(180deg, var(--el-silk) 0%, var(--el-sand) 62%, var(--el-dsand) 100%); }
.el-sun { position: absolute; left: 50%; bottom: 0; width: clamp(180px, 30vw, 420px); aspect-ratio: 1; border-radius: 50%; background: radial-gradient(circle at 50% 40%, var(--el-sand) 0%, color-mix(in srgb, var(--el-bronze) 70%, var(--el-sand)) 55%, var(--el-bronze) 100%); box-shadow: 0 0 120px 40px color-mix(in srgb, var(--el-sand) 70%, transparent); transform: translate(-50%, 45%); }
.el-sky__word { position: absolute; inset-inline: 0; max-width: none; bottom: 18%; text-align: center; font-family: "Bricolage Grotesque", var(--font-body); font-weight: 700; font-size: clamp(2.4rem, 0.4rem + 8vw, 8rem); line-height: 1; letter-spacing: -0.03em; white-space: nowrap; color: var(--el-choc); mix-blend-mode: multiply; opacity: 0.85; }
.el-ground { padding-block: var(--space-8); background: var(--el-bronze); color: var(--el-silk); }
.el-ground h2 { color: var(--el-silk); }
.el-strategy__key { color: var(--el-sand); font-weight: 600; }

/* The Identity: 60/40 on Dark Chocolate beside the golden-hour eye. */
.el-identity { background: var(--el-choc); }
.el-identity .case-split-text { justify-content: center; }
.el-identity .case-split-text h2 { color: var(--el-sand); }
.el-identity__line.el-identity__line { max-width: 20ch; margin-top: var(--space-5); font-family: "Bricolage Grotesque", var(--font-body); font-weight: 500; font-size: clamp(1.6rem, 1.1rem + 2vw, 2.8rem); line-height: 1.15; letter-spacing: -0.01em; color: var(--el-silk); }
.el-identity .case-split-image img { transform: none; object-position: 50% 45%; }

/* Logo */
.el-logo { background: var(--el-silk); color: var(--el-choc); }
.el-logo h3, .el-colors h3, .el-type h3 { font-size: var(--step-2); }
.el-logo h3 { color: var(--el-choc); }
.el-logo__text { max-width: 44rem; margin-top: var(--space-4); }
.el-logo__stage { display: grid; place-items: center; padding-block: var(--space-8); }
.el-logo__wordmark { width: min(100%, 720px); }
.el-icon-row { display: grid; gap: var(--space-6); align-items: center; margin-top: var(--space-6); padding-top: var(--space-7); border-top: 1px solid var(--el-linen); }
.el-icon-row__tile { display: grid; place-items: center; width: clamp(140px, 16vw, 200px); aspect-ratio: 1; border-radius: 16px; background: var(--el-bronze); --mark: var(--el-silk); }
.el-icon-row__tile .el-mark { width: 44%; }
.el-icon-row__text { max-width: 36rem; }
.el-icon-row__social { display: block; width: 100%; height: auto; }

/* Colours: the shared full-bleed hierarchy, two rows only (no accent row in
   the main palette); the sub-brand accent pairs follow on Silk. */
.el-sublabel { margin-top: var(--space-6); color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.el-palette .color-hierarchy-row-primary { --row-share: 60; }
.el-palette .color-hierarchy-row-secondary { --row-share: 40; }
.el-palette { height: clamp(560px, 38vw, 720px); }
.el-subs { padding: var(--space-7) var(--page-inset) var(--space-8); }
.el-subs .el-sublabel { margin-top: 0; }
.el-subs__grid { display: grid; gap: var(--space-6); margin: var(--space-5) 0 0; padding: 0; list-style: none; }
.el-sub { padding: var(--space-6) var(--space-5) var(--space-5); border-radius: 16px; background: var(--el-silk); color: var(--el-choc); }
.el-sub__logo { display: block; width: 100%; max-width: 340px; height: auto; }
.el-sub__tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); margin-top: var(--space-6); }
.el-sub__tile { display: grid; place-items: center; aspect-ratio: 1; border-radius: 12px; }
.el-sub__tile .el-mark { width: 34%; }
.el-sub__name { margin-top: var(--space-5); font-weight: 600; }
.el-sub__pair { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); margin-top: var(--space-2); font-size: var(--step--1); }
.el-sub__pair span { display: inline-flex; align-items: center; gap: 0.5em; }
.el-sub__pair span::before { content: ""; width: 0.9em; height: 0.9em; border-radius: 50%; background: var(--sw); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--el-choc) 18%, transparent); }

/* Typefaces: three cards, each showing its weights and a line from the brand. */
.el-type__grid { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.el-face { display: flex; flex-direction: column; padding: var(--space-6) var(--space-5); border-radius: 16px; background: var(--el-silk); color: var(--el-choc); }
.el-face__role { color: var(--el-bronze); font-size: var(--step--1); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; }
.el-face__glyphs { display: flex; gap: 0.25em; margin-block: var(--space-4); font-size: clamp(3.5rem, 2.6rem + 3vw, 5.5rem); line-height: 1; color: var(--el-bronze); }
.el-face__name { font-weight: 600; font-size: var(--step-1); }
.el-face__weights { color: color-mix(in srgb, var(--el-choc) 75%, var(--el-silk)); font-size: var(--step--1); }
.el-face__sample { margin-top: auto; padding-top: var(--space-6); font-size: var(--step-1); line-height: 1.35; }
.el-face--head { font-family: "Bricolage Grotesque", var(--font-body); font-weight: 600; }
.el-face__sample.el-face--head { font-size: var(--step-2); line-height: 1.1; }
.el-face--sub { font-family: "Commissioner", var(--font-body); font-weight: 600; }
.el-face--body { font-family: "Commissioner", var(--font-body); font-weight: 400; }
.el-face__glyphs.el-face--head { font-size: clamp(2.6rem, 2rem + 2vw, 4rem); }

/* Result gallery: tote tall on the left, jar and laptop stacked beside it. */
.el-gallery { display: grid; grid-template-columns: 1fr; margin-top: var(--space-6); padding-block: 0; }
.el-gallery img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
.el-gallery__laptop { object-position: 50% 85%; }

@media (min-width: 40rem) {
  .el-insight { padding-block: 136px; }
  .el-reasons { padding: var(--space-7); }
}
@media (min-width: 48rem) {
  .el-steps { grid-template-columns: 1fr 1.4fr 1.2fr; gap: var(--space-6); }
  .el-reasons { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); }
  .el-icon-row { grid-template-columns: auto 1fr; }
  .el-icon-row__social { grid-column: 1 / -1; max-width: 560px; justify-self: end; }
  .el-subs__grid, .el-type__grid { grid-template-columns: repeat(3, 1fr); }
  .el-colors .color-hierarchy-intro-grid { grid-template-columns: 1fr 1fr; }
  .el-gallery { grid-template-columns: 1.15fr 1fr; grid-template-rows: auto auto; }
  /* left column 1.15 wide, right column two 4:3 images (1.5 tall in total):
     this ratio makes the cream exactly as tall as the stack beside it.
     Order: cream left, laptop top right, tote bottom right (her call). */
  .el-gallery .el-gallery__cream { grid-row: 1 / 3; aspect-ratio: 1.15 / 1.5; }
}
@media (min-width: 64rem) {
  .el-reasons { grid-template-columns: repeat(4, 1fr); }
  .el-icon-row { grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr); }
  .el-icon-row__social { grid-column: auto; max-width: none; }
}
.el-palette .color-hierarchy-row-secondary .color-hierarchy-cell { padding-inline: 24px; }
.el-palette .color-hierarchy-row-secondary .color-hierarchy-cell:first-child { padding-left: var(--page-inset); }
.el-palette .color-hierarchy-row-secondary .color-hierarchy-cell:last-child { padding-right: var(--page-inset); }
@media (max-width: 39.99rem) {
  /* four across is too narrow on a phone: two rows of two (as on Lux Flowers) */
  .el-palette { height: 640px; }
  .el-palette .color-hierarchy-row-secondary { flex-wrap: wrap; }
  .el-palette .color-hierarchy-row-secondary .color-hierarchy-cell { flex-basis: 50%; }
  .el-palette .color-hierarchy-row-secondary .color-hierarchy-cell:nth-child(odd) { padding-left: var(--page-inset); }
  .el-palette .color-hierarchy-name { font-size: var(--step-0); }
}

/* ---- Ellswood motion --------------------------------------------------------
   Same contract as above: hidden "before" states only behind .has-reveal,
   all of it off under prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .cs-el .case-hero-bleed { overflow: hidden; }
  .cs-el .case-hero-bleed img { animation: is-settle 1.8s cubic-bezier(0.2, 0.7, 0.2, 1) both; }

  /* the strike-throughs draw themselves once the line is in view */
  .has-reveal .el-stance__no::after,
  .has-reveal .el-step__old s::after { transform: scaleX(0); transition: transform 0.7s cubic-bezier(0.6, 0, 0.2, 1) 0.45s; }
  .has-reveal .el-stance.is-in .el-stance__no::after,
  .has-reveal .el-step.is-in .el-step__old s::after { transform: none; }
  .has-reveal .el-stance__yes { opacity: 0; transform: translateY(0.15em); transition: opacity 0.6s ease 1.05s, transform 0.6s ease 1.05s; }
  .has-reveal .el-stance.is-in .el-stance__yes { opacity: 1; transform: none; }

  /* the low sun drifts across the Insight band */
  .el-insight::before { animation: el-sweep 16s ease-in-out infinite alternate; }
  .has-reveal .el-insight__line span { opacity: 0.25; transition: opacity 1.2s ease 0.5s; }
  .has-reveal .el-insight__line.is-in span { opacity: 1; }

  /* the sun sets: starts high in the sky and sinks to the horizon */
  .has-reveal .el-sky[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .el-sun { transform: translate(-50%, -35%); transition: transform 2.6s cubic-bezier(0.25, 0.6, 0.25, 1) 0.2s; }
  .has-reveal .el-sky.is-in .el-sun { transform: translate(-50%, 45%); }

  .has-reveal .el-seen .case-split-image img,
  .has-reveal .el-identity .case-split-image img { transform: scale(1.1); transition: transform 1.6s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .el-seen .case-split-image.is-in img,
  .has-reveal .el-identity .case-split-image.is-in img { transform: none; }

  /* palette paints upward swatch by swatch (as on Intelligible) */
  .has-reveal .el-palette[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .el-palette .color-hierarchy-cell { clip-path: inset(100% 0 0 0); transition: clip-path 0.7s cubic-bezier(0.2, 0.7, 0.2, 1), flex-grow 0.5s ease; transition-delay: calc(var(--i, 0) * 90ms), 0s; }
  .has-reveal .el-palette.is-in .color-hierarchy-cell { clip-path: inset(0); }

  @media (hover: hover) and (min-width: 40rem) {
    .el-palette .color-hierarchy-cell { transition: flex-grow 0.5s ease; }
    .el-palette .color-hierarchy-cell:hover { flex-grow: 1.6; }
    .el-sub__tile { transition: transform 0.35s ease; }
    .el-sub:hover .el-sub__tile:last-child { transform: translateY(-4px); }
  }
}
@keyframes el-sweep { from { transform: translateX(-35%); } to { transform: translateX(35%); } }

/* ==========================================================================
   Nest Therapy for Kids case study (article.cs-nt)
   Client colours are declared inline on the article (--nt-*). The story runs
   from "a lot of blue" to joy: Rich Blue carries the trust moments (hero,
   Insight, logomark), Lemon/Orange/Carrot arrive as the warmth, and the
   tagline band uses the guidelines' Periwinkle-to-Rich-Blue gradient.
   Brand moments use the client's Playfair Display; the page's own H1/H2 stay
   in the site's Syne.
   ========================================================================== */
.nt-prose p + p { margin-top: var(--space-3); }
.nt-prose strong { font-weight: 600; }
.nt-h3 { font-size: var(--step-2); }
.nt-lead { max-width: 44rem; margin-top: var(--space-4); }

/* Hero: the reversed (light) primary logo on Rich Blue, a giant Periwinkle logomark
   turning slowly behind it (the swirl = movement, per her copy). */
.nt-hero { position: relative; overflow: hidden; display: grid; place-items: center; height: clamp(320px, 52vw, 640px); padding-block: 0; background: var(--nt-blue); }
.nt-hero__swirl { position: absolute; right: -14%; top: 50%; width: clamp(420px, 70vw, 1000px); height: auto; opacity: 0.35; transform: translateY(-50%); }
.nt-hero__logo { position: relative; width: min(76%, 640px); height: auto; }

/* Challenge: the founder's one strong wish, set apart on a Lemon rule. */
.nt-wish { max-width: 52rem; margin-top: var(--space-8); padding: var(--space-2) 0 var(--space-2) var(--space-6); border-left: 6px solid var(--nt-lemon); color: var(--nt-blue); font-size: var(--step-1); line-height: 1.45; }
.nt-wish strong { display: block; margin-top: var(--space-3); font-family: "Playfair Display", Georgia, serif; font-weight: 700; font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.4rem); line-height: 1.2; }

/* Research: text only (her call), on Platinum for a change of rhythm. */
.nt-research { background: var(--nt-platinum); color: var(--nt-ink); }
.nt-research h2 { color: var(--nt-blue); }

/* Insight: Rich Blue. The first sentence is the quiet one (everyone has
   trust); "happy to go" gets a Lemon hand-drawn underline. */
.nt-insight { position: relative; overflow: hidden; padding-block: 96px; background: var(--nt-blue); color: var(--nt-ivory); }
.nt-chip { display: inline-block; margin-bottom: var(--space-6); padding: var(--space-1) var(--space-3); border: 1px solid var(--nt-lemon); border-radius: 999px; color: var(--nt-lemon); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.nt-insight__line { max-width: 24ch; font-family: "Playfair Display", Georgia, serif; font-weight: 700; font-size: clamp(2rem, 1.1rem + 3.6vw, 4.4rem); line-height: 1.12; }
.nt-insight__line > span { display: block; }
.nt-insight__quiet { margin-bottom: 0.35em; color: color-mix(in srgb, var(--nt-ivory) 72%, var(--nt-blue)); font-weight: 600; font-size: 0.62em; }
.nt-scribble { position: relative; display: inline-block; color: var(--nt-lemon); }
.nt-scribble svg { position: absolute; left: -2%; bottom: -0.16em; width: 104%; height: 0.32em; overflow: visible; }
.nt-scribble path { fill: none; stroke: var(--nt-lemon); stroke-width: 5; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.nt-insight__body { max-width: 40rem; margin-top: var(--space-8); margin-left: auto; font-size: var(--step-1); line-height: 1.5; color: color-mix(in srgb, var(--nt-ivory) 88%, var(--nt-blue)); }

/* Direction: the four values, then the hue shift. */
.nt-direction { background: var(--nt-ivory); color: var(--nt-ink); }
.nt-direction h2 { color: var(--nt-blue); }
.nt-values { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5) var(--space-6); margin: var(--space-8) 0 0; padding: var(--space-6) 0; border-block: 1px solid color-mix(in srgb, var(--nt-blue) 25%, transparent); list-style: none; }
.nt-values li { font-family: "Playfair Display", Georgia, serif; font-weight: 700; font-size: clamp(1.8rem, 1.2rem + 2.4vw, 3.4rem); line-height: 1; color: var(--nt-blue); }
.nt-values li:nth-child(2) { color: var(--nt-carrot); }
.nt-shift { display: grid; gap: var(--space-7); align-items: center; margin-top: var(--space-8); }
.nt-shift__text p { max-width: 36rem; font-size: var(--step-1); line-height: 1.5; }
.nt-shift__fig { margin: 0; }
.nt-shift__track { position: relative; display: grid; grid-template-columns: 3fr auto 1fr; align-items: center; gap: var(--space-4); }
/* illustrative category range, sky to navy (not measured data) */
.nt-shift__band { height: 72px; border-radius: 12px; background: linear-gradient(90deg, #a9cdf2, #4f8fd6 45%, #1f4f8f 75%, #12284e); }
.nt-shift__arrow { width: 40px; height: 2px; background: var(--nt-blue); position: relative; }
.nt-shift__arrow::after { content: ""; position: absolute; right: -1px; top: -5px; border: 6px solid transparent; border-left: 9px solid var(--nt-blue); border-right: 0; }
.nt-shift__nest { height: 72px; border-radius: 12px; background: var(--nt-blue); box-shadow: 0 0 0 4px var(--nt-ivory), 0 0 0 6px var(--nt-peri); }
.nt-shift__labels { display: grid; grid-template-columns: 3fr 1fr; gap: calc(40px + 2 * var(--space-4)); margin-top: var(--space-3); font-size: var(--step--1); color: color-mix(in srgb, var(--nt-ink) 75%, var(--nt-ivory)); }
.nt-shift__labels span:last-child { color: var(--nt-blue); font-weight: 600; }
.nt-shift__warm { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-5); }
.nt-shift__warm span { width: 40px; height: 40px; border-radius: 50%; }
.nt-shift__warm em { margin-left: var(--space-2); font-family: "Playfair Display", Georgia, serif; font-style: normal; font-weight: 700; font-size: var(--step-1); color: var(--nt-carrot); }

/* Tagline band: the guidelines' closing gradient. Nest (logomark) and bird
   (the flying mascot) side by side. */
.nt-tagline { position: relative; overflow: hidden; padding-block: 96px; background: linear-gradient(135deg, var(--nt-peri) 0%, var(--nt-blue) 70%); color: var(--nt-ivory); }
.nt-tagline__intro { max-width: 40rem; font-size: var(--step-1); color: var(--nt-ivory); }
.nt-tagline__big { max-width: none; margin-top: var(--space-6); font-family: "Playfair Display", Georgia, serif; font-weight: 800; font-size: clamp(2.6rem, 1rem + 6.4vw, 7.5rem); line-height: 1.02; letter-spacing: -0.01em; }
.nt-tagline__big span { display: block; }
.nt-tagline__big span + span { color: var(--nt-lemon); }
.nt-pair { display: grid; gap: var(--space-6); margin-top: var(--space-8); }
.nt-pair__item { display: grid; grid-template-columns: 96px 1fr; gap: var(--space-5); align-items: center; padding-top: var(--space-5); border-top: 1px solid color-mix(in srgb, var(--nt-ivory) 35%, transparent); }
.nt-pair__item img { width: 96px; height: auto; }
.nt-pair__item p { font-size: var(--step-1); line-height: 1.4; color: var(--nt-ivory); }
.nt-pair__item strong { font-family: "Playfair Display", Georgia, serif; font-weight: 700; color: var(--nt-lemon); }

/* Logo */
.nt-logo { background: var(--nt-ivory); color: var(--nt-ink); }
.nt-logo h2, .nt-logo .nt-h3 { color: var(--nt-blue); }
.nt-logo .nt-h3 { margin-top: var(--space-6); }
.nt-mark { display: grid; gap: var(--space-7); align-items: center; margin-top: var(--space-8); }
.nt-mark__stage { display: grid; place-items: center; aspect-ratio: 1; max-width: 440px; width: 100%; border-radius: 50%; background: var(--nt-blue); }
.nt-petals { width: 62%; height: auto; overflow: visible; }
.nt-petals path { fill: var(--nt-peri); transform-box: view-box; transform-origin: 153.2px 151px; }
.nt-mark__text p { max-width: 34rem; font-size: var(--step-1); line-height: 1.5; }
.nt-construction { margin: var(--space-8) 0 0; padding: clamp(24px, 4vw, 56px); border: 1px solid color-mix(in srgb, var(--nt-blue) 14%, transparent); border-radius: 16px; }
.nt-construction img { display: block; width: 100%; height: auto; }
.nt-system__intro { margin-top: var(--space-8); }
.nt-system { display: grid; gap: var(--space-4); margin: var(--space-6) 0 0; padding: 0; list-style: none; }
.nt-system__tile { display: flex; flex-direction: column; padding: var(--space-5); border-radius: 16px; background: var(--nt-platinum); }
.nt-system__art { display: grid; place-items: center; min-height: 180px; padding-block: var(--space-5); }
.nt-system__art img { width: min(88%, 300px); height: auto; }
.nt-system__tile--wide .nt-system__art img { width: min(88%, 460px); }
.nt-system__art .nt-system__v1 { width: min(60%, 170px); }
.nt-system__art .nt-system__mark { width: 96px; }
.nt-system__tile--dark { background: var(--nt-blue); }
.nt-system__name { margin-top: auto; font-weight: 600; color: var(--nt-blue); }
.nt-system__use { font-size: var(--step--1); color: color-mix(in srgb, var(--nt-ink) 75%, var(--nt-platinum)); }
.nt-system__tile--dark .nt-system__name { color: var(--nt-ivory); }
.nt-system__tile--dark .nt-system__use { color: color-mix(in srgb, var(--nt-ivory) 82%, var(--nt-blue)); }

/* Colours: full-bleed mosaic. Rich Blue leads, the warm three sit together
   as the joy row, Ivory and Platinum close. */
.nt-colors { padding-block: var(--space-8) 0; }
.nt-palette { display: grid; grid-template-columns: repeat(2, 1fr); margin-top: var(--space-7); }
.nt-swatch { display: flex; flex-direction: column; justify-content: flex-end; min-height: 150px; padding: var(--space-5) var(--page-inset); color: var(--nt-blue); }
.nt-swatch__name { font-weight: 600; font-size: var(--step-1); }
.nt-swatch__name span { margin-left: 0.4em; font-weight: 400; font-size: var(--step--1); }
.nt-swatch__codes { margin-top: var(--space-1); font-size: var(--step--1); line-height: 1.5; }
.nt-swatch--blue { grid-column: 1 / -1; min-height: 260px; background: var(--nt-blue); color: var(--nt-ivory); }
.nt-swatch--peri { background: var(--nt-peri); color: var(--nt-ink); }
.nt-swatch--lemon { background: var(--nt-lemon); }
.nt-swatch--orange { background: var(--nt-orange); color: var(--nt-ink); }
.nt-swatch--carrot { background: var(--nt-carrot); color: var(--nt-ink); }
.nt-swatch--ivory { background: var(--nt-ivory); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--nt-blue) 12%, transparent); }
.nt-swatch--platinum { background: var(--nt-platinum); }

/* Typefaces */
.nt-type__grid { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.nt-face { display: flex; flex-direction: column; padding: var(--space-6) var(--space-5); border-radius: 16px; background: var(--nt-platinum); color: var(--nt-blue); }
.nt-face__role { font-size: var(--step--1); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--nt-blue); }
.nt-face__glyphs { display: flex; gap: 0.3em; margin-block: var(--space-4); font-size: clamp(3rem, 2.2rem + 3vw, 5rem); line-height: 1; color: var(--nt-peri); }
.nt-face__name { font-weight: 600; font-size: var(--step-1); }
.nt-face__weights { font-size: var(--step--1); color: color-mix(in srgb, var(--nt-ink) 75%, var(--nt-platinum)); }
.nt-face__sample { margin-top: auto; padding-top: var(--space-6); font-size: var(--step-1); line-height: 1.4; color: var(--nt-ink); }
.nt-face--playfair .nt-face__glyphs, .nt-face--playfair .nt-face__sample { font-family: "Playfair Display", Georgia, serif; }
.nt-face--playfair .nt-face__sample { font-weight: 800; font-size: var(--step-3); line-height: 1.1; color: var(--nt-blue); }
.nt-face--inter .nt-face__glyphs, .nt-face--inter .nt-face__sample { font-family: "Inter", var(--font-body); }
.nt-face--inter .nt-face__sample { font-weight: 300; }
.nt-face--inter .nt-face__sample strong { font-weight: 600; color: var(--nt-blue); }

/* Mascot: the real goldfinch beside the drawn one, on Periwinkle (as on the
   guidelines' mascot page). */
.nt-mascot { padding-block: var(--space-8); background: var(--nt-peri); color: var(--nt-ink); }
.nt-mascot .nt-h3 { color: var(--nt-ink); }
.nt-mascot__grid { display: grid; gap: var(--space-7); align-items: center; }
.nt-mascot__text > p { max-width: 36rem; margin-top: var(--space-4); }
.nt-mascot__ref { display: grid; grid-template-columns: 88px 1fr; gap: var(--space-1) var(--space-4); align-items: center; margin-top: var(--space-7); padding: var(--space-4); border-radius: 16px; background: var(--nt-ivory); max-width: 26rem; }
.nt-mascot__ref img { grid-row: 1 / 3; width: 88px; height: 88px; border-radius: 50%; object-fit: cover; }
.nt-mascot__ref p { font-weight: 600; color: var(--nt-blue); }
.nt-mascot__ref p span { display: block; font-weight: 400; font-size: var(--step--1); color: color-mix(in srgb, var(--nt-ink) 75%, var(--nt-ivory)); }
.nt-mascot__swatch { display: flex; align-items: center; gap: var(--space-2); }
.nt-mascot__swatch::before { content: ""; order: 2; width: 18px; height: 18px; border-radius: 50%; background: var(--nt-lemon); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--nt-ink) 15%, transparent); }
.nt-mascot__swatch span { order: 0; }
.nt-mascot__stage { position: relative; display: grid; place-items: center; }
.nt-mascot__stage::before { content: ""; position: absolute; width: 82%; aspect-ratio: 1; border-radius: 50%; background: radial-gradient(circle, color-mix(in srgb, var(--nt-ivory) 35%, transparent), transparent 70%); }
.nt-mascot__stage img { position: relative; width: min(100%, 520px); height: auto; }

/* Result gallery, two rows (her layout): the shirt beside the sign and
   stickers stacked, then the pins full width. */
.nt-gallery { display: grid; grid-template-columns: 1fr; margin-top: var(--space-6); padding-block: 0; }
.nt-gallery img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
.nt-gallery__shirt { object-position: 50% 30%; }

@media (min-width: 40rem) {
  .nt-insight, .nt-tagline { padding-block: 136px; }
  .nt-values { grid-template-columns: repeat(4, auto); justify-content: space-between; }
  .nt-palette { grid-template-columns: repeat(6, 1fr); }
  .nt-swatch { min-height: 200px; padding-inline: var(--space-5); }
  .nt-swatch--blue { grid-column: span 4; min-height: 320px; padding-left: var(--page-inset); }
  .nt-swatch--peri { grid-column: span 2; min-height: 320px; padding-right: var(--page-inset); }
  .nt-swatch--lemon, .nt-swatch--orange, .nt-swatch--carrot { grid-column: span 2; }
  .nt-swatch--lemon, .nt-swatch--ivory { padding-left: var(--page-inset); }
  .nt-swatch--ivory, .nt-swatch--platinum { grid-column: span 3; min-height: 140px; }
}
@media (min-width: 48rem) {
  .nt-shift { grid-template-columns: 1fr 1.1fr; gap: var(--space-8); }
  .nt-pair { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
  .nt-pair__item { grid-template-columns: 120px 1fr; }
  .nt-pair__item img { width: 120px; }
  .nt-mark { grid-template-columns: minmax(0, 440px) 1fr; gap: var(--space-8); }
  .nt-system { grid-template-columns: repeat(3, 1fr); }
  .nt-system__tile--wide { grid-column: 1 / -1; }
  .nt-type__grid { grid-template-columns: 1.2fr 1fr; }
  .nt-mascot__grid { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
  .nt-gallery { grid-template-columns: 1fr 1fr; }
  /* two 3:2 images stacked on the right are 4/3 of the column width tall,
     so the shirt beside them is 3:4 */
  .nt-gallery .nt-gallery__shirt { grid-row: 1 / 3; aspect-ratio: 3 / 4; }
  .nt-gallery .nt-gallery__sign, .nt-gallery .nt-gallery__stickers { aspect-ratio: 3 / 2; }
  .nt-gallery .nt-gallery__pins { grid-column: 1 / -1; aspect-ratio: 16 / 9; object-position: 50% 80%; }
}
@media (min-width: 64rem) {
  .nt-system { grid-template-columns: 1.6fr 1fr 1fr 1fr; }
  .nt-system__tile--wide { grid-column: auto; }
}

/* ---- Nest motion -------------------------------------------------------------
   Same contract as above: hidden "before" states only behind .has-reveal,
   all of it off under prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .nt-hero__swirl { animation: nt-turn 90s linear infinite; }
  .has-reveal .nt-hero__logo { animation: is-settle 1.4s cubic-bezier(0.2, 0.7, 0.2, 1) both; }

  /* the Lemon underline draws itself under "happy to go" */
  .has-reveal .nt-scribble path { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 0.9s cubic-bezier(0.6, 0, 0.2, 1) 0.7s; }
  .has-reveal .nt-insight__line.is-in .nt-scribble path { stroke-dashoffset: 0; }

  /* the shift: the category band appears, then Rich Blue steps out of it */
  .has-reveal .nt-shift__fig[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .nt-shift__band { clip-path: inset(0 100% 0 0); transition: clip-path 0.9s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .nt-shift__arrow, .has-reveal .nt-shift__nest, .has-reveal .nt-shift__warm span, .has-reveal .nt-shift__warm em { opacity: 0; transform: translateX(-12px); transition: opacity 0.5s ease, transform 0.5s ease; }
  .has-reveal .nt-shift__arrow { transition-delay: 0.8s; }
  .has-reveal .nt-shift__nest { transition-delay: 1s; }
  .has-reveal .nt-shift__warm span:nth-child(1) { transition-delay: 1.3s; }
  .has-reveal .nt-shift__warm span:nth-child(2) { transition-delay: 1.4s; }
  .has-reveal .nt-shift__warm span:nth-child(3) { transition-delay: 1.5s; }
  .has-reveal .nt-shift__warm em { transition-delay: 1.65s; }
  .has-reveal .nt-shift__fig.is-in .nt-shift__band { clip-path: inset(0); }
  .has-reveal .nt-shift__fig.is-in :is(.nt-shift__arrow, .nt-shift__nest, .nt-shift__warm span, .nt-shift__warm em) { opacity: 1; transform: none; }

  /* the bird keeps flying, gently */
  .nt-pair__bird { animation: nt-hover 3.2s ease-in-out infinite alternate; }

  /* the petals gather into the nest, one after another */
  .has-reveal .nt-mark__stage[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .nt-petals path { opacity: 0; transform: rotate(-70deg) scale(1.5); transition: opacity 0.6s ease, transform 1.1s cubic-bezier(0.2, 0.7, 0.2, 1); transition-delay: calc(var(--p) * 70ms + 0.15s); }
  .has-reveal .nt-mark__stage.is-in .nt-petals path { opacity: 1; transform: none; }

  /* palette paints upward swatch by swatch (as on Ellswood) */
  .has-reveal .nt-palette[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .nt-swatch { clip-path: inset(100% 0 0 0); transition: clip-path 0.7s cubic-bezier(0.2, 0.7, 0.2, 1); transition-delay: calc(var(--i, 0) * 80ms); }
  .has-reveal .nt-palette.is-in .nt-swatch { clip-path: inset(0); }

  @media (hover: hover) {
    .nt-mark__stage .nt-petals { transition: transform 1.2s cubic-bezier(0.2, 0.7, 0.2, 1); }
    .nt-mark__stage:hover .nt-petals { transform: rotate(36deg); }
    .nt-mascot__stage img { transition: transform 0.5s cubic-bezier(0.3, 1.6, 0.5, 1); }
    .nt-mascot__stage:hover img { transform: translateY(-10px) rotate(-3deg); }
  }
}
@keyframes nt-turn { from { transform: translateY(-50%) rotate(0deg); } to { transform: translateY(-50%) rotate(360deg); } }
@keyframes nt-hover { from { transform: translateY(4px) rotate(-2deg); } to { transform: translateY(-8px) rotate(2deg); } }

/* ==========================================================================
   Safer Gambling Strategies case study (article.cs-sgs)
   Free redesign (her call, 2026-10-06; no longer mirrors the studio site). The
   idea is the maze: the navy concept band, the 11x11 logomark that builds
   itself with an Old Gold route traced through its corridors, then the calm,
   institutional system (configurations, scale, palette, type). Client
   colours are inline on the article (--sgs-*); client fonts are Plus Jakarta
   Sans (ExtraBold) and Inter (Regular). Page H1/H2 stay in the site's Syne.
   ========================================================================== */
.sgs-prose p { max-width: 38rem; font-size: var(--step-0); line-height: 1.65; }
.sgs-h3 { font-size: var(--step-2); }
.sgs-lead { max-width: 44rem; margin-top: var(--space-4); }

/* Overview + Challenge: heading column, prose beside it; then the
   category's defaults struck out, "Authoritative." left standing. */
.sgs-intro__grid { display: grid; gap: var(--space-5); }
.sgs-intro__grid--challenge { margin-top: var(--space-8); padding-top: var(--space-8); border-top: 1px solid var(--secondary); }
.sgs-stance { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.15em 0.45em; margin-top: var(--space-8); font-family: "Plus Jakarta Sans", var(--font-body); font-weight: 800; font-size: clamp(2.2rem, 1rem + 5.4vw, 6rem); line-height: 1; letter-spacing: -0.03em; }
.sgs-stance__no { position: relative; color: color-mix(in srgb, var(--sgs-grey) 55%, var(--sgs-seasalt)); text-decoration: none; }
.sgs-stance__no::after { content: ""; position: absolute; left: -2%; right: -2%; top: 52%; height: 0.07em; background: var(--sgs-grey); transform-origin: left; }
.sgs-stance__yes { color: var(--sgs-marian); }

/* Concept: navy, full-bleed split; the stairwell photo stays upright. */
.sgs-concept { display: grid; background: var(--sgs-navy); color: var(--sgs-seasalt); }
.sgs-concept__text { display: flex; flex-direction: column; justify-content: center; padding: 72px var(--page-inset); }
.sgs-concept__text h2 { color: var(--sgs-grey); font-size: var(--step-3); }
.sgs-concept__text p { max-width: 34rem; margin-top: var(--space-5); font-size: var(--step-1); line-height: 1.5; color: var(--sgs-seasalt); }
.sgs-concept__wordmark { width: min(70%, 360px); height: auto; margin-top: var(--space-8); }
.sgs-concept__image { overflow: hidden; }
.sgs-concept__image img { display: block; width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

/* Geometry: the maze on navy, built from its grid, with the route traced. */
.sgs-geometry { padding-block: var(--space-8) 96px; background: var(--sgs-navy); color: var(--sgs-seasalt); }
.sgs-geometry__grid { display: grid; gap: var(--space-7); align-items: center; }
.sgs-maze { margin: 0; width: min(100%, 520px); }
.sgs-maze svg { display: block; width: 100%; height: auto; overflow: visible; }
.sgs-maze__grid line { stroke: var(--sgs-grey); stroke-width: 0.6; opacity: 0.5; }
.sgs-maze__mark { fill: var(--sgs-alice); }
.sgs-maze__route { fill: none; stroke: var(--sgs-gold); stroke-width: 6; stroke-linecap: round; stroke-linejoin: round; }
.sgs-maze__start { fill: var(--sgs-navy); stroke: var(--sgs-gold); stroke-width: 3; }
.sgs-maze__end { fill: var(--sgs-gold); }
.sgs-geometry__units { display: flex; align-items: baseline; gap: 0.2em; margin-bottom: var(--space-5); font-family: "Plus Jakarta Sans", var(--font-body); font-weight: 800; font-size: clamp(3rem, 2rem + 4vw, 6rem); line-height: 1; letter-spacing: -0.03em; color: var(--sgs-grey); }
.sgs-geometry__units span { color: var(--sgs-seasalt); }
.sgs-geometry__text > p:not(.sgs-geometry__units) { max-width: 32rem; font-size: var(--step-1); line-height: 1.5; color: var(--sgs-seasalt); }
.sgs-geometry__text strong { color: var(--sgs-grey); font-weight: 600; }
.sgs-legend { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); margin: var(--space-6) 0 0; padding: 0; list-style: none; font-size: var(--step--1); color: var(--sgs-alice); }
.sgs-legend li { display: inline-flex; align-items: center; gap: 0.6em; }
.sgs-legend li::before { content: ""; width: 28px; }
.sgs-legend__grid::before { height: 14px; background: repeating-linear-gradient(90deg, var(--sgs-grey) 0 1px, transparent 1px 7px); opacity: 0.8; }
.sgs-legend__route::before { height: 4px; border-radius: 2px; background: var(--sgs-gold); }

/* Logo hierarchy: three configurations, then the emblem at real sizes from
   15px up, beside the embroidered polo. */
.sgs-logos { background: var(--sgs-alice); color: var(--sgs-navy); }
.sgs-logos .sgs-h3 { color: var(--sgs-navy); }
.sgs-logos .sgs-lead { color: var(--sgs-navy); }
.sgs-configs { display: grid; gap: var(--space-4); margin: var(--space-7) 0 0; padding: 0; list-style: none; }
.sgs-config { display: flex; flex-direction: column; padding: var(--space-5); border-radius: 16px; background: var(--sgs-seasalt); }
.sgs-config__art { display: grid; place-items: center; min-height: 200px; padding-block: var(--space-5); }
.sgs-config__art img { width: min(90%, 340px); height: auto; }
.sgs-config__art .sgs-config__short { width: 96px; }
.sgs-config__art .sgs-config__mark { width: 104px; }
.sgs-config__name { display: flex; gap: var(--space-3); align-items: baseline; padding-top: var(--space-4); border-top: 1px solid var(--sgs-alice); font-weight: 600; color: var(--sgs-marian); }
.sgs-config__name span { font-size: var(--step--1); font-weight: 400; color: var(--sgs-marian); opacity: 0.75; }
.sgs-scale { display: grid; gap: var(--space-6); align-items: end; margin-top: var(--space-8); }
.sgs-ladder { margin: 0; overflow-x: auto; }
.sgs-ladder ol { display: flex; align-items: flex-end; gap: clamp(16px, 3vw, 40px); margin: 0; padding: 0 0 var(--space-2); list-style: none; }
.sgs-ladder li { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); flex: none; }
.sgs-ladder img { width: var(--s); height: var(--s); }
.sgs-ladder span { font-size: var(--step--1); color: var(--sgs-marian); font-variant-numeric: tabular-nums; }
.sgs-ladder__big img { width: clamp(120px, 14vw, 200px); height: auto; }
.sgs-scale__polo { display: block; width: 100%; height: auto; border-radius: 16px; }
@media (max-width: 39.99rem) {
  /* phones: drop the 120px step rather than shrink it, so every px label
     stays true to the size actually drawn */
  .sgs-ladder .sgs-ladder__step4 { display: none; }
}

/* Colours: three tiers that step down in height (primary, secondary,
   accent), each with its sentence on the left: the hierarchy is the layout. */
.sgs-colors { padding-block: var(--space-8) 0; }
.sgs-palette { margin-top: var(--space-6); }
.sgs-tier { display: grid; grid-template-columns: 1fr 1fr; }
.sgs-tier__text { grid-column: 1 / -1; padding: var(--space-5) var(--page-inset) var(--space-3); font-size: var(--step--1); line-height: 1.45; color: var(--text); }
.sgs-tier__text span { display: block; margin-bottom: var(--space-1); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--sgs-marian); }
.sgs-swatch { display: flex; flex-direction: column; justify-content: flex-end; padding: var(--space-4) var(--page-inset); color: var(--sgs-marian); }
.sgs-swatch__name { font-weight: 600; font-size: var(--step-1); }
.sgs-swatch__codes { margin-top: var(--space-1); font-size: var(--step--1); line-height: 1.5; }
.sgs-tier--primary .sgs-swatch { min-height: 220px; }
.sgs-tier--secondary .sgs-swatch { min-height: 150px; }
.sgs-tier--accent .sgs-swatch { grid-column: 1 / -1; min-height: 88px; }
.sgs-tier--accent .sgs-swatch__name { font-size: var(--step-0); }
.sgs-swatch--marian { background: var(--sgs-marian); color: var(--sgs-seasalt); }
.sgs-swatch--seasalt { background: var(--sgs-seasalt); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sgs-marian) 12%, transparent); }
.sgs-swatch--alice { background: var(--sgs-alice); }
.sgs-swatch--grey { background: var(--sgs-grey); color: var(--sgs-navy); }
.sgs-swatch--gold { background: var(--sgs-gold); color: var(--sgs-navy); }

/* Typography: the headline face on navy, the body face on Alice Blue. */
.sgs-type__grid { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
.sgs-face { display: flex; flex-direction: column; padding: var(--space-6) var(--space-5); border-radius: 16px; }
.sgs-face__label { font-size: var(--step--1); font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; }
.sgs-face__glyphs { margin-block: var(--space-4); font-size: clamp(4rem, 3rem + 4vw, 7rem); line-height: 1; letter-spacing: -0.03em; }
.sgs-face__note { max-width: 30rem; font-size: var(--step-0); }
.sgs-face__sample { margin-top: auto; padding-top: var(--space-7); }
.sgs-face--jakarta { background: var(--sgs-navy); color: var(--sgs-seasalt); }
.sgs-face--jakarta .sgs-face__label { color: var(--sgs-grey); }
.sgs-face--jakarta .sgs-face__note { color: var(--sgs-alice); }
.sgs-face--jakarta .sgs-face__glyphs, .sgs-face--jakarta .sgs-face__sample { font-family: "Plus Jakarta Sans", var(--font-body); font-weight: 800; }
.sgs-face--jakarta .sgs-face__glyphs { color: var(--sgs-grey); }
.sgs-face--jakarta .sgs-face__sample { font-size: clamp(1.8rem, 1.2rem + 2vw, 2.8rem); line-height: 1.08; letter-spacing: -0.02em; color: var(--sgs-seasalt); }
.sgs-face--inter { background: var(--sgs-alice); color: var(--sgs-navy); }
.sgs-face--inter .sgs-face__label { color: var(--sgs-marian); }
.sgs-face--inter .sgs-face__glyphs, .sgs-face--inter .sgs-face__sample { font-family: "Inter", var(--font-body); font-weight: 400; }
.sgs-face--inter .sgs-face__glyphs { color: var(--sgs-marian); }
.sgs-face--inter .sgs-face__sample { font-size: var(--step-1); line-height: 1.45; color: var(--sgs-marian); }

/* Gallery: a bento. Presentation wide, cards square, the folder tall on the
   right; pin and signage below. */
.sgs-gallery { display: grid; grid-template-columns: 1fr; margin-top: var(--space-8); padding-block: 0; }
.sgs-gallery img { display: block; width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.sgs-gallery__signage { object-position: 50% 40%; }

/* Testimonial on the light page (her call), a faint Alice Blue logomark
   behind it, the opening quote mark in Old Gold. */
.sgs-quote { position: relative; overflow: hidden; }
.sgs-quote::after { content: ""; position: absolute; right: -6%; top: 50%; width: clamp(260px, 36vw, 520px); aspect-ratio: 1; background: var(--sgs-alice); transform: translateY(-50%); -webkit-mask: url("../assets/images/work/safer-gambling-strategies/logo-mark-blue.svg") center / contain no-repeat; mask: url("../assets/images/work/safer-gambling-strategies/logo-mark-blue.svg") center / contain no-repeat; pointer-events: none; }
.sgs-quote .container { position: relative; z-index: 1; }
.sgs-quote .testimonial { border-color: var(--sgs-alice); background: var(--sgs-seasalt); }
.sgs-quote .testimonial .quote-mark { color: var(--sgs-gold); }
.sgs-quote .testimonial__by strong { color: var(--sgs-marian); }

@media (min-width: 40rem) {
  /* text column, then the tier's swatches; heights step down 2 : 1.3 : 0.6 */
  .sgs-tier { grid-template-columns: minmax(220px, 28%) 1fr 1fr; }
  .sgs-tier__text { grid-column: auto; align-self: center; padding: var(--space-5) var(--space-6) var(--space-5) var(--page-inset); }
  .sgs-swatch { padding: var(--space-5); }
  .sgs-swatch:last-child { padding-right: var(--page-inset); }
  .sgs-tier--primary .sgs-swatch { min-height: clamp(260px, 26vw, 380px); }
  .sgs-tier--secondary .sgs-swatch { min-height: clamp(170px, 16vw, 240px); }
  .sgs-tier--accent .sgs-swatch { grid-column: 2 / 3; min-height: clamp(84px, 7vw, 110px); }
}
@media (min-width: 48rem) {
  .sgs-intro__grid { grid-template-columns: 1fr 1.25fr; gap: var(--space-8); }
  .sgs-concept { grid-template-columns: 1fr 1fr; }
  .sgs-concept__text { padding-block: 112px; padding-right: var(--space-8); }
  .sgs-concept__image img { aspect-ratio: auto; min-height: 560px; }
  .sgs-geometry__grid { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
  .sgs-configs { grid-template-columns: 1.6fr 1fr 1fr; }
  .sgs-scale { grid-template-columns: 1.3fr 1fr; gap: var(--space-8); }
  .sgs-type__grid { grid-template-columns: 1.15fr 1fr; }
  .sgs-gallery { grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(2, clamp(260px, 26vw, 420px)); }
  .sgs-gallery img { aspect-ratio: auto; }
  .sgs-gallery .sgs-gallery__presentation { grid-column: 1 / 3; }
  .sgs-gallery .sgs-gallery__folder { grid-column: 4; grid-row: 1 / 3; }
  .sgs-gallery .sgs-gallery__pin { grid-column: 1; grid-row: 2; }
  .sgs-gallery .sgs-gallery__signage { grid-column: 2 / 4; grid-row: 2; }
}

/* ---- SGS motion --------------------------------------------------------------
   Same contract as above: hidden "before" states only behind .has-reveal,
   all of it off under prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .cs-sgs .case-hero-bleed { overflow: hidden; }
  .cs-sgs .case-hero-bleed img { animation: is-settle 1.8s cubic-bezier(0.2, 0.7, 0.2, 1) both; }

  /* the category's defaults get struck out, one after the other */
  .has-reveal .sgs-stance__no::after { transform: scaleX(0); transition: transform 0.6s cubic-bezier(0.6, 0, 0.2, 1) 0.4s; }
  .has-reveal .sgs-stance__no + .sgs-stance__no::after { transition-delay: 0.85s; }
  .has-reveal .sgs-stance.is-in .sgs-stance__no::after { transform: none; }
  .has-reveal .sgs-stance__yes { opacity: 0; transform: translateY(0.15em); transition: opacity 0.6s ease 1.4s, transform 0.6s ease 1.4s; }
  .has-reveal .sgs-stance.is-in .sgs-stance__yes { opacity: 1; transform: none; }

  .has-reveal .sgs-concept__image img { transform: scale(1.12); transition: transform 1.8s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .sgs-concept__image.is-in img { transform: none; }

  /* the maze builds: grid, then walls, then the route finds its way through */
  .has-reveal .sgs-maze[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .sgs-maze__grid { opacity: 0; transition: opacity 0.8s ease; }
  .has-reveal .sgs-maze__mark { clip-path: inset(0 100% 0 0); transition: clip-path 1s cubic-bezier(0.6, 0, 0.2, 1) 0.5s; }
  .has-reveal .sgs-maze__route { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 2.6s cubic-bezier(0.45, 0, 0.25, 1) 1.5s; }
  .has-reveal .sgs-maze__start { opacity: 0; transition: opacity 0.3s ease 1.4s; }
  .has-reveal .sgs-maze__end { opacity: 0; transform: scale(0); transform-box: fill-box; transform-origin: center; transition: opacity 0.2s ease 4.05s, transform 0.5s cubic-bezier(0.3, 1.8, 0.5, 1) 4.05s; }
  .has-reveal .sgs-maze.is-in .sgs-maze__grid { opacity: 1; }
  .has-reveal .sgs-maze.is-in .sgs-maze__mark { clip-path: inset(0); }
  .has-reveal .sgs-maze.is-in .sgs-maze__route { stroke-dashoffset: 0; }
  .has-reveal .sgs-maze.is-in :is(.sgs-maze__start, .sgs-maze__end) { opacity: 1; transform: none; }

  /* the emblem grows from 15px to keynote, step by step */
  .has-reveal .sgs-ladder[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .sgs-ladder li { opacity: 0; transform: translateY(12px); transition: opacity 0.5s ease, transform 0.5s ease; transition-delay: calc(var(--i) * 140ms + 0.1s); }
  .has-reveal .sgs-ladder.is-in li { opacity: 1; transform: none; }

  /* the palette paints upward, swatch by swatch */
  .has-reveal .sgs-palette[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .sgs-swatch { clip-path: inset(100% 0 0 0); transition: clip-path 0.75s cubic-bezier(0.2, 0.7, 0.2, 1); transition-delay: calc(var(--i, 0) * 90ms); }
  .has-reveal .sgs-palette.is-in .sgs-swatch { clip-path: inset(0); }

  /* gallery images open like doors in a corridor */
  .has-reveal .sgs-gallery img[data-reveal] { opacity: 1; transform: none; clip-path: inset(0 0 100% 0); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .sgs-gallery img[data-reveal-delay="1"] { transition-delay: 0.12s; }
  .has-reveal .sgs-gallery img[data-reveal-delay="2"] { transition-delay: 0.24s; }
  .has-reveal .sgs-gallery img.is-in { clip-path: inset(0); }

  @media (hover: hover) and (min-width: 40rem) {
    .sgs-config { transition: transform 0.35s ease; }
    .sgs-config:hover { transform: translateY(-4px); }
  }
}


/* ---- pages.css ---- */

/* ---- Home hero: tile reveal ----------------------------------------------
   Scroll-driven (src/js/hero-reveal.js): six colour tiles fly in, gather into a grid,
   zoom until they fill the screen, then split apart and step aside, revealing
   the intro line. The headline is visible first, fades while the tiles cover
   it, and returns as they leave. The section is tall and the stage sticks to
   the viewport while it plays.
   Without JS, or with reduced motion, it is a plain dark hero: headline and
   intro visible, no tiles. Tuning values live here as custom properties --
   hero-reveal.js reads every one of them (see its top), so a config change
   never needs a script edit. Her spec, 2026-09-22. Only columns and grid
   width change by breakpoint (mobile default, then tablet/desktop below);
   every other knob is the same at every size. cols/gap are read once at
   page load (see hero-reveal.js), so this sets each screen's own grid,
   not a live reflow while resizing across breakpoints. */
.hero-reveal {
  --hero-cols: 3;
  --hero-gap: 16px;
  --hero-grid-width: 480px;
  --hero-tile-aspect: 1;     /* width / height; 1 = square */
  --hero-tile-radius: 8px;
  --hero-stagger: 0;         /* delay between tiles in a column, as a share of the timeline */
  --hero-overlap: 0.6;       /* how much the zoom overlaps the last tiles arriving */
  --hero-zoom: 2.05;         /* grid scale at full zoom */
  --hero-spread: 0.4;        /* minimum push apart, as a share of a tile */
  --hero-scrub: 0.08;        /* seconds of smoothing on the scroll value */
  --hero-scroll: 3;          /* extra screens of scrolling the animation takes */
  background: var(--bg); /* the page tone; the whole site now sits on it */
  color: var(--text);
}
@media (min-width: 48rem) {
  .hero-reveal { --hero-grid-width: 720px; } /* tablet: same 3 columns, more room */
}
@media (min-width: 64rem) {
  .hero-reveal { --hero-cols: 4; --hero-grid-width: 1200px; } /* desktop */
}
.hero-reveal__stage {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 80vh;
  min-height: 80svh;
  padding: var(--space-8) var(--page-inset);
  overflow: hidden;
}
.hero-reveal__content {
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  width: 100%;
  padding-inline: var(--page-inset);
  text-align: center;
}
.hero-reveal .label { margin-inline: auto; }
.hero-reveal h1 { color: var(--text); }
.hero__title { max-width: 16em; margin-inline: auto; }
.hero__intro { max-width: 32rem; margin: 0 auto; color: var(--muted); font-size: var(--step-1); }
.hero-reveal__headline, .hero-reveal__body { will-change: transform; backface-visibility: hidden; }
.hero-reveal__grid-wrap { display: none; }

/* ---- Still wall: the hero when motion is reduced (her pick, 2026-10-08) ----
   Shown INSTEAD of the moving tiles to anyone with reduced motion -- the
   device setting or the panel's Reduce motion switch -- and without JS.
   A tilted wall of every case-study mockup (once each, shuffled per visit by
   hero-reveal.js) behind a veil of the page colour, so it follows the theme.
   The images are lazy: hidden, they never download. */
.hero-wall { display: none; position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.hero-wall::after { content: ""; position: absolute; inset: 0; background: color-mix(in srgb, var(--bg) 80%, transparent); }
.hero-wall__grid {
  position: absolute; left: 50%; top: 50%;
  width: max(150%, 760px);
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 10px;
  transform: translate(-50%, -50%) rotate(-12deg);
}
.hero-wall__grid img { display: block; width: 100%; height: auto; aspect-ratio: 1; object-fit: cover; border-radius: 10px; }
@media (min-width: 64rem) {
  .hero-wall__grid { width: max(2100px, 146vw); grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 22px; }
  .hero-wall__grid img { border-radius: 14px; }
}
/* the same switch three ways: device setting, panel switch, no JS */
@media (prefers-reduced-motion: reduce) {
  .hero-wall { display: block; }
  .hero-reveal__stage { min-height: 88vh; min-height: 88svh; }
  .hero-reveal .hero__intro, .hero-reveal .label { color: var(--text); }
}
html[data-motion="reduce"] .hero-wall, html:not(.js) .hero-wall { display: block; }
html[data-motion="reduce"] .hero-reveal__stage, html:not(.js) .hero-reveal__stage { min-height: 88vh; min-height: 88svh; }
html[data-motion="reduce"] .hero-reveal :is(.hero__intro, .label), html:not(.js) .hero-reveal :is(.hero__intro, .label) { color: var(--text); }

/* :not([data-motion]) = the Reduce motion switch (a11y-boot.js) gets the same static hero */
@media (prefers-reduced-motion: no-preference) {
  .js:not([data-motion="reduce"]) .hero-reveal {
    height: calc((1 + var(--hero-scroll)) * 100vh);
    height: calc((1 + var(--hero-scroll)) * 100svh);
  }
  .js:not([data-motion="reduce"]) .hero-reveal__stage {
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100svh;
    min-height: 0;
    padding: 0;
  }
  .js:not([data-motion="reduce"]) .hero-reveal__content { height: 100%; }
  .js:not([data-motion="reduce"]) .hero-reveal__body { opacity: 0; }
  .js:not([data-motion="reduce"]) .hero-reveal__grid-wrap {
    display: block;
    position: absolute;
    top: 50%;
    left: 50%;
    width: min(var(--hero-grid-width), calc(100% - var(--hero-gap) * 2));
    transform: translate(-50%, -50%);
    pointer-events: none;
  }
  .hero-reveal__grid {
    display: grid;
    grid-template-columns: repeat(var(--hero-cols), minmax(0, 1fr));
    gap: var(--hero-gap);
    will-change: transform;
  }
  .hero-reveal__tile {
    width: 100%;
    aspect-ratio: var(--hero-tile-aspect);
    border-radius: var(--hero-tile-radius);
    overflow: hidden;
    visibility: hidden;
    will-change: transform;
    backface-visibility: hidden;
  }
  .hero-reveal__tile img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    user-select: none;
  }
}

/* ---- Home: one page tone, ruled sections --------------------------------
   Every home section sits on --bg (the page tone), with a full-width hairline
   between neighbours -- the same line the footer carries on top, so the
   Contact section needs none below it. Home only: the rest of the site keeps
   its no-dividers rule. */
[data-page="home"] main > section { background: var(--bg); }
[data-page="home"] main > section + section { border-top: 1px solid var(--secondary); }

/* ---- Home: featured-work showcase ---------------------------------------
   A port of the React Bits "Showcase 2" marquee. The header sits in the
   .container; the track breaks out full-bleed below it. Cards are staggered
   heights, the first is the real case study and links to it.
   Without JS, or with reduced motion, this is a plain scrollable row --
   nothing here hides content, and the drift is added by the script (which
   sets .has-showcase). See src/js/work-showcase.js. */
.showcase__cta { margin-top: var(--space-6); }
.showcase__viewport { margin-top: var(--space-8); overflow: hidden; }
/* no JS: the row is still reachable, by scrolling it */
.showcase__viewport { overflow-x: auto; scrollbar-width: none; }
.showcase__viewport::-webkit-scrollbar { display: none; }
.has-showcase .showcase__viewport { overflow-x: hidden; }

.showcase__track {
  display: flex;
  align-items: flex-end;
  gap: var(--space-6);
  width: max-content;
  padding-inline: var(--page-inset);
}
/* pan-y: a horizontal finger drag goes to the script, a vertical one still scrolls the page */
.has-showcase .showcase__track { padding-inline: 0; cursor: grab; will-change: transform; touch-action: pan-y; }
.has-showcase .showcase__track.is-dragging { cursor: grabbing; }

.showcase__card {
  flex: none;
  width: 280px;
  height: 400px;
  overflow: hidden;
  border-radius: 8px; /* her card-radius standard, same as the portrait and case-study thumbnails */
  background: var(--fill);
  text-decoration: none;
}
@media (min-width: 40rem) { .showcase__card { width: 320px; } }
.showcase__card--2 { height: 450px; }
.showcase__card--3 { height: 420px; }
.showcase__card--4 { height: 380px; }
.showcase__card--5 { height: 440px; }
.showcase__card img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  user-select: none;
}
.showcase__card .ph-image { display: grid; width: 100%; height: 100%; border: 0; }

/* Hover (and keyboard focus): the picture dims and a "View project" pill
   fades in at the centre. The whole card is the link; the pill is only the
   visual cue (aria-hidden), so the accessible name stays the image alt.
   Hover-capable pointers only -- on touch, a tap simply opens the project. */
.showcase__card { position: relative; display: block; }
.showcase__card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 45%, transparent);
  opacity: 0;
  transition: opacity 0.25s;
  pointer-events: none;
}
.showcase__view {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, calc(-50% + 0.5rem));
  transition: opacity 0.25s, transform 0.25s, background-color 0.2s, color 0.2s, border-color 0.2s;
  pointer-events: none;
}
@media (hover: hover) {
  .showcase__card:hover::after { opacity: 1; }
  .showcase__card:hover .showcase__view { opacity: 1; transform: translate(-50%, -50%); }
}
.showcase__card:focus-visible::after { opacity: 1; }
.showcase__card:focus-visible .showcase__view { opacity: 1; transform: translate(-50%, -50%); }
/* mid-drag the row is being thrown, not browsed: no pill flicker */
.showcase__track.is-dragging .showcase__card::after,
.showcase__track.is-dragging .showcase__view { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .showcase__view { transform: translate(-50%, -50%); } }
html[data-motion="reduce"] .showcase__view { transform: translate(-50%, -50%); }

/* ---- Home: stories (testimonials) ----------------------------------------
   Every case study's testimonial pulled in automatically at build time (see
   collect_stories()/build_story_markup() in build.py) -- a new case study's
   testimonial shows up here on its own, nothing to edit here. One quote
   shown at a time on the left, a clickable name/role list on the right;
   stories.js rotates, the list's underline fills as a timer, prev/next and
   clicking a name jump directly. Static (no rotation, no progress bar) with
   reduced motion -- see stories.js. */
.stories__head { display: flex; flex-direction: column; gap: var(--space-5); }
@media (min-width: 40rem) { .stories__head { flex-direction: row; align-items: flex-end; justify-content: space-between; } }
.stories__arrows { display: flex; gap: var(--space-3); }
.stories__arrow {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border: 1px solid var(--secondary);
  border-radius: 999px;
  background: none;
  color: var(--text);
  cursor: pointer;
}
.stories__arrow .icon { width: 1rem; height: 1rem; }
.stories__arrow:hover { border-color: transparent; background: var(--accent); color: var(--bg); }
.stories__arrow--solid { border-color: var(--text); background: var(--text); color: var(--bg); }

.stories__rule { margin-top: var(--space-6); border-top: 1px solid var(--secondary); }
@media (min-width: 64rem) { .stories__rule { margin-top: var(--space-7); } }

.stories__body { display: grid; gap: var(--space-7); margin-top: var(--space-7); }
@media (min-width: 64rem) { .stories__body { grid-template-columns: 7fr 5fr; gap: var(--space-8); } }

.stories__quote-mark {
  display: block;
  color: var(--accent);
  font-family: "Google Sans Flex", var(--font-body);
  font-weight: 600;
  font-size: clamp(4rem, 2.5rem + 5vw, 6rem);
  line-height: 0.8;
}
/* All panels share one grid cell, so the block's height always matches the
   TALLEST currently-visible one -- no hardcoded min-height to guess at
   across testimonials of very different lengths. */
/* Height follows the active quote (stories.js sets it and the transition
   eases the change), so a short quote doesn't leave the longest one's gap. */
.stories__panels { display: grid; margin-top: var(--space-2); overflow: hidden; transition: height 0.3s ease; }
.stories__panel { grid-area: 1 / 1; align-self: start; transition: opacity 0.3s ease, transform 0.3s ease; }
.stories__panel[hidden] { display: block; opacity: 0; transform: translateY(8px); pointer-events: none; visibility: hidden; }
.stories__quote { max-width: none; font-size: var(--step-0); line-height: 1.6; }
.stories__quote p + p { margin-top: var(--space-4); }
.stories__by { margin-top: var(--space-6); }
.stories__by strong { display: block; font-size: var(--step-0); font-weight: 500; }
.stories__by span { display: block; margin-top: 2px; color: var(--muted); font-size: var(--step--1); }
@media (prefers-reduced-motion: reduce) { .stories__panel, .stories__panels { transition: none; } }

.stories__list { display: flex; flex-direction: column; }
.stories__nav-item {
  display: block;
  width: 100%;
  padding-block: var(--space-4);
  border: 0;
  border-top: 1px solid var(--secondary);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.stories__list > :last-child { border-bottom: 1px solid var(--secondary); }
.stories__nav-name { display: block; color: var(--muted); font-size: var(--step-0); font-weight: 500; transition: color 0.2s ease; }
.stories__nav-role { display: block; margin-top: 2px; color: var(--muted); font-size: var(--step--1); opacity: 0.7; }
.stories__nav-item[aria-pressed="true"] .stories__nav-name { color: var(--text); }
.stories__nav-item:hover .stories__nav-name { color: var(--text); }
.stories__nav-bar { display: block; margin-top: var(--space-3); height: 1px; background: var(--secondary); overflow: hidden; }
.stories__nav-bar-fill { display: block; width: 100%; height: 100%; background: var(--text); transform: scaleX(0); transform-origin: left; }
.stories__nav-item[aria-pressed="true"] .stories__nav-bar-fill { transition: transform 0.1s linear; }

.home-about { align-items: center; }
.home-about__image { max-width: 24rem; }
.home-about .ph-image { aspect-ratio: 1 / 1; object-position: 50% 22%; }
@media (min-width: 48rem) { .home-about .ph-image { aspect-ratio: 4 / 5; } }
.home-about h2 { margin-bottom: var(--space-4); font-size: calc(var(--step-3) * 0.8); }
.home-about p + p { margin-top: var(--space-5); }
.home-contact__cta { margin-top: var(--space-6); }

/* Contact: two columns, both full-bleed (the Contact page's technique): no
   .container and no section padding, so the left panel reaches the viewport's
   left edge and the waves reach the right edge, the About section above and
   the footer below with zero gap. The left column keeps the content lined up
   with every other section via --page-inset on its leading edge. */
.home-contact__grid { display: grid; grid-template-columns: 1fr; }
@media (min-width: 64rem) {
  .home-contact__grid { grid-template-columns: 1fr 1fr; align-items: stretch; }
}
.home-contact__main {
  padding-block: var(--space-8);
  padding-inline: var(--page-inset);
}
@media (min-width: 64rem) {
  .home-contact__main { padding-block: var(--space-9); padding-inline-end: var(--space-8); }
}
/* the Contact page's waves (src/js/contact-waves.js), blended over --bg the
   same way that page's panel is */
.home-contact__visual { position: relative; overflow: hidden; min-height: 20rem; background: var(--bg); }
.home-contact__waves { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }

/* ---- About -------------------------------------------------------------- */
.about-bio { align-items: start; }
.about-bio__image { max-width: 24rem; }
.about-bio .ph-image { aspect-ratio: 1 / 1; object-position: 50% 22%; }
@media (min-width: 48rem) {
  .about-bio { grid-template-columns: 1fr 1.4fr; }
  .about-bio .ph-image { aspect-ratio: 4 / 5; }
}
.about-bio .prose p + p { margin-top: var(--space-5); }
.about-bio .prose h2:first-child { margin-top: 0; }

/* ---- 404 -----------------------------------------------------------------
   Ported from a React/Tailwind component she supplied (React Bits "404-3"),
   rebuilt framework-free. Dark like the home hero: background is --text.
   The numerals are two stacked copies of "404" -- a blurred one behind and a
   sharp one in front -- each with its own mask gradient, so the glyphs sit
   solid at the bottom and dissolve upward into a glow. The block then pulls
   itself up and down with negative margins so the headline tucks into the
   faded tail of the numbers; that overlap is the whole idea of the design.
   The numerals are NOT Syne: that face is reserved for H1/H2 (QA enforces it),
   so they are the body face at its heaviest weight. */
.not-found {
  display: flex;
  align-items: flex-start;
  min-height: 60svh;
  padding: var(--space-6) var(--page-inset);
  background: var(--bg);
  color: var(--text);
  text-align: center;
}
@media (min-width: 40rem) { .not-found { padding-block: var(--space-7); } }
.not-found__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 1400px;
  margin-inline: auto;
}

.not-found__numerals {
  --nf-size: 110px;
  position: relative;
  padding: var(--space-6) 2.5rem;
  margin: calc(var(--space-6) * -1) 0 calc(var(--space-7) * -1);
  user-select: none;
  -webkit-user-select: none;
  animation: nf-settle 0.8s ease-out both;
}
/* The side padding is only room for the blur to bleed into. The template's
   120/140px pushes the block (1.86em of glyphs + padding) past the viewport at
   640 and 768, so it is trimmed to a width that fits every breakpoint. */
@media (min-width: 40rem) {
  .not-found__numerals { --nf-size: 220px; padding: var(--space-9) 80px; margin: -5rem 0 -7rem; }
}
@media (min-width: 48rem) { .not-found__numerals { --nf-size: 280px; } }
@media (min-width: 64rem) { .not-found__numerals { --nf-size: 340px; } }

.not-found__digits {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent); /* her call: the page's one accent moment (8.9:1 on the dark page) */
  font-size: var(--nf-size);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  /* solid at the foot of the glyphs, gone by two thirds up */
  /* a mask only reads the alpha channel, so the colour is arbitrary: an
     on-palette opaque tone stands in for the template's untinted literal,
     which the palette test rejects on sight (comments included). */
  -webkit-mask-image: linear-gradient(to top, var(--text) 15%, var(--text) 35%, transparent 65%);
  mask-image: linear-gradient(to top, var(--text) 15%, var(--text) 35%, transparent 65%);
}
.not-found__digits--glow {
  position: absolute;
  inset: 0;
  filter: blur(14px);
  /* the glow lives in a band above the solid part, fading out at both ends */
  -webkit-mask-image: linear-gradient(to top, transparent 25%, var(--text) 60%, var(--text) 82%, transparent 100%);
  mask-image: linear-gradient(to top, transparent 25%, var(--text) 60%, var(--text) 82%, transparent 100%);
}

.not-found__title {
  margin-top: var(--space-4);
  /* the template's 20 -> 30px ramp; no step token covers it (--step-1 stops at
     24px, --step-2 starts at 26px), so this one is spelled out. */
  font-size: clamp(1.25rem, 1.05rem + 0.85vw, 1.875rem);
  color: var(--text);
  animation: nf-rise 0.5s ease 0.25s both;
}
@media (min-width: 40rem) { .not-found__title { margin-top: var(--space-5); } }

.not-found__note {
  max-width: 20rem;
  margin-top: var(--space-3);
  color: var(--muted);
  font-size: var(--step--1);
  line-height: 1.6;
  animation: nf-rise 0.5s ease 0.35s both;
}

.not-found__cta { margin-top: var(--space-6); animation: nf-rise 0.5s ease 0.45s both; }
@media (min-width: 40rem) { .not-found__cta { margin-top: var(--space-7); } }

/* Entry animation (the motion/react `animate` equivalent). The hidden state
   lives only inside the keyframes, so with reduced motion -- where base.css
   kills animations outright -- everything simply renders in place. */
@keyframes nf-settle {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: none; }
}
@keyframes nf-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* ---- About headline: a pull-quote ---------------------------------------
   Sized like an H2 (the same token as .prose h2). Its big opening quote mark
   is the page's ONE accent moment, so the accent bar above the H1 is switched
   off here. The mark is decorative (aria-hidden), which is why accent is OK. */
.quote-title { font-size: var(--step-2); max-width: 26em; }
.quote-mark {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--accent);
  font-size: clamp(8rem, 5rem + 7vw, 14rem);
  line-height: 0.8;
  height: 0.42em;
}

/* About subheader: 15% smaller than the shared page intro, set as a small dark
   block (primary background, page-coloured text, like the site's text selection).
   Other pages' intros are unchanged. */
.about-subheader {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  background: var(--primary);
  color: var(--bg);
  font-size: calc(var(--step-1) * 0.85);
  line-height: 1.35;
}
/* Mobile (her call, 2026-09-22): force the break after "Nadia Dolot." so the
   name and the title never split mid-word on a narrow screen. From tablet up
   there is room for one line, so the break is switched off rather than removed. */
.about-subheader__break { display: block; }
@media (min-width: 40rem) { .about-subheader__break { display: none; } }

/* ---- Contact: two-column panel ------------------------------------------
   Ported from a React/Tailwind component she supplied (React Bits "Contact 2"),
   rebuilt framework-free. Dark like the home hero, the header and the footer.
   Left column holds the lead line and the studio details, right column the
   title and the form. The scroll-in reveal is the motion/react `whileInView`
   equivalent; see src/js/contact-form.js.
   NOTE: the form has no backend yet. Submit is intercepted by that script
   until her own Cloudflare Worker exists (PLACEHOLDERS.md). */
.contact-panel {
  background: var(--bg);
  color: var(--text);
}
/* No .container here (case-study full-bleed technique): the aside's own
   background -- an animated waves canvas, src/js/contact-waves.js -- needs to
   reach the true viewport edge and touch the header/footer with zero gap.
   The section itself carries no padding any more; each column re-creates its
   own share of the old padding-block, and the main column keeps --page-inset
   on its trailing edge so its content still lines up with every other page's
   container edge (the aside gets that same inset on its OWN leading edge,
   since that is now the page's true left edge too). */
.contact-panel__grid { display: grid; grid-template-columns: 1fr; gap: var(--space-7); }
@media (min-width: 64rem) {
  .contact-panel__grid { grid-template-columns: 1fr 1fr; gap: var(--space-8); align-items: stretch; }
}

/* Left column: waves canvas behind everything, lead at the top, details
   pinned to the bottom on desktop. Stacked (mobile/tablet) it is the first
   block, so it only needs the section's old TOP padding -- a bottom pad here
   too would double up with .contact-panel__main's own top-facing edge and
   the grid gap between them. Side by side (64rem+) it needs both, to match
   .contact-panel__main's height and vertical rhythm exactly. */
.contact-panel__aside {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-top: var(--space-8);
  padding-inline: var(--page-inset);
  background: var(--bg); /* fallback if WebGL never mounts */
}
@media (min-width: 40rem) { .contact-panel__aside { padding-top: var(--space-9); } }
@media (min-width: 64rem) { .contact-panel__aside { padding-block: var(--space-9); padding-inline-end: 0; } }

.contact-panel__waves { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }

/* Everything else in the column sits above the canvas. */
.contact-panel__lead { position: relative; z-index: 1; font-size: var(--step-2); font-weight: var(--weight-heading); color: var(--text); }
.contact-panel__details { position: relative; z-index: 1; margin-top: var(--space-9); display: grid; gap: var(--space-6); }
@media (min-width: 64rem) { .contact-panel__details { margin-top: 0; } }

.contact-panel__badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  margin: 0;
  border: 1px solid var(--muted);
  border-radius: 50%;
  font-size: var(--step-0);
  font-weight: 500;
}
.contact-panel__address { display: grid; gap: var(--space-1); }
/* Pinned to the bottom of the column, over moving pixels: sized and weighted
   up from the rest of the aside's already-bumped type for readability. Same
   light --text as the lead line, not --muted -- muted's grey reads faint
   against the waves (her call). */
.contact-panel__address p { margin: 0; font-size: var(--step-0); font-weight: 500; color: var(--text); }

.contact-panel__social { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: 0; padding: 0; list-style: none; }
.contact-panel__social a { font-size: var(--step-0); color: var(--text); }

/* Right column. Stacked, it comes second, so it only needs the old BOTTOM
   padding (see .contact-panel__aside above); side by side it needs both. */
.contact-panel__main { padding-bottom: var(--space-8); padding-inline: var(--page-inset); }
@media (min-width: 40rem) { .contact-panel__main { padding-bottom: var(--space-9); } }
@media (min-width: 64rem) { .contact-panel__main { padding-block: var(--space-9); padding-inline-start: 0; } }
.contact-panel__title { margin-bottom: var(--space-7); color: var(--text); }
@media (min-width: 64rem) { .contact-panel__title { margin-bottom: var(--space-8); } }

/* ---- Contact form -------------------------------------------------------
   Underlined fields: no boxes, just a bottom rule that brightens on focus.
   Colour alone is never the focus signal -- the rule also doubles in weight
   (via box-shadow, so nothing reflows). */
.contact-form { display: grid; gap: var(--space-6); }
.contact-form__row { display: grid; gap: var(--space-6); }
@media (min-width: 40rem) { .contact-form__row { grid-template-columns: 1fr 1fr; } }
.field { margin: 0; }
.field__input {
  width: 100%;
  padding: 0 0 var(--space-3);
  border: 0;
  border-bottom: 1px solid var(--secondary);
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font-size: var(--step-0);
  transition: border-color 0.2s, box-shadow 0.2s;
}
.field__input::placeholder { color: var(--muted); opacity: 1; }
.field__input:focus-visible {
  outline: none;
  border-bottom-color: var(--text);
  box-shadow: 0 1px 0 0 var(--text);
}
.field__input--area { resize: none; display: block; }
/* "Did you mean ...?" under the email field (contact-form.js). */
.field__hint[hidden] { display: none; }
.field__hint { display: block; margin-top: var(--space-2); font-size: var(--step--1); color: var(--muted); }
.field__hint-fix {
  font: inherit; color: var(--text); background: none; border: 0; padding: 0;
  text-decoration: underline; text-underline-offset: 0.2em; cursor: pointer;
}

/* Round checkbox: a real <input>, restyled, so keyboard and screen readers
   still get a checkbox. The tick is drawn with a rotated corner. */
.field--check { display: flex; align-items: flex-start; gap: var(--space-3); }
.check__input {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.125rem 0 0;
  border: 1px solid var(--muted);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  transition: background-color 0.2s, border-color 0.2s;
}
.check__input:checked { border-color: var(--text); background: var(--text); }
.check__input:checked::after {
  content: "";
  position: absolute;
  left: 0.4rem;
  top: 0.17rem;
  width: 0.28rem;
  height: 0.55rem;
  border: solid var(--bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.check__label { font-size: var(--step--1); color: var(--muted); }
.check__label a { color: inherit; text-decoration: underline; transition: color 0.2s; }
.check__label a:hover { color: var(--text); }

/* ---- Start a project: choice chips ---------------------------------------
   Same underlined-field form as Contact, plus pill chips for the multiple-
   choice questions. Each chip is a real checkbox/radio (invisible, stretched
   over the pill) wrapped in its <label>, so keyboard, screen readers and native
   validation all still work. Picked = the light --primary pill, and the
   weight goes up too, so colour is never the only signal. */
.project-form { max-width: 48rem; margin-inline: auto; } /* centred under the centred header (her call) */
.project-form .field { max-width: none; } /* the site-wide p measure would make these narrower than the Name/Email row */
.choice { margin: 0; padding: 0; border: 0; min-width: 0; }
.choice__legend {
  display: block;
  margin-bottom: var(--space-4);
  padding: 0;
  color: var(--text);
  font-size: var(--step-0);
  font-weight: 500;
}
.choice__options { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.choice__option { position: relative; display: inline-flex; }
.choice__input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.choice__chip {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--secondary);
  border-radius: 999px;
  color: var(--text);
  font-size: var(--step--1);
  line-height: 1.4;
  transition: background-color 0.2s, border-color 0.2s, color 0.2s;
}
.choice__option:hover .choice__chip { border-color: var(--muted); }
.choice__input:checked + .choice__chip {
  border-color: var(--primary);
  background: var(--primary);
  color: var(--bg);
  font-weight: 600;
}
.choice__input:focus-visible + .choice__chip { outline: 2px solid var(--primary); outline-offset: 3px; }

.contact-form__actions { margin: 0; }
.contact-form__status { margin: 0; font-size: var(--step--1); color: var(--muted); }
.contact-form__status.is-error { color: var(--text); }
/* Spam trap field: off-screen rather than display:none, which some bots skip. */
.form-trap { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
/* Turnstile slot: taken out of the form's grid (no row, no extra gap) but
   still rendered, unless Cloudflare needs the visitor to click, when
   contact-form.js adds .is-visible and it becomes a normal row. */
.contact-form { position: relative; }
.form-turnstile { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }
.form-turnstile.is-visible { position: static; width: auto; height: auto; overflow: visible; opacity: 1; pointer-events: auto; }

/* Scroll-in reveal (the motion/react `whileInView` equivalent). The hidden
   state is applied only once the script has added .has-reveal, so the page
   still reads normally if JS never runs. */
@media (prefers-reduced-motion: no-preference) {
  .has-reveal [data-reveal] {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.4s ease, transform 0.4s ease;
  }
  .has-reveal [data-reveal-delay="1"] { transition-delay: 0.1s; }
  .has-reveal [data-reveal-delay="2"] { transition-delay: 0.2s; }
  .has-reveal [data-reveal-delay="3"] { transition-delay: 0.3s; }
  .has-reveal [data-reveal].is-in { opacity: 1; transform: none; }
}

/* ---- Blog post ------------------------------------------------------------
   Two columns from 64rem: a sticky "In this post" menu on the left that
   follows the reader (blog-toc.js marks the section in view), the post on the
   right. Below 64rem the menu is a collapsible box above the post. Body text
   matches the About page: --step-0, regular weight. */
/* Post titles sized like case-study titles, not the site H1 */
.post .page-header h1 { font-size: clamp(1.9rem, 1.4rem + 2.2vw, 3.25rem); }
/* Second part of the title: H3 size in the body face, semibold, accent
   (her call) */
.post-title__sub { display: block; margin-top: var(--space-3); font-family: var(--font-body); font-weight: 600; font-size: var(--step-2); line-height: 1.25; letter-spacing: normal; color: var(--accent); }

.post-layout { display: grid; gap: var(--space-6); align-items: start; }
.post-toc__box { border: 1px solid var(--secondary); border-radius: 8px; padding: var(--space-3) var(--space-4); }
.post-toc__label { color: var(--muted); font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; cursor: pointer; }
.post-toc__list { margin: var(--space-3) 0 0; padding: 0; list-style: none; }
.post-toc__list a {
  display: block;
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
  border-left: 2px solid var(--secondary);
  color: var(--muted);
  font-size: var(--step--1);
  line-height: 1.4;
  text-decoration: none;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.post-toc__list a:hover { color: var(--text); }
.post-toc__list a.is-active { border-left-color: var(--accent); color: var(--text); font-weight: 600; }

@media (min-width: 64rem) {
  .post-layout { grid-template-columns: 16rem minmax(0, 1fr); gap: var(--space-9); }
  .post-toc { position: sticky; top: var(--space-6); max-height: calc(100vh - 2 * var(--space-6)); overflow-y: auto; }
  .post-toc__box { border: 0; border-radius: 0; padding: 0; }
  .post-toc__label { cursor: default; pointer-events: none; list-style: none; }
  .post-toc__label::-webkit-details-marker { display: none; }
}

.post-body h2 { scroll-margin-top: var(--space-6); }
.post-body h2:first-of-type { margin-top: var(--space-8); }
.post-body p + p, .post-body ul + p { margin-top: var(--space-4); }
.post-body .post-pull {
  margin-block: var(--space-6);
  padding-left: var(--space-4);
  border-left: 2px solid var(--text);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.35;
}
.post-body .post-pull + h2 { margin-top: var(--space-7); }
.post-body strong { font-weight: 600; }

/* Blog index: newest first, each post one ruled row */
.post-list { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--secondary); }
.post-list li { border-bottom: 1px solid var(--secondary); }
.post-list a { display: grid; gap: var(--space-2); padding-block: var(--space-6); color: inherit; text-decoration: none; }
.post-list .post-list__meta { color: var(--accent); font-weight: 500; font-size: var(--step--1); letter-spacing: 0.1em; text-transform: uppercase; }
.post-list h2 { max-width: 28ch; font-size: var(--step-2); line-height: 1.2; }
.post-list a:hover h2 { text-decoration: underline; text-decoration-thickness: 1px; }
.post-list p { color: var(--muted); }
@media (min-width: 64rem) {
  .post-list a { grid-template-columns: 1fr 2fr; column-gap: var(--space-6); }
  .post-list__meta { grid-row: 1 / span 2; padding-top: 0.4em; }
}

/* ---- Home + About motion (2026-10-07 touch-up, structure unchanged) -------
   Same contract as the case studies: hidden "before" states only behind
   .has-reveal (reveal.js), load-time animations end visible, and all of it
   is off under prefers-reduced-motion. Photos tilt toward the pointer on
   mouse/trackpad screens only (tilt.js sets --tilt-x/--tilt-y). */
.is-tiltable { position: relative; perspective: 900px; }
.is-tiltable .ph-image { transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)); transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1); }
.is-tiltable::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 8px;
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}
.is-tiltable:hover::after { opacity: 1; background: radial-gradient(circle at var(--glow-x, 50%) var(--glow-y, 50%), color-mix(in srgb, var(--text) 16%, transparent) 0%, transparent 55%); }

@media (prefers-reduced-motion: no-preference) {
  /* Featured work: the row of cards opens card by card */
  .has-reveal .showcase__viewport[data-reveal] { opacity: 1; transform: none; }
  .has-reveal .showcase__card { clip-path: inset(100% 0 0 0 round 8px); transition: clip-path 0.9s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .showcase__card:nth-child(2) { transition-delay: 0.08s; }
  .has-reveal .showcase__card:nth-child(3) { transition-delay: 0.16s; }
  .has-reveal .showcase__card:nth-child(4) { transition-delay: 0.24s; }
  .has-reveal .showcase__card:nth-child(5) { transition-delay: 0.32s; }
  .has-reveal .showcase__card:nth-child(n+6) { transition-delay: 0.4s; }
  .has-reveal .showcase__viewport.is-in .showcase__card { clip-path: inset(0 round 8px); }
  .showcase__card img { transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1); }

  /* Testimonials: the rule under the heading draws left to right */
  .has-reveal .stories__rule[data-reveal] { opacity: 1; transform: scaleX(0); transform-origin: left; transition: transform 1.2s cubic-bezier(0.6, 0, 0.2, 1) 0.2s; }
  .has-reveal .stories__rule.is-in { transform: none; }

  /* Portraits: open from the bottom while the photo settles */
  .has-reveal .home-about__image[data-reveal],
  .has-reveal .about-bio__image[data-reveal] { opacity: 1; transform: none; clip-path: inset(100% 0 0 0 round 8px); transition: clip-path 1.1s cubic-bezier(0.6, 0, 0.2, 1); }
  .has-reveal .home-about__image.is-in,
  .has-reveal .about-bio__image.is-in { clip-path: inset(0 round 8px); }
  .has-reveal .home-about__image:not(.is-in) .ph-image,
  .has-reveal .about-bio__image:not(.is-in) .ph-image { transform: scale(1.12); }
  .has-reveal .home-about__image:not(.is-in),
  .has-reveal .about-bio__image:not(.is-in) { overflow: hidden; } /* the zoomed photo must not widen the page */
  .has-reveal .home-about__image .ph-image,
  .has-reveal .about-bio__image .ph-image { transition: transform 1.6s cubic-bezier(0.2, 0.7, 0.2, 1); }
  .has-reveal .is-tiltable.is-in .ph-image { transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1); }

  /* About header: the quote mark drops in, the quote rises, the name block wipes open */
  .about-head .quote-title { animation: nd-rise 0.9s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
  .about-head .quote-mark { animation: nd-mark 1s cubic-bezier(0.3, 1.4, 0.5, 1) 0.1s both; transform-origin: 0 60%; }
  .about-head .about-subheader { animation: nd-wipe 0.8s cubic-bezier(0.6, 0, 0.2, 1) 0.55s both; }

  /* About bio: paragraphs follow each other in */
  .has-reveal .about-bio .prose [data-reveal] { transition-duration: 0.6s; }

  /* What I do: rules draw in, then each service rises, one after another */
  .has-reveal .about-services[data-reveal] { opacity: 1; transform: none; position: relative; border-top-color: transparent; }
  .has-reveal .about-services::before,
  .has-reveal .about-services li::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    background: var(--secondary);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 1s cubic-bezier(0.6, 0, 0.2, 1);
  }
  .has-reveal .about-services::before { top: -1px; }
  .has-reveal .about-services li { position: relative; border-bottom-color: transparent; }
  .about-services li:nth-child(2) { --i: 1; }
  .about-services li:nth-child(3) { --i: 2; }
  .about-services li:nth-child(4) { --i: 3; }
  .about-services li:nth-child(5) { --i: 4; }
  .has-reveal .about-services li::after { bottom: -1px; transition-delay: calc(var(--i, 0) * 120ms + 0.1s); }
  .has-reveal .about-services li > * { opacity: 0; transform: translateY(14px); transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1); transition-delay: calc(var(--i, 0) * 120ms + 0.2s); }
  .has-reveal .about-services.is-in::before,
  .has-reveal .about-services.is-in li::after { transform: none; }
  .has-reveal .about-services.is-in li > * { opacity: 1; transform: none; }

  @media (hover: hover) {
    .showcase__card:hover img { transform: scale(1.05); }

    /* arrow buttons: the arrow nudges toward where it points */
    .showcase__cta .nav-cta .icon,
    .home-contact__cta .nav-cta .icon { transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.2, 1); }
    .showcase__cta .nav-cta:hover .icon,
    .home-contact__cta .nav-cta:hover .icon { transform: translate(2px, -2px); }

    /* "More about me": the underline sweeps out and back in */
    .home-about .text-link { text-decoration: none; background: linear-gradient(currentColor, currentColor) right bottom / 100% 1px no-repeat; padding-bottom: 2px; transition: background-size 0.35s ease; }
    .home-about .text-link:hover { background-size: 0 1px; animation: nd-underline 0.7s ease 0.05s forwards; }

    /* services: the row leans in */
    .about-services h3,
    .about-services p { transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1); }
    .about-services li:hover h3 { transform: translateX(8px); }
    .has-reveal .about-services.is-in li:hover > h3 { transform: translateX(8px); transition-delay: 0s; }
  }
}
@keyframes nd-rise { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
@keyframes nd-mark { from { opacity: 0; transform: translateY(-20px) scale(0.6); } to { opacity: 1; transform: none; } }
@keyframes nd-wipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0); } }
@keyframes nd-underline { from { background-position: left bottom; background-size: 0 1px; } to { background-position: left bottom; background-size: 100% 1px; } }

/* ---- About: FAQ ----------------------------------------------------------
   Her "FAQ 7" reference, framework-free: category tabs (left, sticky on
   desktop; a scrolling row on phones) with a sliding pill, one card per
   category, an accordion inside it. Content is one source in build.py (FAQ)
   for this and the FAQPage structured data. Without JS (no .has-faq) every
   category and answer simply shows. src/js/faq.js does the rest. */
.faq-title { margin-top: var(--space-4); }
.faq-intro { margin-top: var(--space-4); max-width: 36rem; }
.faq { display: grid; gap: var(--space-6); margin-top: var(--space-7); }
@media (min-width: 64rem) {
  .faq { grid-template-columns: 260px minmax(0, 1fr); gap: var(--space-7); margin-top: var(--space-8); }
}

.faq__tabs { display: none; position: relative; gap: 6px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: none; }
.faq__tabs::-webkit-scrollbar { display: none; }
.has-faq .faq__tabs { display: flex; }
@media (min-width: 64rem) {
  .has-faq .faq__tabs { flex-direction: column; position: sticky; top: 6rem; align-self: start; overflow: visible; padding-bottom: 0; }
}
.faq__tab {
  position: relative;
  z-index: 1;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: 0.75rem 1rem;
  border: 0;
  border-radius: 12px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: var(--step--1, 0.9rem);
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  transition: color 0.2s;
}
.faq__tab:hover, .faq__tab[aria-selected="true"] { color: var(--text); }
.faq__tab:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.faq__count { color: var(--muted); font-size: 0.8em; font-variant-numeric: tabular-nums; }
/* the selected tab's background, slid into place by faq.js */
.faq__pill {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  border: 1px solid var(--secondary);
  border-radius: 12px;
  background: var(--fill);
  pointer-events: none;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), width 0.4s cubic-bezier(0.22, 1, 0.36, 1), height 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.faq__pill.is-jump { transition: none; }

.faq__panels { display: grid; gap: var(--space-6); }
.has-faq .faq__panels { display: block; min-height: 430px; }
.faq__panel { overflow: hidden; border: 1px solid var(--secondary); border-radius: 24px; background: var(--fill); }
.faq__panel[hidden] { display: none; }
.faq__panel:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.faq__head { padding: var(--space-5) var(--space-5); border-bottom: 1px solid var(--secondary); }
@media (min-width: 40rem) { .faq__head { padding: var(--space-5) var(--space-6); } }
.faq__head h3 { font-family: var(--font-body); font-size: var(--step-1); font-weight: 600; color: var(--text); }
.faq__head p { margin-top: var(--space-1, 0.25rem); color: var(--muted); font-size: 0.9rem; }

.faq__item + .faq__item { border-top: 1px solid var(--secondary); }
.faq__qh { margin: 0; font: inherit; }
.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  width: 100%;
  padding: var(--space-5);
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-weight: 500;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
}
@media (min-width: 40rem) { .faq__q { padding: var(--space-5) var(--space-6); } }
.faq__q:focus-visible { outline: 2px solid var(--text); outline-offset: -2px; }
.faq__qtext { flex: 1; transition: color 0.2s; }
.faq__q:hover .faq__qtext { color: var(--muted); }
.faq__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--muted);
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.2s, color 0.2s;
}
.faq__icon .icon { width: 1rem; height: 1rem; }
.faq__q:hover .faq__icon { color: var(--text); }
.faq__item.is-open .faq__icon { transform: rotate(45deg); background: var(--text); color: var(--bg); }

/* answer: open by default (no JS); with JS, collapsed rows animate their height */
.faq__a > div { overflow: hidden; }
.faq__a p { max-width: 42rem; padding: 0 var(--space-5) var(--space-5); color: var(--text); }
@media (min-width: 40rem) { .faq__a p { padding: 0 var(--space-6) var(--space-6); } }
.has-faq .faq__a {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 0.4s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.has-faq .faq__item.is-open .faq__a { grid-template-rows: 1fr; opacity: 1; }
.has-faq .faq__item:not(.is-open) .faq__a { visibility: hidden; transition: grid-template-rows 0.4s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s, visibility 0s 0.4s; }

/* switching category: the card fades up, its rows follow one after another */
@keyframes faq-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.faq__panel.is-entering { animation: faq-in 0.3s cubic-bezier(0.22, 1, 0.36, 1) both; }
.faq__panel.is-entering .faq__item { animation: faq-in 0.45s cubic-bezier(0.22, 1, 0.36, 1) both; animation-delay: calc(var(--i) * 60ms + 60ms); }

@media (prefers-reduced-motion: reduce) {
  .faq__pill, .faq__icon, .has-faq .faq__a, .has-faq .faq__item:not(.is-open) .faq__a { transition: none; }
  .faq__panel.is-entering, .faq__panel.is-entering .faq__item { animation: none; }
}


/* ---- a11y.css ---- */

/* ==========================================================================
   Accessibility settings (spec 2026-10-08). Button + panel, then one block
   per setting keyed off attributes a11y-boot.js puts on <html>.
   ========================================================================== */
/* ---- Header button ------------------------------------------------------ */
.a11y-btn {
  display: flex; align-items: center; justify-content: center;
  width: 2.75rem; height: 2.75rem; padding: 0;
  border: 1px solid var(--secondary); border-radius: 50%;
  background: none; color: var(--text); cursor: pointer;
  transition: background-color 0.2s, color 0.2s;
}
.a11y-btn:hover { background: var(--fill); }
.a11y-btn[aria-expanded="true"] { background: var(--primary); border-color: var(--primary); color: var(--bg); }
/* the button sits with "Start a project" as one group; below 64rem that CTA
   is hidden and the group sits right, beside the burger */
.site-header__inner { position: relative; }
.site-header__actions { display: flex; align-items: center; gap: var(--space-3); margin-left: auto; }
@media (min-width: 64rem) { .site-header__actions { margin-left: 0; } }

/* ---- Panel: full screen below 64rem (like the menu), card from 64rem ---- */
.a11y-panel {
  position: fixed; inset: 0; z-index: 60;
  display: grid; align-content: start; gap: 0;
  overflow-y: auto;
  padding: var(--space-4) var(--page-inset) var(--space-6);
  background: var(--bg); color: var(--text);
  font-size: var(--step--1);
}
.a11y-panel[hidden] { display: none; }
@media (min-width: 64rem) {
  .a11y-panel {
    position: absolute; inset: auto; top: calc(100% + var(--space-2)); right: 0;
    width: 22rem; max-height: calc(100vh - 7rem);
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--secondary); border-radius: 12px;
    box-shadow: 0 18px 40px color-mix(in srgb, var(--bg) 70%, transparent);
  }
  .a11y-row__hint { display: none; }
}
.a11y-panel__top { display: flex; align-items: center; justify-content: space-between; padding-bottom: var(--space-3); }
.a11y-panel__title { font-family: var(--font-body); font-size: var(--step-1); font-weight: 600; }
.a11y-panel__close { display: flex; align-items: center; justify-content: center; width: 2.25rem; height: 2.25rem; padding: 0; border: 1px solid var(--secondary); border-radius: 50%; background: none; color: var(--text); cursor: pointer; }
.a11y-row { display: grid; gap: var(--space-2); margin: 0; padding: var(--space-3) 0; border: 0; border-top: 1px solid var(--secondary); min-width: 0; }
.a11y-row[hidden] { display: none; }
.a11y-row__name { padding: 0; font-weight: 500; font-size: var(--step-0); }
/* a floated legend leaves the fieldset border and sits in the row like a label */
.a11y-row > legend { float: left; width: 100%; }
.a11y-row > legend + * { clear: both; }
.a11y-row__line { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.a11y-row__hint { margin: 0; color: var(--muted); }
.a11y-seg { display: flex; gap: 2px; padding: 3px; border: 1px solid var(--secondary); border-radius: 999px; }
.a11y-seg label { flex: 1; min-width: 0; }
.a11y-seg input, .a11y-cv input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.a11y-seg span { display: block; padding: 0.4rem 0.5rem; border-radius: 999px; text-align: center; color: var(--muted); cursor: pointer; white-space: nowrap; }
.a11y-seg input:checked + span { background: var(--primary); color: var(--bg); }
.a11y-seg input:focus-visible + span, .a11y-cv input:focus-visible + span { outline: 2px solid var(--text); outline-offset: 2px; }
.a11y-seg--size label:nth-child(1) span { font-size: 0.85em; }
.a11y-seg--size label:nth-child(3) span { font-size: 1.2em; }
/* every size chip as tall as the largest one: the letter shrinks, the chip does not */
.a11y-seg--size label { display: flex; }
.a11y-seg--size span { flex: 1; display: flex; align-items: center; justify-content: center; }
.a11y-switch { position: relative; flex: none; width: 2.5rem; height: 1.4rem; padding: 0; border: 1px solid var(--muted); border-radius: 999px; background: none; cursor: pointer; }
.a11y-switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 0.9rem; height: 0.9rem; border-radius: 50%; background: var(--muted); transition: transform 0.2s ease; }
.a11y-switch[aria-checked="true"] { background: var(--primary); border-color: var(--primary); }
.a11y-switch[aria-checked="true"]::after { transform: translateX(1.1rem); background: var(--bg); }
.a11y-switch:disabled { opacity: 0.6; cursor: not-allowed; }
.a11y-cv { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-1); }
.a11y-cv span { display: grid; justify-items: center; gap: 4px; padding: var(--space-2) 2px; border: 1px solid var(--secondary); border-radius: 8px; color: var(--muted); text-align: center; font-size: 0.85em; cursor: pointer; }
.a11y-cv input:checked + span { border-color: var(--text); color: var(--text); }
.a11y-cv__dot { width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--accent); }
.a11y-panel__foot { display: flex; justify-content: space-between; align-items: center; padding-top: var(--space-3); border-top: 1px solid var(--secondary); color: var(--muted); }
.a11y-reset { padding: 0; border: 0; background: none; color: var(--text); font: inherit; font-weight: 500; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }

/* ---- Theme: case studies keep every coloured background ---------------
   Her rule (2026-10-08): in Dark, no coloured background in a case study
   changes. The article keeps the light palette; only sections marked
   data-neutral (plain copy on the page background) follow the theme. */
[data-page-theme="light"][data-theme="dark"] article[class^="cs-"] {
  --bg: #faf9f7; --text: #1b1a18; --primary: #2c2b27; --secondary: #aba79a;
  --accent: #92afcc; --muted: #5e5d5b; --muted-on-primary: #bcbbb9; --fill: #e6e5e0;
  background: var(--bg); color: var(--text);
}
[data-page-theme="light"][data-theme="dark"] article[class^="cs-"] [data-neutral] {
  --bg: #1b1a18; --text: #faf9f7; --primary: #e3e3e2; --secondary: #5f5e54;
  --accent: #9cbce1; --muted: #b7b6b4; --muted-on-primary: #575655; --fill: #2c2b27;
  background: var(--bg); color: var(--text);
}
/* a neutral block that is only container-wide (the "All case studies" link)
   paints its dark background edge to edge, so it never reads as a box */
[data-page-theme="light"][data-theme="dark"] article[class^="cs-"] > .container[data-neutral] {
  box-shadow: 0 0 0 100vmax var(--bg);
  clip-path: inset(0 -100vmax);
}

/* ---- High contrast ------------------------------------------------------ */
[data-contrast="high"] a:not(.nav-cta):not(.wordmark):not(.showcase__card):not(.btn):not(.site-nav a) { text-decoration: underline; text-underline-offset: 3px; }
[data-contrast="high"] :focus-visible { outline: 3px solid var(--text); outline-offset: 3px; }
/* the case-study pins above set tokens on the article itself, so high
   contrast and colour vision repeat their values there (light values on the
   pinned article, dark values on its neutral sections) */
[data-contrast="high"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] { --muted: #1b1a18; --secondary: #6b6a63; --accent: #1f4675; }
[data-contrast="high"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] [data-neutral] { --muted: #faf9f7; --secondary: #a3a29c; --accent: #c4d9f2; }
[data-cv="rg"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] { --accent: #2f5fb3; }
[data-cv="rg"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] [data-neutral] { --accent: #a8c4ff; }
[data-cv="by"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] { --accent: #0c6b86; }
[data-cv="by"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] [data-neutral] { --accent: #56cfe1; }
[data-cv="grey"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] { --accent: #5e5d5b; }
[data-cv="grey"][data-page-theme="light"][data-theme="dark"] article[class^="cs-"] [data-neutral] { --accent: #c9c8c6; }

/* ---- Text size: every size is rem-based, so the root scales all of it --- */
html[data-text="115"] { font-size: 115%; }
html[data-text="130"] { font-size: 130%; }

/* ---- Readable spacing (WCAG 1.4.12 text-spacing values) ----------------- */
[data-spacing="wide"] body { line-height: 1.8; }
[data-spacing="wide"] main :is(p, li, dd, dt, blockquote, label, td, th, h1, h2, h3, h4) {
  letter-spacing: 0.12em; word-spacing: 0.16em; line-height: 1.5;
}
[data-spacing="wide"] main :is(p, li, dd) { line-height: 1.8; }
[data-spacing="wide"] main p + p { margin-top: 2em; }
[data-spacing="wide"] main a:not(.nav-cta):not(.showcase__card):not(.btn) { text-decoration: underline; text-underline-offset: 3px; }

/* ---- Dyslexia-friendly: OpenDyslexic, no italics, no capitals ----------
   Client specimens keep their own fonts: they name the family literally,
   not through these tokens, and the overrides skip client areas. */
[data-dyslexia="on"] {
  --font-display: "OpenDyslexic", "Instrument Sans", system-ui, sans-serif;
  --font-body:    "OpenDyslexic", "Instrument Sans", system-ui, sans-serif;
}
[data-dyslexia="on"] body :not([data-client-brand], [data-client-brand] *, .specimen, .specimen *) { font-style: normal !important; text-transform: none !important; }
[data-dyslexia="on"] main :is(em, i, cite):not([data-client-brand] *, .specimen *) { font-weight: 600; }

/* ---- Reading guide -------------------------------------------------------- */
.a11y-guide { display: none; }
[data-guide="on"] .a11y-guide {
  display: block; position: fixed; inset: 0; z-index: 55; pointer-events: none;
  --guide-h: 3.2em;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--bg) 55%, transparent) calc(var(--guide-y, 40vh) - var(--guide-h) / 2),
    transparent calc(var(--guide-y, 40vh) - var(--guide-h) / 2),
    transparent calc(var(--guide-y, 40vh) + var(--guide-h) / 2),
    color-mix(in srgb, var(--bg) 55%, transparent) calc(var(--guide-y, 40vh) + var(--guide-h) / 2));
}

/* ---- Reduce motion: the device setting's result, on request -------------- */
html[data-motion="reduce"] *, html[data-motion="reduce"] *::before, html[data-motion="reduce"] *::after {
  animation: none !important;
  transition: none !important;
  scroll-behavior: auto !important;
}
html[data-motion="reduce"] { scroll-behavior: auto !important; }

/* ---- Long words under wide spacing / the wider font / larger text -------
   OpenDyslexic and the WCAG spacing values make words wider, so a long word
   ("Accessibility" in an H1, a palette label) can outgrow a phone screen.
   Let it hyphenate, or break as a last resort, instead of pushing the page
   sideways. Only in these modes; the default layout is untouched. */
:is([data-spacing="wide"], [data-text]) main :is(h1, h2, h3, h4, p, li, dt, dd, span, a, figcaption, blockquote) {
  overflow-wrap: anywhere; hyphens: auto;
}
:is([data-spacing="wide"], [data-text]) .color-hierarchy-descriptor { white-space: normal; }
