/* ============================================================
   Niall Arnfield — Terminal
   Single stylesheet. Modern terminal aesthetic (white-on-near-
   black monospace) with animated charts, real photos, scroll-in
   card reveals. Clean-room build — no legacy from prior themes.
============================================================ */

:root {
  --bg:           oklch(0.04 0 0);
  --bg-pane:      oklch(0.07 0 0);
  --fg:           oklch(0.96 0 0);
  --fg-soft:      oklch(0.96 0 0 / 0.65);
  --fg-mute:      oklch(0.96 0 0 / 0.36);
  --accent:       oklch(0.98 0 0);
  --accent-dim:   oklch(0.55 0 0);
  --accent-warm:  oklch(0.88 0.07 60);

  /* ─── Case-study brand accents ─────────────────────────────────
     Matches the hero reel slide colours so the visitor sees the
     same hue from card hover → article body. White on hover keeps
     the data readable in the monochrome theme palette. */
  --cs-praxis:    oklch(0.84 0.14 75);   /* warm amber — money / growth */
  --cs-3d:        oklch(0.82 0.14 220);  /* cyan — tech / WebGL */
  --cs-siv:       oklch(0.82 0.18 145);  /* green — Lighthouse / pass */
  --line:         oklch(0.96 0 0 / 0.16);
  --card:         oklch(0.08 0 0 / 0.5);
  --pad-card:     clamp(24px, 3vw, 44px);
  --pad-x:        clamp(16px, 3vw, 36px);
  --maxw:         1280px;
  --font:         "Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --ease:         cubic-bezier(0.2, 0.8, 0.2, 1);

  /* ─── Type scale ───────────────────────────────────────────────
     Three levels for body content (primary/secondary/tertiary)
     plus heading and display for emphasis. All sizes nudged down
     ~1-2px from the original so the site reads more compact.
     Utility classes .t-primary / .t-secondary / .t-tertiary near
     the bottom of this file apply the tokens via markup. */
  --fs-display:    clamp(20px, 1.6vw, 24px);
  --fs-heading:    clamp(14px, 1vw, 16px);
  --fs-primary:    clamp(13px, 0.9vw, 14px);
  --fs-secondary:  12px;
  --fs-tertiary:   11px;
}

* { box-sizing: border-box; border-radius: 0; }
html, body { margin: 0; padding: 0; }
html { color: var(--fg); background: var(--bg); scroll-behavior: smooth; }
body {
  font-family: var(--font);
  font-size: var(--fs-primary);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ---------- Chrome: vignette + noise + scanlines ---------- */
body::before {
  content: ''; position: fixed; inset: 0; z-index: 9997;
  pointer-events: none;
  background: radial-gradient(ellipse 90% 80% at center,
    transparent 50%,
    rgb(0 0 0 / 0.25) 85%,
    rgb(0 0 0 / 0.6) 100%);
}
body::after {
  content: ''; position: fixed; inset: -80px; z-index: 9999;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(to bottom,
      transparent 0,
      transparent 2px,
      rgb(255 255 255 / 0.08) 2px,
      rgb(255 255 255 / 0.08) 3px),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.9 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 100% 3px, 240px 240px;
  background-repeat: repeat;
  opacity: 0.10;
  transform: translate3d(0, 0, 0);
  animation: nat-buzz 0.5s steps(8) infinite;
  will-change: transform;
}
@keyframes nat-buzz {
   0%   { transform: translate3d(  0,   0, 0); }
  12%   { transform: translate3d(-33,  17, 0); }
  25%   { transform: translate3d( 41, -29, 0); }
  37%   { transform: translate3d(-18,  47, 0); }
  50%   { transform: translate3d( 62, -11, 0); }
  62%   { transform: translate3d(-47, -53, 0); }
  75%   { transform: translate3d( 23,  35, 0); }
  87%   { transform: translate3d(-55,  19, 0); }
 100%   { transform: translate3d(  0,   0, 0); }
}
@keyframes nat-blink { 50% { opacity: 0; } }

/* Glitch moment — fires on hover of .work__item / .service-card.
   Card jitters ±3px over 7 stepped frames (~360ms), while the
   ::before stripe flashes on/off at three different heights to read
   as a "video signal tear". Stepped easing keeps it digital. */
@keyframes nat-glitch {
   0%   { transform: translate(0, 0); }
  14%   { transform: translate(-3px,  1px); }
  28%   { transform: translate( 3px, -1px); }
  42%   { transform: translate(-2px,  0  ); }
  57%   { transform: translate( 2px,  1px); }
  71%   { transform: translate(-1px, -1px); }
  85%   { transform: translate( 1px,  0  ); }
 100%   { transform: translate(0,    0  ); }
}
@keyframes nat-glitch-tear {
   0%   { opacity: 0;   top: 30%; height: 12px; }
  14%   { opacity: 0.95; top: 18%; height: 10px; }
  28%   { opacity: 0;   top: 18%; }
  42%   { opacity: 0.85; top: 62%; height: 16px; }
  57%   { opacity: 0;   top: 62%; }
  71%   { opacity: 0.9; top: 40%; height: 6px;  }
  85%   { opacity: 0; }
 100%   { opacity: 0; }
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 4px; }
img { max-width: 100%; height: auto; display: block; }
p { margin: 0 0 0.8em; }
p:last-child { margin-bottom: 0; }

/* ---------- Nav ---------- */
.nav {
  position: relative;
  margin: clamp(14px, 1.6vw, 22px) auto 0;
  width: calc(100% - 2 * var(--pad-x));
  max-width: var(--maxw);
  padding: 10px clamp(14px, 1.8vw, 22px);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 18px;
  align-items: center;
  background: color-mix(in oklab, var(--card) 80%, transparent);
  border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  box-shadow:
    0 0 10px color-mix(in oklab, var(--accent) 12%, transparent),
    0 0 28px color-mix(in oklab, var(--accent) 6%, transparent);
}
.nav__brand { font-weight: 700; font-size: var(--fs-primary); color: var(--fg); letter-spacing: 0.04em; }
.nav__brand::before { content: '> '; color: var(--accent); }
.nav__links { display: flex; gap: 4px; justify-self: center; }
.nav__links a {
  color: var(--fg-soft); padding: 6px 14px;
  font-size: var(--fs-primary); font-weight: 500;
  transition: color 0.15s, background 0.15s;
}
.nav__links a:hover {
  color: var(--fg);
  background: color-mix(in oklab, var(--accent) 8%, transparent);
  text-decoration: none;
}
.nav__actions { display: flex; gap: 8px; justify-self: end; align-items: center; }
/* .nav__cv + .nav__book inherit the unified .term-btn system below. */

@media (max-width: 900px) {
  .nav { grid-template-columns: auto 1fr; }
  .nav__links { display: none; }
}

/* ---------- Section card frame ---------- */
.section {
  position: relative;
  margin: clamp(20px, 2.5vw, 36px) auto;
  width: calc(100% - 2 * var(--pad-x));
  max-width: var(--maxw);
  background: var(--card);
  border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  box-shadow:
    0 0 10px color-mix(in oklab, var(--accent) 10%, transparent),
    0 0 28px color-mix(in oklab, var(--accent) 5%, transparent),
    0 1px 0 rgb(255 255 255 / 0.04) inset,
    0 30px 60px -40px rgb(0 0 0 / 0.7);
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.section.is-in { opacity: 1; transform: none; }
.section#top { opacity: 1; transform: none; }

/* Titlebar layout — macOS Terminal style: window-control dots on the
   far left, path label centered. Three-column grid where the outer
   columns are equal flex (1fr) so the label stays mathematically
   centered between the dots and the right edge. */
.section__titlebar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 10px clamp(14px, 1.6vw, 22px);
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 24%, transparent);
  font-size: var(--fs-secondary); letter-spacing: 0.06em; text-transform: lowercase;
  color: color-mix(in oklab, var(--accent) 80%, var(--fg));
}
.section__titlebar-left {
  justify-self: center;
  text-align: center;
}
.section__titlebar-left::before { content: none; }

/* Window control dots — red close / yellow minimize / green zoom.
   Authentic macOS Terminal palette. Inset highlight gives them
   the slight 3D pop of the real thing. */
.section__dots {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-self: end;
}
/* Section eyebrow (──  01 · intro, ── 02 · about, etc.) — promoted
   from the section body into the titlebar's left column. Kills the
   duplicated label that used to sit inside .section__body. */
.section__titlebar-eyebrow {
  justify-self: start;
  letter-spacing: 0.06em;
  color: var(--fg-mute);
}
.section__titlebar-eyebrow::before {
  content: '── ';
  color: var(--accent);
  margin-right: 4px;
}
.section__dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: block;
  border: 1px solid rgb(0 0 0 / 0.22);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.18);
}
.section__dot--close { background: #ff5f56; }
.section__dot--min   { background: #ffbd2e; }
.section__dot--max   { background: #27c93f; }
.section__body { padding: var(--pad-card); }
/* .section__eyebrow retired — eyebrow now lives in the titlebar as
   .section__titlebar-eyebrow. */

/* ---------- Prompt + command line ---------- */
.line {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-top: clamp(20px, 2.4vw, 32px);
}
.line:first-of-type { margin-top: 0; }
/* Prompt is terminal chrome — lowercase, muted, no glow. The chrome
   reads as "this is a terminal" without competing with the command. */
.prompt {
  color: color-mix(in oklab, var(--fg) 45%, transparent);
  font-weight: 400;
  font-size: 0.82em;
  text-transform: lowercase;
  letter-spacing: 0;
  white-space: nowrap; user-select: none;
}
/* Command is the section headline — uppercase, accent, bolder, glowing.
   This is the line the visitor actually reads ("WHO AM I?", "SHOW ME
   YOUR WORK", etc.) so it gets the visual weight. */
.cmd {
  color: var(--accent);
  font-weight: 700;
  font-size: 1.05em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-shadow:
    0 0  6px color-mix(in oklab, var(--accent) 40%, transparent),
    0 0 16px color-mix(in oklab, var(--accent) 18%, transparent);
}
/* Typewriter modifier states. Cursor lives on the active one;
   pending cmds get a dim, non-blinking cursor (they're queued). */
.cmd--pending::after,
.cmd--typing::after {
  content: '▮';
  margin-left: 2px;
  color: var(--accent);
  text-shadow: 0 0 6px color-mix(in oklab, var(--accent) 45%, transparent);
}
.cmd--typing::after { animation: nat-blink 0.7s steps(2) infinite; }
.cmd--pending::after { opacity: 0.35; }
.cmd--typed::after { content: none; }
.output {
  display: block;
  /* Top-only padding — sits flush with the prompt line on the left
     so section content uses the full card width instead of being
     visually shoved right by the terminal-style indent. */
  padding-top: clamp(8px, 1vw, 14px);
  color: color-mix(in oklab, var(--fg) 92%, transparent);
}
.arrow { color: var(--accent); font-weight: 700; margin-right: 6px; }

/* .exec — the "execute command" CTA. Sits inline with the
   NIALL@PORTFOLIO:~$ prompt so it reads as the command being typed.
   Shares the unified button shape with brackets-swapped-for-cursor:
   the blinking ▮ replaces the closing ] so it still feels like a
   live command line. Default is the discreet transparent shape;
   hover lights up to solid fill + glow like every other CTA. */
.exec {
  display: inline-flex;
  align-items: center;
  padding: 8px 14px;
  font-family: var(--font);
  font-size: var(--fs-primary);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--accent);
  background: transparent;
  border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
  text-decoration: none;
  cursor: pointer;
  transition:
    background 0.2s var(--ease),
    color 0.2s var(--ease),
    border-color 0.2s var(--ease),
    box-shadow 0.2s var(--ease),
    transform 0.12s var(--ease);
}
.exec::after {
  content: '▮';
  margin-left: 10px;
  color: var(--accent);
  animation: nat-blink 0.8s steps(2) infinite;
}
.exec:hover {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
  text-decoration: none;
  box-shadow:
    0 0 14px color-mix(in oklab, var(--accent) 32%, transparent),
    0 0 32px color-mix(in oklab, var(--accent) 14%, transparent);
}
.exec:hover::after { color: var(--bg); }
.exec:active { transform: translateY(1px); }

h1, h2, h3, h4 {
  margin: 0; font-weight: 700; letter-spacing: 0.02em;
  text-shadow:
    0 0  4px color-mix(in oklab, currentColor 40%, transparent),
    0 0 12px color-mix(in oklab, currentColor 20%, transparent);
}

/* ---------- Hero ---------- */
.hero__body {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 56px);
}
@media (max-width: 900px) { .hero__body { grid-template-columns: 1fr; } }
.hero__name { display: block; font-size: var(--fs-display); font-weight: 700; margin-bottom: 4px; }
.hero__role { display: block; font-size: var(--fs-primary); color: var(--accent); margin-bottom: 12px; }
.hero__pitch {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-heading);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.hero__pitch li {
  position: relative;
  padding-left: 18px;
}
.hero__pitch li::before {
  content: '\2022'; /* • */
  position: absolute;
  left: 0;
  top: 0;
  color: var(--accent);
  font-weight: 700;
}
.hero__sep { margin: 0 8px; opacity: 0.45; }

/* ---------- Lighthouse gauge ---------- */
.gauge {
  padding: clamp(16px, 1.8vw, 24px);
  border: 1px dashed color-mix(in oklab, var(--accent) 28%, transparent);
  font-size: var(--fs-primary);
}
.gauge__title {
  display: block; color: var(--accent);
  font-weight: 700; letter-spacing: 0.12em; font-size: var(--fs-secondary);
  text-transform: uppercase; margin-bottom: 12px;
}
.gauge ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.gauge li { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 12px; align-items: center; }
.gauge__lbl { color: color-mix(in oklab, var(--fg) 88%, transparent); }
.gauge__bar { color: var(--accent); font-weight: 600; }
.gauge__bracket { font-size: 1.1em; }
.gauge__track {
  display: inline-block;
  width: clamp(80px, 9vw, 120px);
  height: 0.85em;
  background: color-mix(in oklab, var(--accent) 10%, transparent);
  margin: 0 2px;
  position: relative; overflow: hidden;
  vertical-align: middle;
}
.gauge__fill {
  display: block; height: 100%;
  width: 0;
  background: var(--accent);
  box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 50%, transparent);
  transition: width 1.4s var(--ease);
}
.gauge.is-in .gauge__fill { width: var(--w, 100%); }
.gauge__val { color: var(--accent); font-weight: 700; min-width: 3ch; text-align: right; }
.gauge__verify {
  margin-top: 12px; padding-top: 10px;
  border-top: 1px dashed color-mix(in oklab, var(--accent) 16%, transparent);
  color: var(--fg-mute); font-size: var(--fs-tertiary); letter-spacing: 0.04em;
}

/* ---------- Hero case-study reel ----------
   3-slide rotating panel on the hero's right column. Each .reel__slide
   is a clickable link to a case study; the active one is opacity 1 +
   pointer-events on, the others sit underneath at opacity 0. JS
   advances every 6s and toggles .is-on. Hover anywhere on the reel
   pauses the timer. Dots underneath are tablist buttons for direct
   navigation.
============================================================ */
.reel {
  position: relative;
  display: flex; flex-direction: column;
  gap: 14px;
  padding: clamp(14px, 1.6vw, 18px);
  border: 1px dashed color-mix(in oklab, var(--accent) 28%, transparent);
  /* The hero grid stretches both columns to match the taller one — the
     reel sits in the shorter column and we let its viewport flex-grow
     to fill the column so we don't waste vertical real-estate. */
}
.reel__viewport {
  position: relative;
  flex: 1 1 auto;
  min-height: 340px;
}
.reel__slide {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  gap: 12px;
  color: var(--fg);
  text-decoration: none;
  opacity: 0;
  pointer-events: none;
  /* Fade-out is fast, no delay. Fade-in waits for fade-out to finish.
     This staggers the cross-fade so two slides never overlap mid-way. */
  transition: opacity 0.25s var(--ease);
}
.reel__slide.is-on {
  opacity: 1;
  pointer-events: auto;
  transition: opacity 0.30s var(--ease) 0.25s;
}
.reel__head {
  display: flex; justify-content: space-between; align-items: center;
  padding-bottom: 8px;
  border-bottom: 1px dashed color-mix(in oklab, var(--accent) 20%, transparent);
  font-size: var(--fs-tertiary);
  letter-spacing: 0.10em;
  text-transform: uppercase;
}
.reel__head span:first-child { color: var(--accent); font-weight: 700; }
.reel__head span:last-child  { color: var(--fg-mute); }
.reel__thumb {
  position: relative;
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  background: oklch(0.06 0 0);
  /* Grow to fill the leftover space between header and stats+caption.
     min-height keeps the image readable on shorter columns. */
  flex: 1 1 auto;
  min-height: 200px;
  overflow: hidden;
}
.reel__img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0.78;
  filter: grayscale(0.6) contrast(1.08);
  transition: opacity 0.3s var(--ease), filter 0.3s var(--ease);
}
.reel__slide:hover .reel__img { opacity: 0.95; filter: grayscale(0.2) contrast(1.02); }

/* ── Sub-slide chart (series + paired variants) ────────────────────
   Vertical bar chart. Bars are absolute-positioned inside a relative
   flex container so the bottom edge anchors at the baseline and the
   height grows upward. CSS animation runs whenever the slide gets
   .is-on, with a per-bar `--d` delay so bars cascade in left-to-right.
*/
.reel__chart {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  padding: clamp(10px, 1.4vw, 18px) clamp(8px, 1.2vw, 16px) clamp(8px, 1vw, 12px);
  border: 1px solid color-mix(in oklab, var(--accent) 18%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
  position: relative;
  overflow: hidden;
}
.reel__chart-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 14px;
}
.reel__chart-title {
  font-size: var(--fs-tertiary);
  color: var(--chart-color, var(--accent));
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 700;
  transition: color 0.3s var(--ease);
}
.reel__chart-axis {
  font-size: 10px;
  color: var(--fg-mute);
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  line-height: 1.1;
}
.reel__chart-axis span { color: color-mix(in oklab, var(--fg-mute) 70%, transparent); }
.reel__chart-bars {
  flex: 1 1 auto;
  display: flex;
  align-items: flex-end;
  gap: clamp(4px, 0.6vw, 8px);
  /* Subtle grid baseline */
  background-image:
    linear-gradient(to top,
      color-mix(in oklab, var(--accent) 10%, transparent) 1px,
      transparent 1px),
    linear-gradient(to top,
      color-mix(in oklab, var(--accent) 5%, transparent) 1px,
      transparent 1px),
    linear-gradient(to top,
      color-mix(in oklab, var(--accent) 5%, transparent) 1px,
      transparent 1px);
  background-position: bottom, 33% bottom, 66% bottom;
  background-size: 100% 100%, 100% 66.66%, 100% 33.33%;
  background-repeat: no-repeat;
  padding-bottom: 22px;     /* room for x labels under each bar */
  padding-top: 14px;        /* room for value above each bar */
  position: relative;
}
/* Single-series bar */
.reel__chart-bar {
  flex: 1 1 0;
  position: relative;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.reel__chart-fill {
  display: block;
  width: 70%;
  height: 0;
  background: var(--chart-color, var(--accent));
  box-shadow: 0 0 8px color-mix(in oklab, var(--chart-color, var(--accent)) 35%, transparent);
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.reel__chart-val {
  position: absolute;
  bottom: var(--h, 0%);
  margin-bottom: 2px;
  font-size: 10px;
  color: var(--fg-soft);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
  transition-delay: calc(var(--d, 0s) + 0.8s);
}
.reel__chart-lbl {
  position: absolute;
  top: 100%;
  margin-top: 6px;
  font-size: 10px;
  color: var(--fg-mute);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
/* Animate the fill height when the parent slide is active. The
   per-bar `--d` (computed in PHP) staggers each bar's start. */
.reel__slide.is-on .reel__chart-fill {
  animation: nat-bar-grow 1s var(--ease) forwards;
  animation-delay: var(--d, 0s);
}
.reel__slide.is-on .reel__chart-val {
  opacity: 1;
  transform: none;
}
@keyframes nat-bar-grow {
  from { height: 0; }
  to   { height: var(--h, 50%); }
}
.reel:hover .reel__slide.is-on .reel__chart-fill,
.reel:focus-within .reel__slide.is-on .reel__chart-fill {
  animation-play-state: paused;
}

/* Hover on the slide swaps the colorised chart back to the
   monochrome accent so the data reads cleanly in the theme palette
   while the visitor inspects. Affects bars, glow, title, and the
   "after" value label in paired charts. */
.reel__slide:hover .reel__chart-fill {
  background: var(--accent);
  box-shadow: 0 0 8px color-mix(in oklab, var(--accent) 35%, transparent);
}
.reel__slide:hover .reel__chart-bar--before .reel__chart-fill {
  /* before-bars stay dim grey regardless of state — override the
     accent-fill hover so they don't pop white. */
  background: color-mix(in oklab, var(--fg) 32%, transparent);
  box-shadow: none;
}
.reel__slide:hover .reel__chart-title,
.reel__slide:hover .reel__chart-bar--after .reel__chart-val {
  color: var(--accent);
}

/* Paired variant: groups of two bars side-by-side per metric */
.reel__chart--paired .reel__chart-bars {
  /* slightly larger gap between groups */
  gap: clamp(6px, 0.8vw, 14px);
}
.reel__chart-group {
  flex: 1 1 0;
  position: relative;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.reel__chart-pair {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 4px;
  width: 100%;
  height: 100%;
}
.reel__chart-bar--before .reel__chart-fill,
.reel__chart-bar--after  .reel__chart-fill { width: 100%; }
.reel__chart-bar--before .reel__chart-fill {
  background: color-mix(in oklab, var(--fg) 32%, transparent);
  box-shadow: none;
}
.reel__chart-bar--after .reel__chart-fill {
  background: var(--chart-color, var(--accent));
}
.reel__chart-bar--before,
.reel__chart-bar--after {
  flex: 1 1 0;
  position: relative;
  height: 100%;
  display: flex;
  align-items: flex-end;
}
.reel__chart-bar--before .reel__chart-val {
  color: var(--fg-mute);
}
.reel__chart-bar--after .reel__chart-val {
  color: var(--chart-color, var(--accent));
  font-weight: 700;
  transition: color 0.3s var(--ease);
}
.reel__chart--paired .reel__chart-group .reel__chart-lbl {
  top: 100%;
}

.reel__stats {
  display: flex; flex-wrap: wrap; gap: 6px;
  font-size: var(--fs-tertiary);
  color: var(--accent);
}
.reel__stats span {
  padding: 3px 8px;
  border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  background: color-mix(in oklab, var(--accent) 5%, transparent);
}
.reel__cap {
  margin: 0;
  font-size: var(--fs-secondary);
  color: var(--fg-soft);
  line-height: 1.4;
}

/* Per-slide "View case study →▮" pill. Lives at the bottom of every
   slide; the parent .reel__slide is already an anchor, so this is
   just the visual affordance. Lights up to solid fill when the
   whole slide is hovered. Cursor blink at the end matches the rest
   of the site's CTA language. */
.reel__cta {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: 4px;
  margin-top: 4px;
  padding: 6px 12px;
  font-family: var(--font);
  font-size: var(--fs-primary);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent);
  background: transparent;
  border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
  transition:
    background 0.2s var(--ease),
    color 0.2s var(--ease),
    border-color 0.2s var(--ease),
    box-shadow 0.2s var(--ease);
}
.reel__cta::after {
  content: '▮';
  margin-left: 6px;
  color: var(--accent);
  animation: nat-blink 0.8s steps(2) infinite;
}
.reel__slide:hover .reel__cta {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
  box-shadow:
    0 0 14px color-mix(in oklab, var(--accent) 30%, transparent),
    0 0 32px color-mix(in oklab, var(--accent) 14%, transparent);
}
.reel__slide:hover .reel__cta::after { color: var(--bg); }

/* Slide 3 — the Lighthouse slide reuses .gauge for its body.
   Strip the gauge's own outer border because .reel already has one,
   then let it flex-grow so its 4 bars spread to fill the height. */
.reel__slide .gauge {
  border: 0;
  padding: 0;
  flex: 1 1 auto;
  display: flex; flex-direction: column;
}
.reel__slide .gauge ul {
  flex: 1 1 auto;
  justify-content: space-around;
}

/* Progress-bar navigation. Two stacked bars:
   • TOP bar tracks the core (case study). Active segment fills
     0 → 100% over `--reel-core-step` (== 3× sub step) so it
     visually mirrors the full duration of the core's sub-slides.
   • BOTTOM bar tracks the current sub-slide. Active segment fills
     0 → 100% over `--reel-step` (one sub duration). When it hits
     100% the JS animationend listener advances; sub overflow rolls
     into the next core.
   Past segments stay full, future segments are empty. Hover/focus
   pauses both fills via animation-play-state. */
.reel {
  --reel-step: 5s;
  --reel-core-step: calc(var(--reel-step) * 3);
}
.reel__bars {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: 4px;
}
.reel__bar {
  display: flex;
  gap: 4px;
}
/* Top bar — three clickable segments, one per sub-slide. Active
   segment fills over `--reel-step` (one sub duration). When the JS
   animationend listener sees the active segment hit 100%, it advances
   to the next sub (or rolls into the next core's sub 0). Past sub
   segments stay full; future segments are empty. */
.reel__dot {
  flex: 1;
  height: 3px;
  background: color-mix(in oklab, var(--accent) 14%, transparent);
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: background 0.2s var(--ease);
}
.reel__dot:hover {
  background: color-mix(in oklab, var(--accent) 26%, transparent);
}
.reel__dot-fill {
  position: absolute;
  inset: 0;
  width: 0;
  background: var(--accent);
  box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 50%, transparent);
}
.reel__bar--sub .reel__dot.is-on .reel__dot-fill {
  animation: nat-reel-fill var(--reel-step) linear forwards;
}
.reel__dot.is-done .reel__dot-fill {
  width: 100%;
  animation: none;
}

/* Bottom bar — single continuous track spanning the full reel width,
   tracks the macro/core progress. Fills over `--reel-core-step` (one
   full core's duration = 3× sub step). Restarts whenever the core
   index changes. Same thickness as the top bar so the two stack as
   one progress instrument. */
.reel__corebar {
  flex: 1;
  height: 3px;
  background: color-mix(in oklab, var(--accent) 14%, transparent);
  position: relative;
  overflow: hidden;
}
.reel__corebar-fill {
  position: absolute;
  inset: 0;
  width: 0;
  background: var(--accent);
  box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 50%, transparent);
  animation: nat-reel-fill var(--reel-core-step) linear forwards;
}

.reel:hover .reel__dot.is-on .reel__dot-fill,
.reel:focus-within .reel__dot.is-on .reel__dot-fill,
.reel:hover .reel__corebar-fill,
.reel:focus-within .reel__corebar-fill {
  animation-play-state: paused;
}
@keyframes nat-reel-fill {
  from { width: 0; }
  to   { width: 100%; }
}

.counter { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 700; }
.spark { display: inline-block; letter-spacing: 0.04em; color: var(--accent); font-weight: 700; }

/* ---------- Polaroid photo (terminal-framed) ---------- */
.poly {
  margin: 0;
  display: inline-flex;
  flex-direction: column;
  background: color-mix(in oklab, var(--bg-pane) 90%, transparent);
  border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  box-shadow:
    0 0 12px color-mix(in oklab, var(--accent) 10%, transparent),
    0 30px 50px -32px rgb(0 0 0 / 0.7);
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.poly:hover {
  transform: translateY(-3px);
  box-shadow:
    0 0 18px color-mix(in oklab, var(--accent) 16%, transparent),
    0 36px 60px -32px rgb(0 0 0 / 0.7);
}
.poly__tb {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 12px;
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.poly__tb-name { color: var(--accent); }
.poly__img {
  display: block; width: 100%; height: auto;
  filter: grayscale(0.15) contrast(1.05);
}
.poly__cap {
  padding: 10px 12px;
  font-size: var(--fs-secondary);
  color: var(--fg-soft);
  border-top: 1px dashed color-mix(in oklab, var(--accent) 16%, transparent);
}
.poly__cap::before { content: '> '; color: var(--accent); }

/* ---------- About ---------- */
.about__grid {
  display: grid;
  grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}
@media (max-width: 900px) { .about__grid { grid-template-columns: 1fr; } }
.about__bio p { margin: 0 0 14px; }
.about__bio p:last-child { margin: 0; }

.about__stats {
  margin-top: clamp(20px, 2.4vw, 28px);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.4vw, 22px);
}
.about__stat {
  padding: clamp(12px, 1.4vw, 18px);
  border: 1px dashed color-mix(in oklab, var(--accent) 24%, transparent);
}
.about__stat-num {
  font-size: var(--fs-display);
  font-weight: 700;
  color: var(--accent);
  display: block; line-height: 1;
  margin-bottom: 6px;
}
.about__stat-lbl {
  font-size: var(--fs-tertiary); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--fg-soft);
}
/* Higher specificity than the unified .term-btn block (which sits
   further down the file and otherwise wins source order with its
   own `margin-top: 4px`). Anchor the CTA well below the stats grid. */
.about__bio .about__cta {
  margin-top: clamp(32px, 4vw, 56px);
  align-self: flex-start;
}

/* ---------- Services ---------- */
.services__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 1.8vw, 24px);
  margin-top: 16px;
}
@media (max-width: 900px) { .services__grid { grid-template-columns: 1fr; } }
.service-card {
  position: relative;
  overflow: hidden;
  padding: clamp(20px, 2vw, 28px);
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
  display: flex; flex-direction: column; gap: 12px;
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease), background 0.25s var(--ease);
}
/* Chips inside the services cards specifically drop the dashed top
   divider (and the padding above) — only the chips inside work-item
   cards keep their divider via the base .service-card__chips rule. */
.service-card .service-card__chips {
  border-top: 0;
  padding-top: 0;
}
/* Glitch tear stripe — invisible by default; the `nat-glitch-tear`
   animation flashes it across the card during hover. Sized as a
   thin horizontal slice and repositioned via `top` per keyframe. */
.service-card::before {
  content: '';
  position: absolute;
  left: -3px; right: -3px;
  top: 30%;
  height: 12px;
  background: color-mix(in oklab, var(--accent) 22%, transparent);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
}
.service-card:hover::before { animation: nat-glitch-tear 0.36s steps(7) 1; }
.service-card > * { position: relative; z-index: 2; }
.service-card:hover {
  animation: nat-glitch 0.36s steps(7) 1;
  border-color: color-mix(in oklab, var(--accent) 45%, transparent);
  background: color-mix(in oklab, var(--accent) 5%, transparent);
}
.service-card__icon {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid color-mix(in oklab, var(--accent) 35%, transparent);
  font-size: 18px; color: var(--accent);
}
.service-card__num {
  font-size: var(--fs-tertiary); letter-spacing: 0.18em;
  color: var(--accent); font-weight: 700;
}
.service-card__title { font-size: var(--fs-heading); }
.service-card__desc {
  font-size: var(--fs-primary);
  color: color-mix(in oklab, var(--fg) 80%, transparent);
  margin: 0 0 auto;
}
.service-card__chips {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding-top: 8px;
  border-top: 1px dashed color-mix(in oklab, var(--accent) 18%, transparent);
  font-size: var(--fs-tertiary); color: var(--accent);
}
.service-card__chips span {
  padding: 2px 8px;
  border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  background: color-mix(in oklab, var(--accent) 5%, transparent);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease);
}
.section.is-in .service-card__chips span,
.section.is-in .work__item .service-card__chips span { opacity: 1; transform: none; }
.section.is-in .service-card__chips span:nth-child(1) { transition-delay: 0.20s; }
.section.is-in .service-card__chips span:nth-child(2) { transition-delay: 0.30s; }
.section.is-in .service-card__chips span:nth-child(3) { transition-delay: 0.40s; }
.section.is-in .service-card__chips span:nth-child(4) { transition-delay: 0.50s; }

/* ---------- Work ---------- */
.work__list {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: flex; flex-direction: column;
  gap: clamp(18px, 2vw, 26px);
}
.work__item {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(180px, 220px) minmax(0, 1fr);
  gap: clamp(20px, 2vw, 32px);
  padding: clamp(18px, 2vw, 24px);
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}
/* Glitch tear stripe — see .service-card::before for the model. */
.work__item::before {
  content: '';
  position: absolute;
  left: -3px; right: -3px;
  top: 30%;
  height: 14px;
  background: color-mix(in oklab, var(--accent) 20%, transparent);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
}
.work__item:hover::before { animation: nat-glitch-tear 0.36s steps(7) 1; }
.work__item > * { position: relative; z-index: 2; }
.work__item:hover {
  animation: nat-glitch 0.36s steps(7) 1;
  border-color: color-mix(in oklab, var(--accent) 40%, transparent);
  background: color-mix(in oklab, var(--accent) 4%, transparent);
}
@media (max-width: 700px) { .work__item { grid-template-columns: 1fr; } }
.work__thumb {
  position: relative; margin: 0;
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  background: oklch(0.06 0 0);
  aspect-ratio: 16 / 11;
  overflow: hidden;
}
.work__thumb-tb {
  position: absolute; left: 0; right: 0; top: 0;
  display: flex; justify-content: space-between; align-items: center;
  padding: 6px 10px;
  background: color-mix(in oklab, oklch(0 0 0) 60%, transparent);
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 18%, transparent);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
  z-index: 2;
}
.work__thumb-tb span:first-child { color: var(--accent); }
.work__thumb-img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0.7;
  filter: grayscale(0.7) contrast(1.1);
  transition: opacity 0.3s, filter 0.3s;
}
.work__item:hover .work__thumb-img { opacity: 0.92; filter: grayscale(0.3) contrast(1.05); }
.work__num { font-weight: 700; color: var(--accent); font-size: var(--fs-primary); letter-spacing: 0.04em; }
.work__title {
  display: block; font-size: var(--fs-heading);
  margin: 4px 0 8px;
}
.work__desc { color: var(--fg-soft); font-size: var(--fs-primary); margin: 0 0 14px; }

/* ---------- Terminal button (unified) ----------
   The ONE CTA button across the whole site. Default is the discreet
   shape: transparent fill, accent border at 40%, accent text, brackets
   at 65% accent. Hover lights it up: solid accent fill, bg-colour text,
   brackets darken to 55% bg, and a 2-stop phosphor glow blooms behind.
   Every CTA shares this language: case-study reads, FAQ read-full,
   nav CV + Book now, contact email + LinkedIn. The .exec button
   ("Book a discovery call" / "Start a project") inherits the same
   shape but swaps the closing bracket for its blinking cursor so it
   still reads as the "executed command" inline with the prompt.
============================================================ */
.term-btn,
.work__link,
.faq__more,
.nav__cv,
.nav__book,
.cta__contact a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  margin-top: 4px;
  font-family: var(--font);
  font-size: var(--fs-primary);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent);
  background: transparent;
  border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
  text-decoration: none;
  cursor: pointer;
  transition:
    background 0.2s var(--ease),
    color 0.2s var(--ease),
    border-color 0.2s var(--ease),
    box-shadow 0.2s var(--ease),
    transform 0.12s var(--ease);
}
/* Every CTA carries the same blinking ▮ cursor at the end — the same
   "live terminal command" indicator that .exec uses. Cursor inherits
   accent in the default outlined state, flips to bg on hover when the
   fill inverts. */
.term-btn::after,
.work__link::after,
.faq__more::after,
.nav__cv::after,
.nav__book::after,
.cta__contact a::after {
  content: '▮';
  margin-left: 8px;
  color: var(--accent);
  animation: nat-blink 0.8s steps(2) infinite;
  letter-spacing: 0;
}
.term-btn:hover,
.work__link:hover,
.faq__more:hover,
.nav__cv:hover,
.nav__book:hover,
.cta__contact a:hover {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
  text-decoration: none;
  box-shadow:
    0 0 14px color-mix(in oklab, var(--accent) 30%, transparent),
    0 0 32px color-mix(in oklab, var(--accent) 14%, transparent);
}
.term-btn:hover::after,
.work__link:hover::after,
.faq__more:hover::after,
.nav__cv:hover::after,
.nav__book:hover::after,
.cta__contact a:hover::after {
  color: var(--bg);
}
.term-btn:active,
.work__link:active,
.faq__more:active,
.nav__cv:active,
.nav__book:active,
.cta__contact a:active { transform: translateY(1px); }

/* Nav buttons are tight: same shape, smaller padding + font.
   Tighter cursor margin too so the button stays compact. */
.nav__cv,
.nav__book {
  padding: 6px 10px;
  font-size: var(--fs-secondary);
  margin-top: 0;
}
.nav__cv::after,
.nav__book::after { margin-left: 6px; }

/* Contact buttons: text inside is longer (email address), so we want
   them to read as compact pills inline with the metadata row. */
.cta__contact a {
  margin-top: 0;
  font-size: var(--fs-secondary);
  padding: 6px 10px;
}
.cta__contact a::after { margin-left: 6px; }
.term-btn:active,
.work__link:active,
.faq__more:active { transform: translateY(1px); }

/* ---------- Before/after comparison chart ---------- */
.compare {
  margin-top: 14px; padding: 14px;
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
  font-size: var(--fs-secondary);
}
.compare__title {
  display: block; font-size: 10px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--accent);
  margin-bottom: 12px;
}
.compare__row {
  display: grid;
  grid-template-columns: minmax(0, 110px) repeat(2, minmax(0, 1fr)) auto;
  gap: 10px;
  align-items: center;
  padding: 4px 0;
}
.compare__row + .compare__row { border-top: 1px dashed color-mix(in oklab, var(--accent) 14%, transparent); }
.compare__label { color: var(--fg-soft); }
.compare__bar { display: grid; grid-template-columns: 4ch 1fr; gap: 6px; align-items: center; }
.compare__bar-val { font-weight: 700; }
.compare__bar-val--before { color: var(--accent-dim); }
.compare__bar-val--after  { color: var(--accent); }
.compare__bar-track {
  height: 8px; background: color-mix(in oklab, var(--accent) 8%, transparent);
  position: relative; overflow: hidden;
}
.compare__bar-fill {
  display: block; height: 100%; width: 0;
  transition: width 1.6s var(--ease);
}
.compare__bar--before .compare__bar-fill { background: var(--accent-dim); }
.compare__bar--after .compare__bar-fill { background: var(--accent); box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 40%, transparent); }
.compare.is-in .compare__bar-fill { width: var(--w, 100%); }
.compare__delta { text-align: right; font-weight: 700; color: var(--accent-warm); font-size: var(--fs-tertiary); }

/* ---------- Skills ---------- */
.skills__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 1.8vw, 24px);
  margin-top: 16px;
}
@media (max-width: 700px) { .skills__grid { grid-template-columns: 1fr; } }
.skill-group {
  padding: clamp(16px, 1.8vw, 22px);
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
}
.skill-group__name {
  display: block;
  color: var(--accent);
  font-size: var(--fs-tertiary); letter-spacing: 0.16em;
  text-transform: uppercase; margin-bottom: 10px;
}
.skill-group__name::before { content: '[ '; }
.skill-group__name::after { content: ' ]'; }
.skill-bar {
  display: grid; grid-template-columns: minmax(0, 1fr) 1fr;
  gap: 12px; align-items: center; padding: 4px 0;
  font-size: var(--fs-primary);
}
.skill-bar__name { color: var(--fg); }
.skill-bar__track {
  height: 6px; background: color-mix(in oklab, var(--accent) 8%, transparent);
  overflow: hidden; position: relative;
}
.skill-bar__fill {
  display: block; height: 100%; width: 0;
  background: var(--accent);
  box-shadow: 0 0 4px color-mix(in oklab, var(--accent) 40%, transparent);
  transition: width 1.4s var(--ease);
}
.skill-group.is-in .skill-bar__fill { width: var(--w, 100%); }

/* ---------- Experience timeline ---------- */
.exp__list { list-style: none; margin: 16px 0 0; padding: 0; }
.exp__item {
  display: grid;
  grid-template-columns: minmax(120px, auto) minmax(0, 1fr);
  gap: clamp(16px, 2vw, 28px);
  padding: clamp(14px, 1.4vw, 20px) 0;
  border-bottom: 1px dashed color-mix(in oklab, var(--accent) 16%, transparent);
}
.exp__item:last-child { border-bottom: 0; }
.exp__commit { color: var(--accent); font-weight: 600; font-size: var(--fs-primary); letter-spacing: 0.04em; }
.exp__commit::before { content: 'commit '; color: var(--fg-mute); }
.exp__details strong { display: block; color: var(--fg); font-size: var(--fs-primary); margin-bottom: 4px; }
.exp__details span { color: var(--fg-soft); font-size: var(--fs-primary); }

/* ---------- FAQ ---------- */
.faq__list {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: flex; flex-direction: column;
  gap: clamp(18px, 2vw, 24px);
}
.faq__q {
  display: flex; gap: 10px; align-items: baseline;
  font-size: var(--fs-heading);
  color: var(--fg); font-weight: 600;
  margin-bottom: 8px;
}
.faq__q::before { content: '?'; color: var(--accent); font-weight: 700; }
.faq__a { margin: 0; padding-left: 22px; color: var(--fg-soft); font-size: var(--fs-primary); }

/* ---------- CTA ---------- */
.cta__contact {
  display: flex; gap: 14px; flex-wrap: wrap;
  font-size: var(--fs-primary); color: var(--fg-soft);
  margin-top: 12px;
}
/* .cta__contact a inherits the unified .term-btn system. */

/* ---------- Clients ----------
   Credibility band of 20 client logos. Logos are normalised to a
   common cell size and treated grayscale + dimmed so they don't
   compete with the monochrome palette; each lights up to full
   colour + brightness on hover so any one can still be parsed. */
.clients__lede {
  margin: 0 0 clamp(20px, 2.4vw, 32px);
  font-size: var(--fs-primary);
  color: var(--fg-soft);
  max-width: 60ch;
  line-height: 1.5;
}
.clients__lede strong { color: var(--fg); font-weight: 700; }
.clients__grid {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: clamp(8px, 1vw, 14px);
}
.clients__cell {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(14px, 1.4vw, 20px) 12px;
  min-height: 70px;
  border: 1px dashed color-mix(in oklab, var(--accent) 16%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}
.clients__cell:hover {
  border-color: color-mix(in oklab, var(--accent) 38%, transparent);
  background: color-mix(in oklab, var(--accent) 5%, transparent);
}
.clients__logo {
  display: block;
  max-width: 100%;
  max-height: 38px;
  width: auto; height: auto;
  object-fit: contain;
  filter: grayscale(1) brightness(1.4) contrast(0.95);
  opacity: 0.68;
  transition: filter 0.25s var(--ease), opacity 0.25s var(--ease);
}
.clients__cell:hover .clients__logo {
  filter: grayscale(0) brightness(1.1) contrast(1);
  opacity: 1;
}

/* ---------- Qualifications ----------
   Two MMU credential cards side-by-side. Crest on the left, type
   pill / degree title / institution+year + 1st-Class badge on the
   right. Stacks single-column on narrow viewports. */
.quals__lede {
  margin: 0 0 clamp(20px, 2.4vw, 32px);
  font-size: var(--fs-primary);
  color: var(--fg-soft);
  max-width: 60ch;
  line-height: 1.5;
}
.quals__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 1.8vw, 24px);
}
@media (max-width: 700px) { .quals__grid { grid-template-columns: 1fr; } }
.quals__card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(14px, 1.6vw, 22px);
  align-items: center;
  padding: clamp(18px, 2vw, 26px);
  border: 1px dashed color-mix(in oklab, var(--accent) 28%, transparent);
  background: color-mix(in oklab, var(--accent) 3%, transparent);
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}
.quals__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.quals__card:hover {
  border-color: color-mix(in oklab, var(--accent) 48%, transparent);
  background: color-mix(in oklab, var(--accent) 6%, transparent);
}
.quals__crest {
  width: clamp(56px, 6vw, 84px);
  height: auto;
  display: block;
}
.quals__type {
  display: inline-block;
  font-size: var(--fs-tertiary);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  padding: 2px 10px;
  border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
  margin-bottom: 10px;
}
.quals__title {
  margin: 0 0 10px;
  font-size: var(--fs-heading);
  line-height: 1.25;
  color: var(--fg);
}
.quals__meta {
  margin: 0;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 10px;
  font-size: var(--fs-tertiary);
  color: var(--fg-mute);
  letter-spacing: 0.06em;
}
.quals__badge {
  padding: 2px 8px;
  border: 1px solid color-mix(in oklab, var(--accent) 45%, transparent);
  background: color-mix(in oklab, var(--accent) 8%, transparent);
  color: var(--accent);
  font-weight: 700;
}

/* ---------- Case study (single-case_study.php) ----------
   Deep-dive deliverable for a `case_study` CPT post. Single .section
   card wrapping the featured image, title, optional meta strip from
   easy-portfolio, the_content() body (rendered verbatim — any inline
   markup the editor added shows through), and a back-to-home CTA. */
.cs__hero {
  margin: 0 0 clamp(20px, 2.4vw, 32px);
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  background: oklch(0.06 0 0);
  overflow: hidden;
  aspect-ratio: 16 / 9;
}
.cs__hero-img {
  width: 100%; height: 100%;
  display: block;
  object-fit: cover;
  filter: grayscale(0.3) contrast(1.05);
}
.cs__title {
  margin: 0 0 16px;
  font-size: var(--fs-display);
  letter-spacing: 0.02em;
}
.cs__meta {
  display: flex; flex-wrap: wrap;
  gap: 18px;
  margin: 0 0 clamp(24px, 2.6vw, 36px);
  padding: 12px 0;
  border-top: 1px dashed color-mix(in oklab, var(--accent) 18%, transparent);
  border-bottom: 1px dashed color-mix(in oklab, var(--accent) 18%, transparent);
  font-size: var(--fs-secondary);
  color: var(--fg-soft);
  letter-spacing: 0.04em;
}
.cs__meta strong {
  color: var(--accent);
  margin-right: 6px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  font-size: var(--fs-tertiary);
}
.cs__body {
  font-size: var(--fs-primary);
  line-height: 1.65;
  color: color-mix(in oklab, var(--fg) 92%, transparent);
}
.cs__body p { margin: 0 0 1em; }
.cs__body h2,
.cs__body h3,
.cs__body h4 {
  margin: clamp(28px, 3vw, 44px) 0 clamp(10px, 1vw, 16px);
  font-size: var(--fs-heading);
  letter-spacing: 0.02em;
}
.cs__body img,
.cs__body figure {
  max-width: 100%;
  height: auto;
  margin: clamp(16px, 2vw, 28px) 0;
}
.cs__body ul,
.cs__body ol {
  margin: 0 0 1em 1.4em;
  padding: 0;
}
.cs__body li { margin-bottom: 0.4em; }
.cs__body a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.cs__body blockquote {
  margin: clamp(16px, 2vw, 28px) 0;
  padding: 14px 18px;
  border-left: 2px solid var(--accent);
  background: color-mix(in oklab, var(--accent) 3%, transparent);
  font-style: italic;
  color: var(--fg);
}
.cs__back {
  margin-top: clamp(32px, 3.5vw, 48px) !important;
}

/* ---------- Case-study data dashboard ----------
   Used on the beauty-brand sprint case study (gated in single-case_study.php
   by slug). Three blocks of comparison data — Lighthouse, payload/speed
   and architecture — rendered as a single dashboard above the prose so
   the headline numbers land before the reader hits the long-form copy.

   Reuses the homepage .compare component for the bar rows. The .cs__arch
   grid below is for qualitative (non-numeric) comparisons — same dashed
   chrome and predecessor/after columns, no bars. */
.cs__dashboard {
  margin: 0 0 clamp(32px, 4vw, 48px);
  padding: clamp(20px, 2.4vw, 32px);
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 3%, transparent);
}
.cs__dashboard-title {
  margin: 0 0 6px;
  font-size: var(--fs-heading);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--accent);
}
.cs__dashboard-lede {
  margin: 0 0 clamp(18px, 2vw, 28px);
  font-size: var(--fs-secondary);
  color: var(--fg-soft);
  letter-spacing: 0.04em;
}
.cs__dashboard .compare {
  margin-top: 0;
  margin-bottom: clamp(16px, 1.8vw, 24px);
}
.cs__dashboard .compare:last-of-type {
  margin-bottom: 0;
}
.cs__arch {
  margin-top: clamp(16px, 1.8vw, 24px);
  padding: 14px;
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
  font-size: var(--fs-secondary);
}
.cs__arch-title {
  display: block;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 12px;
}
.cs__arch-grid {
  display: block;
}
.cs__arch-head,
.cs__arch-row {
  display: grid;
  grid-template-columns: minmax(0, 160px) repeat(2, minmax(0, 1fr));
  gap: 10px;
  align-items: start;
  padding: 8px 0;
}
.cs__arch-head {
  padding-top: 0;
  padding-bottom: 10px;
  border-bottom: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
}
.cs__arch-row + .cs__arch-row {
  border-top: 1px dashed color-mix(in oklab, var(--accent) 14%, transparent);
}
.cs__arch-h {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
  font-weight: 700;
}
.cs__arch-h:nth-child(2) { color: var(--accent-dim); }
.cs__arch-h:nth-child(3) { color: var(--accent); }
.cs__arch-lbl {
  color: var(--fg-soft);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.cs__arch-cell {
  line-height: 1.5;
}
.cs__arch-cell--before { color: color-mix(in oklab, var(--accent-dim) 90%, var(--fg)); }
.cs__arch-cell--after  { color: var(--fg); }
.cs__arch-cell--after::before {
  content: '→ ';
  color: var(--accent);
  margin-right: 2px;
  opacity: 0.6;
}
@media (max-width: 700px) {
  .cs__arch-head { display: none; }
  .cs__arch-row {
    grid-template-columns: 1fr;
    gap: 4px;
  }
  .cs__arch-lbl {
    color: var(--accent);
    margin-bottom: 2px;
  }
  .cs__arch-cell--before::before {
    content: 'Predecessor: ';
    color: var(--fg-mute);
    text-transform: uppercase;
    letter-spacing: 0.10em;
    font-size: 10px;
    margin-right: 4px;
  }
  .cs__arch-cell--after::before {
    content: 'True Makeup: ';
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.10em;
    font-size: 10px;
    margin-right: 4px;
    opacity: 1;
  }
}

/* ---------- Case-study article body (the_content) ----------
   Classes the editor's article markup uses inside the post body —
   .cs-result-lede (bold opening paragraph), .cs-pillar (each of the
   five pillar-page sections), .cs-feature-list (the brand-voice and
   Easy Makeup bullet lists). Styled here so the article body lands
   in the same terminal language as everything else.
*/
.cs__body .cs-result-lede {
  margin: 0 0 clamp(24px, 2.6vw, 36px);
  padding: clamp(14px, 1.6vw, 20px) clamp(16px, 1.8vw, 22px);
  border-left: 2px solid var(--accent);
  background: color-mix(in oklab, var(--accent) 4%, transparent);
  font-size: var(--fs-heading);
  line-height: 1.55;
  color: var(--fg);
}
.cs__body .cs-result-lede strong {
  color: var(--accent);
  letter-spacing: 0.01em;
}
.cs__body .cs-pillar {
  margin: clamp(32px, 3.5vw, 48px) 0;
  padding: clamp(20px, 2.4vw, 28px);
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
}
.cs__body .cs-pillar__eyebrow {
  display: block;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 6px;
}
.cs__body .cs-pillar__title {
  margin: 0 0 clamp(10px, 1vw, 14px);
  font-size: var(--fs-heading);
  letter-spacing: 0.02em;
  color: var(--fg);
}
.cs__body .cs-pillar > p {
  margin: 0 0 clamp(16px, 1.8vw, 24px);
}
.cs__body .cs-feature-list {
  margin: 0 0 1em;
  padding: 0;
  list-style: none;
}
.cs__body .cs-feature-list li {
  position: relative;
  margin: 0 0 clamp(10px, 1.2vw, 14px);
  padding: 10px 12px 10px 24px;
  border-top: 1px dashed color-mix(in oklab, var(--accent) 14%, transparent);
}
.cs__body .cs-feature-list li:last-child {
  border-bottom: 1px dashed color-mix(in oklab, var(--accent) 14%, transparent);
}
.cs__body .cs-feature-list li::before {
  content: '▸';
  position: absolute;
  left: 6px;
  top: 10px;
  color: var(--accent);
  font-weight: 700;
}
.cs__body .cs-feature-list li strong {
  color: var(--accent);
  letter-spacing: 0.02em;
}
.cs__body code {
  font-family: var(--font);
  font-size: 0.92em;
  padding: 1px 6px;
  background: color-mix(in oklab, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in oklab, var(--accent) 14%, transparent);
  color: var(--accent);
}

/* ---------- ep-stats / ep-bar-chart / ep-compare (Praxis-era charts) ----
   The Praxis Digital case study uses three editor-authored chart
   families inside the_content():
     .ep-stats  — 4-tile hero stat strip
     .ep-bar-chart — labelled animated bar chart
     .ep-compare — before/after data table (no bars)
   Scoped under .cs__body so they don't leak onto the homepage. */

/* 4-tile hero stat strip */
.cs__body .ep-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: clamp(10px, 1.2vw, 16px);
  margin: clamp(24px, 2.6vw, 36px) 0;
  padding: clamp(16px, 1.8vw, 22px);
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 3%, transparent);
}
.cs__body .ep-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px 0;
}
.cs__body .ep-stat strong {
  font-size: var(--fs-display);
  letter-spacing: 0.02em;
  color: var(--accent);
  text-shadow: 0 0 8px color-mix(in oklab, var(--accent) 24%, transparent);
}
.cs__body .ep-stat span {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

/* Labelled animated bar chart */
.cs__body .ep-bar-chart {
  margin: clamp(20px, 2.2vw, 28px) 0;
  padding: clamp(14px, 1.6vw, 20px);
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
}
.cs__body .ep-bar-chart__title {
  margin: 0 0 4px;
  font-size: var(--fs-heading);
  letter-spacing: 0.04em;
  color: var(--accent);
}
.cs__body .ep-bar-chart__caption {
  display: block;
  margin-bottom: 14px;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.cs__body .ep-bar-chart__note {
  margin: 8px 0 clamp(20px, 2.2vw, 28px);
  font-size: var(--fs-tertiary);
  color: var(--fg-mute);
  font-style: italic;
}
.cs__body .ep-bar {
  display: grid;
  grid-template-columns: minmax(0, 110px) minmax(0, 1fr) minmax(0, 64px);
  gap: 10px;
  align-items: center;
  padding: 6px 0;
}
.cs__body .ep-bar + .ep-bar {
  border-top: 1px dashed color-mix(in oklab, var(--accent) 14%, transparent);
}
.cs__body .ep-bar__label {
  font-size: var(--fs-secondary);
  color: var(--fg-soft);
  letter-spacing: 0.04em;
}
.cs__body .ep-bar__track {
  position: relative;
  height: 8px;
  background: color-mix(in oklab, var(--accent) 8%, transparent);
  overflow: hidden;
}
.cs__body .ep-bar__fill {
  display: block;
  height: 100%;
  background: var(--accent);
  box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 40%, transparent);
  /* Inline width="X%" sets the bar's actual width. We animate the
     reveal with clip-path so the IO toggle works without touching the
     inline style. */
  clip-path: inset(0 100% 0 0);
  transition: clip-path 1.4s var(--ease);
}
.cs__body .ep-bar-chart.is-in .ep-bar__fill {
  clip-path: inset(0 0 0 0);
}
@media (prefers-reduced-motion: reduce) {
  .cs__body .ep-bar__fill { clip-path: none; transition: none; }
}
.cs__body .ep-bar__value {
  text-align: right;
  font-weight: 700;
  font-size: var(--fs-secondary);
  color: var(--accent);
}

/* Before/after data table (no bars) */
.cs__body .ep-compare {
  margin: clamp(20px, 2.2vw, 28px) 0;
  padding: clamp(14px, 1.6vw, 20px);
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
}
.cs__body .ep-compare__title {
  margin: 0 0 14px;
  font-size: var(--fs-heading);
  letter-spacing: 0.04em;
  color: var(--accent);
}
.cs__body .ep-compare__row {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr));
  gap: 10px;
  align-items: center;
  padding: 8px 0;
  font-size: var(--fs-secondary);
}
.cs__body .ep-compare__row + .ep-compare__row {
  border-top: 1px dashed color-mix(in oklab, var(--accent) 14%, transparent);
}
.cs__body .ep-compare__label {
  color: var(--fg-soft);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.cs__body .ep-compare__before {
  color: var(--accent-dim);
  font-weight: 700;
}
.cs__body .ep-compare__after {
  color: var(--accent);
  font-weight: 700;
}
.cs__body .ep-compare__after::before {
  content: '→ ';
  opacity: 0.6;
  margin-right: 2px;
}
@media (max-width: 700px) {
  .cs__body .ep-compare__row {
    grid-template-columns: 1fr;
    gap: 2px;
  }
  .cs__body .ep-compare__label {
    color: var(--accent);
    margin-bottom: 2px;
  }
  .cs__body .ep-compare__before::before {
    content: 'Before: ';
    font-size: 10px;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--fg-mute);
    margin-right: 4px;
  }
  .cs__body .ep-compare__after::before {
    content: 'After: ';
    font-size: 10px;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--accent);
    margin-right: 4px;
    opacity: 1;
  }
}

/* ---------- Case-study colorisation ------------------------------
   Every chart, stat tile and scene-graph node on the homepage work
   cards + inside each case study deep-dive gets tinted by a
   per-case-study brand hue. Hovering the host card / article reverts
   to the monochrome accent so the data reads cleanly in the theme
   palette. Mirrors the hero reel's existing colorise-then-hover-revert
   pattern so the visual feels continuous from hero → card → article.

   Three accents:
     --cs-praxis    amber  → Praxis Digital
     --cs-3d        cyan   → 3D Configurator
     --cs-siv       green  → Major UK Beauty Brand sprint
*/

/* Homepage work cards — colorise the compare chart bar fills.
   `.work__item--<slug>` is set in template-parts/work.php. */
.work__item--praxis      { --cs-c: var(--cs-praxis); }
.work__item--3d          { --cs-c: var(--cs-3d); }
.work__item--soinvogue   { --cs-c: var(--cs-siv); }

.work__item .compare__bar--after .compare__bar-fill {
  background: var(--cs-c, var(--accent));
  box-shadow: 0 0 6px color-mix(in oklab, var(--cs-c, var(--accent)) 40%, transparent);
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.work__item .compare__bar-val--after { color: var(--cs-c, var(--accent)); transition: color 0.3s var(--ease); }
.work__item .compare__delta          { color: var(--cs-c, var(--accent-warm)); transition: color 0.3s var(--ease); }

/* Hover the card → bars revert to monochrome white. Before bars
   stay dim-grey regardless. */
.work__item:hover .compare__bar--after .compare__bar-fill {
  background: var(--accent);
  box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 40%, transparent);
}
.work__item:hover .compare__bar-val--after { color: var(--accent); }
.work__item:hover .compare__delta          { color: var(--accent-warm); }

/* Case-study article body — colorise inside .cs--<slug>. The slug
   modifier is emitted by single-case_study.php on the .cs section. */
.cs--praxis-digital-3d   { --cs-c: var(--cs-praxis); }
.cs--mmu-ktp             { --cs-c: var(--cs-3d); }
.cs--beauty-brand-sprint-2026      { --cs-c: var(--cs-siv); }

/* ep-stats — coloured stat numbers */
.cs[class*="cs--"] .cs__body .ep-stat strong {
  color: var(--cs-c, var(--accent));
  text-shadow: 0 0 8px color-mix(in oklab, var(--cs-c, var(--accent)) 26%, transparent);
  transition: color 0.3s var(--ease), text-shadow 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .ep-stats:hover .ep-stat strong {
  color: var(--accent);
  text-shadow: 0 0 8px color-mix(in oklab, var(--accent) 26%, transparent);
}

/* ep-bar-chart — coloured fills + accents (Praxis bars) */
.cs[class*="cs--"] .cs__body .ep-bar-chart .ep-bar-chart__title { color: var(--cs-c, var(--accent)); transition: color 0.3s var(--ease); }
.cs[class*="cs--"] .cs__body .ep-bar-chart .ep-bar__fill {
  background: var(--cs-c, var(--accent));
  box-shadow: 0 0 6px color-mix(in oklab, var(--cs-c, var(--accent)) 40%, transparent);
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .ep-bar-chart .ep-bar__value {
  color: var(--cs-c, var(--accent));
  transition: color 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .ep-bar-chart:hover .ep-bar-chart__title,
.cs[class*="cs--"] .cs__body .ep-bar-chart:hover .ep-bar__value {
  color: var(--accent);
}
.cs[class*="cs--"] .cs__body .ep-bar-chart:hover .ep-bar__fill {
  background: var(--accent);
  box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 40%, transparent);
}

/* ep-compare — coloured "after" values + title */
.cs[class*="cs--"] .cs__body .ep-compare .ep-compare__title { color: var(--cs-c, var(--accent)); transition: color 0.3s var(--ease); }
.cs[class*="cs--"] .cs__body .ep-compare .ep-compare__after { color: var(--cs-c, var(--accent)); transition: color 0.3s var(--ease); }
.cs[class*="cs--"] .cs__body .ep-compare .ep-compare__after::before { color: var(--cs-c, var(--accent)); }
.cs[class*="cs--"] .cs__body .ep-compare:hover .ep-compare__title,
.cs[class*="cs--"] .cs__body .ep-compare:hover .ep-compare__after,
.cs[class*="cs--"] .cs__body .ep-compare:hover .ep-compare__after::before {
  color: var(--accent);
}

/* ep-scene-graph — coloured node borders, titles, edge labels (3D Configurator) */
.cs[class*="cs--"] .cs__body .ep-scene-graph__title { color: var(--cs-c, var(--accent)); transition: color 0.3s var(--ease); }
.cs[class*="cs--"] .cs__body .ep-scene-graph__node {
  border-color: color-mix(in oklab, var(--cs-c, var(--accent)) 60%, transparent);
  background: color-mix(in oklab, var(--cs-c, var(--accent)) 8%, transparent);
  transition: border-color 0.3s var(--ease), background 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph__node-title {
  color: var(--cs-c, var(--accent));
  transition: color 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph__edge {
  color: var(--cs-c, var(--accent));
  transition: color 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph__footer {
  color: var(--cs-c, var(--accent));
  border-top-color: color-mix(in oklab, var(--cs-c, var(--accent)) 24%, transparent);
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph__node--core {
  background: color-mix(in oklab, var(--cs-c, var(--accent)) 18%, transparent);
  border-color: color-mix(in oklab, var(--cs-c, var(--accent)) 90%, transparent);
  box-shadow: 0 0 12px color-mix(in oklab, var(--cs-c, var(--accent)) 22%, transparent);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph__spokes .ep-scene-graph__node::before {
  color: var(--cs-c, var(--accent));
}
.cs[class*="cs--"] .cs__body .ep-scene-graph:hover .ep-scene-graph__title,
.cs[class*="cs--"] .cs__body .ep-scene-graph:hover .ep-scene-graph__node-title,
.cs[class*="cs--"] .cs__body .ep-scene-graph:hover .ep-scene-graph__edge,
.cs[class*="cs--"] .cs__body .ep-scene-graph:hover .ep-scene-graph__footer {
  color: var(--accent);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph:hover .ep-scene-graph__node {
  border-color: color-mix(in oklab, var(--accent) 36%, transparent);
  background: color-mix(in oklab, var(--accent) 5%, transparent);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph:hover .ep-scene-graph__node--core {
  background: color-mix(in oklab, var(--accent) 14%, transparent);
  border-color: color-mix(in oklab, var(--accent) 60%, transparent);
  box-shadow: 0 0 12px color-mix(in oklab, var(--accent) 18%, transparent);
}
.cs[class*="cs--"] .cs__body .ep-scene-graph:hover .ep-scene-graph__footer {
  border-top-color: color-mix(in oklab, var(--accent) 14%, transparent);
}

/* cs-fact-card — coloured CTA box accents (3D Configurator) */
.cs[class*="cs--"] .cs__body .cs-fact-card {
  border-color: color-mix(in oklab, var(--cs-c, var(--accent)) 50%, transparent);
  background: color-mix(in oklab, var(--cs-c, var(--accent)) 6%, transparent);
  box-shadow: 0 0 16px color-mix(in oklab, var(--cs-c, var(--accent)) 16%, transparent);
  transition: border-color 0.3s var(--ease), background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .cs-fact-card h3 {
  color: var(--cs-c, var(--accent));
  transition: color 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .cs-fact-card__cta {
  border-color: color-mix(in oklab, var(--cs-c, var(--accent)) 70%, transparent);
  color: var(--cs-c, var(--accent));
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease), background 0.3s var(--ease);
}
.cs[class*="cs--"] .cs__body .cs-fact-card__cta:hover,
.cs[class*="cs--"] .cs__body .cs-fact-card__cta:focus-visible {
  background: var(--cs-c, var(--accent));
  color: var(--bg);
}
.cs[class*="cs--"] .cs__body .cs-fact-card:hover h3 {
  color: var(--accent);
}

/* Dashboard — already inside .cs--beauty-brand-sprint-2026, but the
   .compare bars need explicit colorisation too (the homepage rules
   above target .work__item, not the dashboard). */
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare__bar--after .compare__bar-fill {
  background: var(--cs-siv);
  box-shadow: 0 0 6px color-mix(in oklab, var(--cs-siv) 40%, transparent);
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare__bar-val--after { color: var(--cs-siv); transition: color 0.3s var(--ease); }
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare__title { color: var(--cs-siv); transition: color 0.3s var(--ease); }
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare__delta { color: var(--cs-siv); transition: color 0.3s var(--ease); }
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare:hover .compare__bar--after .compare__bar-fill {
  background: var(--accent);
  box-shadow: 0 0 6px color-mix(in oklab, var(--accent) 40%, transparent);
}
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare:hover .compare__bar-val--after,
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare:hover .compare__title,
.cs--beauty-brand-sprint-2026 .cs__dashboard .compare:hover .compare__delta {
  color: var(--accent);
}

/* Dashboard architecture grid — tint the "after" column */
.cs--beauty-brand-sprint-2026 .cs__arch-title       { color: var(--cs-siv); transition: color 0.3s var(--ease); }
.cs--beauty-brand-sprint-2026 .cs__arch-h:nth-child(3) { color: var(--cs-siv); transition: color 0.3s var(--ease); }
.cs--beauty-brand-sprint-2026 .cs__arch-cell--after::before { color: var(--cs-siv); transition: color 0.3s var(--ease); }
.cs--beauty-brand-sprint-2026 .cs__arch:hover .cs__arch-title,
.cs--beauty-brand-sprint-2026 .cs__arch:hover .cs__arch-h:nth-child(3),
.cs--beauty-brand-sprint-2026 .cs__arch:hover .cs__arch-cell--after::before {
  color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
  .work__item .compare__bar--after .compare__bar-fill,
  .cs[class*="cs--"] .cs__body .ep-bar__fill,
  .cs[class*="cs--"] .cs__body .ep-scene-graph__node,
  .cs[class*="cs--"] .cs__body .cs-fact-card,
  .cs--beauty-brand-sprint-2026 .cs__dashboard .compare__bar-fill {
    transition: none;
  }
}

/* ---------- ep-scene-graph (3D Configurator pipeline diagrams) -------
   Two layouts:
     .ep-scene-graph__flow — horizontal chain of nodes with arrow
       labels between them (KTP pipeline, URL boot pipeline)
     .ep-scene-graph__hub — one highlighted core node + grid of spokes
       (One platform, five surfaces)
   Wraps to a vertical stack under 700px so each node remains
   readable on mobile. */
.cs__body .ep-scene-graph {
  margin: clamp(28px, 3vw, 40px) 0;
  padding: clamp(18px, 2vw, 24px);
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
  background: color-mix(in oklab, var(--accent) 2%, transparent);
}
.cs__body .ep-scene-graph__title {
  margin: 0 0 4px;
  font-size: var(--fs-heading);
  letter-spacing: 0.04em;
  color: var(--accent);
}
.cs__body .ep-scene-graph__caption {
  display: block;
  margin-bottom: clamp(14px, 1.6vw, 20px);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.cs__body .ep-scene-graph__footer {
  display: block;
  margin-top: clamp(12px, 1.4vw, 18px);
  padding-top: 12px;
  border-top: 1px dashed color-mix(in oklab, var(--accent) 14%, transparent);
  font-size: var(--fs-tertiary);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--accent);
  text-align: center;
}

/* Horizontal flow: nodes + edge labels */
.cs__body .ep-scene-graph__flow {
  display: flex;
  align-items: stretch;
  gap: clamp(6px, 0.8vw, 12px);
  margin: 0;
  padding: 0;
  list-style: none;
  flex-wrap: wrap;
}
.cs__body .ep-scene-graph__node {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: clamp(10px, 1.2vw, 14px) clamp(10px, 1.2vw, 14px);
  border: 1px solid color-mix(in oklab, var(--accent) 36%, transparent);
  background: color-mix(in oklab, var(--accent) 5%, transparent);
}
.cs__body .ep-scene-graph__node-title {
  font-size: var(--fs-secondary);
  color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.cs__body .ep-scene-graph__node-desc {
  font-size: var(--fs-tertiary);
  color: var(--fg-soft);
  line-height: 1.4;
}
.cs__body .ep-scene-graph__edge {
  align-self: center;
  flex: 0 0 auto;
  padding: 0 4px;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}

/* Hub-and-spoke layout */
.cs__body .ep-scene-graph__hub {
  display: grid;
  grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
  gap: clamp(12px, 1.4vw, 18px);
  align-items: stretch;
}
.cs__body .ep-scene-graph__node--core {
  background: color-mix(in oklab, var(--accent) 14%, transparent);
  border-color: color-mix(in oklab, var(--accent) 60%, transparent);
  box-shadow: 0 0 12px color-mix(in oklab, var(--accent) 18%, transparent);
  justify-content: center;
}
.cs__body .ep-scene-graph__node--core .ep-scene-graph__node-title {
  color: var(--accent);
  text-shadow: 0 0 6px color-mix(in oklab, var(--accent) 30%, transparent);
}
.cs__body .ep-scene-graph__spokes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  position: relative;
}
.cs__body .ep-scene-graph__spokes .ep-scene-graph__node {
  position: relative;
  padding-left: clamp(16px, 1.6vw, 22px);
}
.cs__body .ep-scene-graph__spokes .ep-scene-graph__node::before {
  content: '↳';
  position: absolute;
  left: 6px;
  top: 10px;
  color: var(--accent);
  opacity: 0.6;
  font-weight: 700;
}

@media (max-width: 700px) {
  .cs__body .ep-scene-graph__flow {
    flex-direction: column;
  }
  .cs__body .ep-scene-graph__flow .ep-scene-graph__node {
    flex: 1 1 auto;
  }
  .cs__body .ep-scene-graph__edge {
    align-self: flex-start;
    padding: 2px 8px;
  }
  .cs__body .ep-scene-graph__hub {
    grid-template-columns: 1fr;
  }
}

/* ---------- cs-fact-card (final CTA box, 3D Configurator) ----------
   Used at the bottom of the 3D Configurator case study to surface the
   live demo link + the get-in-touch link for v4. */
.cs__body .cs-fact-card {
  margin: clamp(32px, 3.5vw, 48px) 0;
  padding: clamp(20px, 2.4vw, 28px);
  border: 1px solid color-mix(in oklab, var(--accent) 36%, transparent);
  background: color-mix(in oklab, var(--accent) 5%, transparent);
  box-shadow: 0 0 16px color-mix(in oklab, var(--accent) 12%, transparent);
}
.cs__body .cs-fact-card h3 {
  margin: 0 0 10px;
  font-size: var(--fs-heading);
  letter-spacing: 0.04em;
  color: var(--accent);
}
.cs__body .cs-fact-card > p {
  margin: 0 0 14px;
  font-size: var(--fs-secondary);
  color: var(--fg-soft);
  line-height: 1.55;
}
.cs__body .cs-fact-card > p:last-child { margin-bottom: 0; }
.cs__body .cs-fact-card__cta {
  display: inline-block;
  margin-top: 8px;
  padding: 8px 14px;
  border: 1px solid color-mix(in oklab, var(--accent) 60%, transparent);
  background: transparent;
  color: var(--accent);
  text-decoration: none;
  font-size: var(--fs-secondary);
  letter-spacing: 0.04em;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}
.cs__body .cs-fact-card__cta:hover,
.cs__body .cs-fact-card__cta:focus-visible {
  background: var(--accent);
  color: var(--bg);
  box-shadow: 0 0 8px color-mix(in oklab, var(--accent) 40%, transparent);
}
.cs__body .cs-fact-card__cta--secondary {
  border-color: color-mix(in oklab, var(--accent) 30%, transparent);
  color: var(--fg-soft);
}

/* Standalone link row outside fact-cards (MMU thesis link, etc.) */
.cs__body .ep-link-row {
  margin: clamp(14px, 1.6vw, 20px) 0 clamp(24px, 2.4vw, 32px);
  font-size: var(--fs-secondary);
}
.cs__body .ep-link-row a {
  color: var(--accent);
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  padding-bottom: 1px;
  transition: border-color 0.18s var(--ease);
}
.cs__body .ep-link-row a:hover {
  border-bottom-color: var(--accent);
}

/* ---------- Before/after comparison slider (ep-ba-slider) ----------
   Used inside the_content() of the beauty-brand sprint (and any case study that
   pairs old/new screenshots). Ported from the V2 theme so the slider
   keeps its drag-to-wipe + vertical-scroll-to-inspect behaviour under
   the terminal theme. JS lives in main.js as the baSliders IIFE.

   Layout: FRAME = fixed visible height with the handle + chips fixed
   to its edges. SCROLL = absolute container inside the frame that
   takes overflow-y:auto so tall page screenshots can be panned. The
   handle reads --ba-pos from the frame; the before layer clips with
   clip-path to whichever side of the wipe it occupies. */
.ep-ba-slider { margin: clamp(32px, 4vh, 48px) 0; }
.ep-ba-slider__frame {
  position: relative;
  height: clamp(480px, 75vh, 820px);
  overflow: hidden;
  border-radius: clamp(12px, 1.5vw, 18px);
  background: oklch(0.12 0 0);
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  cursor: ew-resize;
  isolation: isolate;
  --ba-pos: 50%;
  contain: paint;
}
.ep-ba-slider__scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in oklab, var(--accent) 30%, transparent) transparent;
}
.ep-ba-slider__scroll::-webkit-scrollbar { width: 6px; }
.ep-ba-slider__scroll::-webkit-scrollbar-track { background: transparent; }
.ep-ba-slider__scroll::-webkit-scrollbar-thumb {
  background: color-mix(in oklab, var(--accent) 30%, transparent);
  border-radius: 3px;
}
.ep-ba-slider__canvas {
  position: relative;
  width: 100%;
  /* aspect-ratio set inline per-pair (e.g. style="aspect-ratio: 1920/7006") */
}
.ep-ba-slider__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
  object-position: top center;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
  will-change: transform;
  transform: translateZ(0);
}
.ep-ba-slider__layer--after { z-index: 1; }
.ep-ba-slider__before-clip {
  position: absolute;
  inset: 0;
  z-index: 2;
  clip-path: inset(0 calc(100% - var(--ba-pos)) 0 0);
  -webkit-clip-path: inset(0 calc(100% - var(--ba-pos)) 0 0);
  pointer-events: none;
  will-change: clip-path;
  transform: translateZ(0);
}
.ep-ba-slider__chip {
  position: absolute;
  top: clamp(10px, 1.2vw, 14px);
  z-index: 10;
  padding: 4px 10px;
  font-family: var(--font);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border-radius: 999px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  pointer-events: none;
  transition: opacity 0.3s ease;
}
.ep-ba-slider__chip--before { left: clamp(10px, 1.2vw, 14px); }
.ep-ba-slider__chip--after  { right: clamp(10px, 1.2vw, 14px); }
.ep-ba-slider__frame[style*="--ba-pos: 0%"]  .ep-ba-slider__chip--before,
.ep-ba-slider__frame[style*="--ba-pos: 100%"] .ep-ba-slider__chip--after { opacity: 0.3; }

.ep-ba-slider__handle {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--ba-pos);
  transform: translateX(-50%);
  width: 44px;
  background: transparent;
  border: 0; padding: 0; margin: 0;
  z-index: 11;
  cursor: ew-resize;
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.ep-ba-slider__handle:focus { outline: none; }
.ep-ba-slider__handle:focus-visible .ep-ba-slider__handle-grip {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 50%, transparent),
              0 4px 16px rgba(0,0,0,0.3);
}
.ep-ba-slider__handle-line {
  position: absolute;
  top: 0; bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: var(--accent);
  pointer-events: none;
}
.ep-ba-slider__handle-grip {
  position: relative;
  width: 40px; height: 40px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  pointer-events: none;
  transition: transform 0.2s var(--ease);
}
.ep-ba-slider__frame:hover .ep-ba-slider__handle-grip,
.ep-ba-slider__handle:focus-visible .ep-ba-slider__handle-grip {
  transform: scale(1.08);
}
.ep-ba-slider__caption {
  display: block;
  margin-top: clamp(10px, 1.2vh, 14px);
  font-family: var(--font);
  font-size: var(--fs-tertiary);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
@media (max-width: 700px) {
  .ep-ba-slider__frame { height: clamp(360px, 65vh, 560px); }
}

/* ---------- Footer ---------- */
.footer {
  margin: clamp(40px, 5vw, 80px) auto clamp(20px, 3vw, 40px);
  text-align: center; font-size: var(--fs-secondary);
  color: var(--fg-mute); letter-spacing: 0.08em;
  display: flex; flex-direction: column; gap: 8px;
}
.footer__line::before { content: '[ '; color: var(--accent); }
.footer__line::after  { content: ' ]'; color: var(--accent); }
.footer__line--meta {
  font-size: var(--fs-tertiary);
  color: var(--fg-mute);
  letter-spacing: 0.06em;
  opacity: 0.85;
}
.footer__line--meta a {
  color: var(--fg-soft);
  text-decoration: underline;
  text-decoration-color: color-mix(in oklab, var(--fg-mute) 50%, transparent);
  text-underline-offset: 3px;
  transition: color 0.18s var(--ease);
}
.footer__line--meta a:hover { color: var(--accent); }

/* ---------- Type scale utility classes ----------
   Apply these via markup when a specific text needs to step up or
   down without writing a custom rule. The CSS components above
   already reference the same tokens directly. */
.t-display   { font-size: var(--fs-display); }
.t-heading   { font-size: var(--fs-heading); }
.t-primary   { font-size: var(--fs-primary); }
.t-secondary { font-size: var(--fs-secondary); }
.t-tertiary  { font-size: var(--fs-tertiary); }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .section { opacity: 1; transform: none; }
  .gauge__fill, .skill-bar__fill, .compare__bar-fill { width: var(--w, 100%) !important; }
  .service-card__chips span { opacity: 1 !important; transform: none !important; }
  /* JS bails on typewriter under reduced-motion, but if the cursor pseudo
     ever leaks through, kill the blink. */
  .cmd--pending::after, .cmd--typing::after { content: none; }
  /* The glitch tear stripe is animation-only; the * { animation: none }
     rule already gates it, but pin opacity to 0 in case it leaks. */
  .work__item::before, .service-card::before { opacity: 0 !important; }
  /* Reel progress bar: animation is killed by the global rule above.
     Show the active segment as fully filled so the user still sees
     which slide they're on. */
  .reel__dot.is-on .reel__dot-fill { width: 100% !important; }
  .reel__corebar-fill { width: 100% !important; }
  /* Chart bars: skip the height animation, render at target height. */
  .reel__slide.is-on .reel__chart-fill { height: var(--h, 50%) !important; }
  .reel__slide.is-on .reel__chart-val { opacity: 1 !important; transform: none !important; }
}

/* ---------- Cookie consent banner ----------
   Hidden by default; main.js reveals it when no consent record exists
   in localStorage. Terminal-themed chrome to match the section cards.
   Sits fixed at bottom-center, slides up on reveal. */
.cookie-banner {
  position: fixed;
  left: 50%;
  bottom: clamp(16px, 2vw, 28px);
  z-index: 9990;
  width: min(640px, calc(100vw - 32px));
  transform: translate(-50%, 30px);
  opacity: 0;
  transition: transform 0.35s var(--ease), opacity 0.35s var(--ease);
  pointer-events: none;
}
.cookie-banner[hidden] { display: none; }
.cookie-banner.is-shown {
  transform: translate(-50%, 0);
  opacity: 1;
  pointer-events: auto;
}
.cookie-banner__inner {
  background: var(--bg-pane);
  border: 1px solid color-mix(in oklab, var(--accent) 36%, transparent);
  box-shadow:
    0 0 24px color-mix(in oklab, var(--accent) 14%, transparent),
    0 6px 36px rgb(0 0 0 / 0.5);
}
.cookie-banner__titlebar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: color-mix(in oklab, var(--accent) 6%, var(--bg));
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
}
.cookie-banner__dots { display: inline-flex; gap: 6px; }
.cookie-banner__dot {
  width: 10px; height: 10px;
  display: inline-block;
}
.cookie-banner__dot--close { background: #ff5f56; }
.cookie-banner__dot--min   { background: #ffbd2e; }
.cookie-banner__dot--max   { background: #27c93f; }
.cookie-banner__title {
  font-size: var(--fs-tertiary);
  letter-spacing: 0.06em;
  color: var(--fg-mute);
  text-transform: lowercase;
}
.cookie-banner__body {
  padding: clamp(14px, 1.8vw, 20px);
}
.cookie-banner__line {
  margin: 0 0 10px;
  font-size: var(--fs-secondary);
  font-family: var(--font);
}
.cookie-banner__prompt {
  color: var(--accent);
  margin-right: 8px;
}
.cookie-banner__cmd {
  color: var(--fg-soft);
}
.cookie-banner__copy {
  margin: 0 0 clamp(14px, 1.6vw, 18px);
  font-size: var(--fs-secondary);
  line-height: 1.55;
  color: color-mix(in oklab, var(--fg) 88%, transparent);
}
.cookie-banner__copy a {
  color: var(--accent);
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
}
.cookie-banner__copy a:hover {
  border-bottom-color: var(--accent);
}
.cookie-banner__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: flex-end;
}
.cookie-banner__btn {
  font-family: var(--font);
  font-size: var(--fs-secondary);
  padding: 8px 14px;
  background: transparent;
  cursor: pointer;
  letter-spacing: 0.04em;
  transition: background 0.18s var(--ease), color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.cookie-banner__btn--reject {
  border: 1px solid color-mix(in oklab, var(--accent) 36%, transparent);
  color: var(--fg-soft);
}
.cookie-banner__btn--reject:hover,
.cookie-banner__btn--reject:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
}
.cookie-banner__btn--accept {
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--bg);
}
.cookie-banner__btn--accept:hover,
.cookie-banner__btn--accept:focus-visible {
  box-shadow:
    0 0  6px color-mix(in oklab, var(--accent) 50%, transparent),
    0 0 16px color-mix(in oklab, var(--accent) 22%, transparent);
}
@media (max-width: 480px) {
  .cookie-banner__actions {
    flex-direction: column-reverse;
  }
  .cookie-banner__btn { width: 100%; text-align: center; }
}
@media (prefers-reduced-motion: reduce) {
  .cookie-banner { transition: none; }
}

/* Tiny revisit link in the footer — "Cookie settings" */
.footer__cookie-link {
  background: none;
  border: none;
  color: var(--fg-mute);
  font-family: var(--font);
  font-size: var(--fs-tertiary);
  letter-spacing: 0.06em;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
  text-decoration-color: color-mix(in oklab, var(--fg-mute) 50%, transparent);
  text-underline-offset: 3px;
  transition: color 0.18s var(--ease);
}
.footer__cookie-link:hover { color: var(--accent); }

/* Inline privacy microcopy under the contact details in the CTA section. */
.cta__privacy-note {
  margin-top: clamp(14px, 1.6vw, 20px);
  font-size: var(--fs-tertiary);
  color: var(--fg-mute);
  letter-spacing: 0.04em;
  line-height: 1.5;
  max-width: 56ch;
}
.cta__privacy-note a {
  color: var(--fg-soft);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in oklab, var(--accent) 30%, transparent);
}
.cta__privacy-note a:hover {
  color: var(--accent);
}

/* ---------- Lead-capture contact form modal ----------
   Triggered by any element with [data-open-form] — the header
   "Book now" + the bottom "Book a discovery call" CTAs both carry
   the attribute. Uses <dialog> for native modal behaviour (focus
   trap + ESC to close come for free in modern browsers); falls
   back gracefully if dialog isn't supported. */
.contact-form {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  padding: 0;
  border: 1px solid color-mix(in oklab, var(--accent) 36%, transparent);
  background: var(--bg-pane);
  color: var(--fg);
  box-shadow:
    0 0 24px color-mix(in oklab, var(--accent) 16%, transparent),
    0 12px 48px rgb(0 0 0 / 0.6);
  z-index: 9995;
  overflow: auto;
}
.contact-form::backdrop {
  background: rgb(0 0 0 / 0.7);
  backdrop-filter: blur(2px);
}
.contact-form[open] {
  display: block;
}
.contact-form:not([open]) {
  display: none;
}
.contact-form__inner {
  display: block;
}
.contact-form__titlebar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: color-mix(in oklab, var(--accent) 6%, var(--bg));
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
}
.contact-form__dots { display: inline-flex; gap: 6px; }
.contact-form__dot {
  width: 12px; height: 12px;
  display: inline-block;
  border: 0; padding: 0;
}
.contact-form__dot--close { background: #ff5f56; cursor: pointer; }
.contact-form__dot--close:hover { box-shadow: 0 0 0 2px rgb(255 95 86 / 0.4); }
.contact-form__dot--min   { background: #ffbd2e; }
.contact-form__dot--max   { background: #27c93f; }
.contact-form__title {
  font-size: var(--fs-tertiary);
  letter-spacing: 0.06em;
  color: var(--fg-mute);
  text-transform: lowercase;
}
.contact-form__body {
  padding: clamp(18px, 2.2vw, 26px);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.contact-form__line {
  margin: 0;
  font-size: var(--fs-secondary);
}
.contact-form__prompt {
  color: var(--accent);
  margin-right: 8px;
}
.contact-form__cmd { color: var(--fg-soft); }
.contact-form__lede {
  margin: 0 0 6px;
  font-size: var(--fs-secondary);
  line-height: 1.55;
  color: color-mix(in oklab, var(--fg) 88%, transparent);
}
.contact-form__row {
  display: flex; flex-direction: column; gap: 6px;
}
.contact-form__label {
  font-size: var(--fs-tertiary);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--fg-soft);
}
.contact-form__req { color: var(--accent-warm); }
.contact-form__input {
  font-family: var(--font);
  font-size: var(--fs-primary);
  padding: 10px 12px;
  background: color-mix(in oklab, var(--bg) 100%, transparent);
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  color: var(--fg);
  width: 100%;
  outline: none;
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
.contact-form__input:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 20%, transparent);
}
.contact-form__input::placeholder {
  color: var(--fg-mute);
  font-style: italic;
}
.contact-form__input--textarea {
  resize: vertical;
  min-height: 100px;
  line-height: 1.5;
}
.contact-form__consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px;
  align-items: start;
  font-size: var(--fs-secondary);
  color: var(--fg-soft);
  line-height: 1.5;
  cursor: pointer;
}
.contact-form__consent input[type=checkbox] {
  margin-top: 3px;
  width: 16px; height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
}
.contact-form__consent a {
  color: var(--accent);
  border-bottom: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
}
.contact-form__consent a:hover { border-bottom-color: var(--accent); }
.contact-form__honeypot {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}
.contact-form__actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 4px;
  flex-wrap: wrap;
}
.contact-form__btn {
  font-family: var(--font);
  font-size: var(--fs-secondary);
  padding: 9px 16px;
  background: transparent;
  cursor: pointer;
  letter-spacing: 0.04em;
  transition: background 0.18s var(--ease), color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.contact-form__btn--cancel {
  border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
  color: var(--fg-soft);
}
.contact-form__btn--cancel:hover { border-color: var(--accent); color: var(--accent); }
.contact-form__btn--submit {
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--bg);
}
.contact-form__btn--submit:hover,
.contact-form__btn--submit:focus-visible {
  box-shadow:
    0 0  6px color-mix(in oklab, var(--accent) 50%, transparent),
    0 0 16px color-mix(in oklab, var(--accent) 22%, transparent);
}
.contact-form__btn--submit[disabled] {
  opacity: 0.5;
  cursor: progress;
}
.contact-form__status {
  margin: 4px 0 0;
  font-size: var(--fs-secondary);
  min-height: 1.5em;
}
.contact-form__status.is-error   { color: var(--accent-warm); }
.contact-form__status.is-success { color: var(--accent); }

@media (max-width: 480px) {
  .contact-form__actions {
    flex-direction: column-reverse;
  }
  .contact-form__btn { width: 100%; text-align: center; }
}

