/* ==========================================================================
   DIGITALLY SOUND — Editorial Light + Spectrum
   Warm bone (#eeece9) + ink (#1e1e1e). Rainbow accents drawn from the logo.
   Fraunces (display) · Space Grotesk (UI) · Space Mono (labels)
   ========================================================================== */

/* ---- Fonts ---------------------------------------------------------------
   Self-hosted rather than pulled from fonts.googleapis.com. Two third-party
   requests per page are gone (one of them render-blocking), and no visitor's
   IP is handed to Google just for loading a typeface - which is what got
   hotlinked Google Fonts ruled a GDPR breach in Germany.

   Fraunces and Space Grotesk are variable fonts, so one file covers the whole
   weight range the site uses. Fraunces keeps its optical-size axis because
   .display sets font-optical-sizing: auto. The latin-ext faces only download
   if a page actually contains one of those characters, which is what the
   unicode-range is for.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/fraunces-latin-opsz-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/fraunces-latin-opsz-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/fraunces-latin-ext-opsz-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/fraunces-latin-ext-opsz-italic.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("fonts/space-grotesk-latin-wght-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("fonts/space-grotesk-latin-ext-wght-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
  font-family: "Space Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/space-mono-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
  font-family: "Space Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/space-mono-latin-700-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

@font-face {
  font-family: "Space Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/space-mono-latin-ext-400-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

@font-face {
  font-family: "Space Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/space-mono-latin-ext-700-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}


/* ---- Reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
img, svg, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul { list-style: none; }

/* ---- Tokens --------------------------------------------------------------- */
:root {
  --bone:     #eeece9;   /* page background */
  --bone-2:   #e6e3dd;   /* raised surface */
  --bone-3:   #ddd9d2;   /* media / placeholder base */
  --ink:      #1e1e1e;   /* primary text */
  --ink-2:    #171717;   /* dark surface (menu) */
  --ink-3:    #232323;
  --bone-dim: #5f5c55;   /* secondary text */
  --bone-mute:#625f57;   /* tertiary text - >=4.5:1 on bone, bone-2 and bone-3 (was #98958c, 2.54:1) */
  --line:     rgba(30, 30, 30, 0.15);
  --line-soft:rgba(30, 30, 30, 0.08);

  /* Spectrum — sampled from the Digitally Sound logo */
  --c-red:    #e63329;
  --c-orange: #f47b20;
  --c-yellow: #f4b70e;
  --c-green:  #59b73a;
  --c-teal:   #12b0a6;
  --c-blue:   #2f74d0;
  --c-indigo: #6a3fd0;
  --c-magenta:#d6389b;
  --c-pink:   #ff4f9a;
  --c-red-ink:#d02218;   /* text-safe red - 4.54:1 on bone; kept for --spectrum-text */
  --c-teal-ink:#0c7771;  /* text-safe teal - 4.58:1 on bone (brand #12b0a6 is 2.29:1) */
  --c-orange-ink:#ae4f09; /* text-safe orange - 4.57:1 on bone (brand #f47b20 is 2.33:1) */
  --c-indigo-ink:#693ed0; /* text-safe indigo - 5.53:1 on bone */
  --c-green-ink:#3a7726;  /* text-safe green - 4.63:1 on bone (brand #59b73a is 2.16:1) */
  --c-blue-ink:#2a68bb;   /* text-safe blue - 4.69:1 on bone (brand #2f74d0 is 3.93:1) */
  --c-magenta-ink:#be2786;/* text-safe magenta - 4.66:1 on bone (brand #d6389b is 3.64:1) */

  /* Accent is teal, not red: red reads as stop / error / slow, which pulls
     against the plain-English, safe-hands tone the copy sets. */
  --accent:   var(--c-teal-ink);
  --spectrum: linear-gradient(90deg, #e63329, #f47b20, #f4b70e, #59b73a, #12b0a6, #2f74d0, #6a3fd0, #d6389b);
  /* Text-safe spectrum: the same hues darkened so every stop clears 4.5:1 on
     bone. NOT currently applied - the vivid --spectrum is used for gradient
     text by design choice, which is a known, accepted WCAG 1.4.3 exception
     (the yellow stop reads about 1.53:1). Swap it in on `em` and `.grad-text`
     to make gradient text AA-compliant. */
  --spectrum-text: linear-gradient(90deg, #d02218, #ae4f09, #886506, #3a7826, #0c7770, #2b6abf, #693ed0, #c12888);

  /* Gradient text on a dark surface. The vivid ramp falls to 2.57:1 at the
     indigo stop against --ink, below even the 3:1 large-text floor. Same
     hues lifted; every stop clears 4.5:1 on ink. Used by .band and by the
     Dark display preference. */
  --spectrum-on-dark: linear-gradient(90deg, #ff6f61, #f79b4a, #e0bf4a, #7fd45f, #3fc9bd, #6fa3ee, #a184ee, #f072bd);

  /* Focus ring */
  --focus-ring:        #1e1e1e;
  --focus-ring-invert: #eeece9;

  --spectrum-conic: conic-gradient(from 210deg, #e63329, #f47b20, #f4b70e, #59b73a, #12b0a6, #2f74d0, #6a3fd0, #d6389b, #e63329);

  --font-display: "Fraunces", Georgia, "Times New Roman", serif;
  --font-ui:      "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono:    "Space Mono", ui-monospace, "SFMono-Regular", monospace;

  /* The scale stopped at --step--1, but six rules were already asking for a
     --step--2. Two carried a fallback and quietly worked; four did not, and an
     undefined var() with no fallback does not fall back to the parent rule, it
     inherits from the parent ELEMENT: those four were rendering at 18.4px, well
     above the --step--1 they were meant to sit under. Defining it fixes all six
     and keeps the two that already had a fallback within a hair of where they
     were. */
  --step--2: clamp(0.68rem, 0.645rem + 0.18vw, 0.8rem);
  --step--1: clamp(0.78rem, 0.74rem + 0.2vw, 0.9rem);
  --step-0:  clamp(1rem, 0.95rem + 0.25vw, 1.15rem);
  --step-1:  clamp(1.35rem, 1.2rem + 0.7vw, 1.75rem);
  --step-2:  clamp(2rem, 1.6rem + 2vw, 3.25rem);
  --step-3:  clamp(3rem, 2rem + 5vw, 6rem);
  --step-4:  clamp(4rem, 2rem + 10vw, 12rem);

  --gutter: clamp(1.25rem, 4vw, 4rem);
  --maxw:   1600px;
  --ease:   cubic-bezier(0.22, 1, 0.36, 1);
  --dur:    0.7s;
}

/* ---- Base ----------------------------------------------------------------- */
html { scroll-behavior: smooth; }
body {
  background: var(--bone);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--step-0);
  line-height: 1.5;
  overflow-x: hidden;
  cursor: none;
}
@media (hover: none) { body { cursor: auto; } }

::selection { background: var(--accent); color: #fff; }

/* Spectrum brand ribbon, pinned to the very top */
html::before {
  content: ""; position: fixed; top: 0; left: 0; right: 0; height: 4px; z-index: 300;
  background: var(--spectrum); background-size: 200% 100%;
  animation: ribbon 14s linear infinite;
}
@keyframes ribbon { to { background-position: 200% 0; } }

/* Film grain */
body::after {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 9998;
  opacity: 0.04; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---- Layout --------------------------------------------------------------- */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: clamp(4.5rem, 10vw, 11rem); position: relative; }
.divider { position: relative; border-top: 1px solid transparent; }
.divider::before { content: ""; position: absolute; top: -1px; left: 0; right: 0; height: 1px; background: var(--line); transform: scaleX(0); transform-origin: left; transition: transform 1.2s var(--ease); }
.divider.line-in::before { transform: scaleX(1); }

/* ---- Typography ----------------------------------------------------------- */
.display { font-family: var(--font-display); font-weight: 340; line-height: 0.92; letter-spacing: -0.03em; font-optical-sizing: auto; }
.display em { font-style: italic; font-weight: 340; }
.d-xl { font-size: var(--step-4); }
.d-lg { font-size: var(--step-3); }
.d-md { font-size: var(--step-2); }
.d-sm { font-size: var(--step-1); }  /* a sub-heading inside a section whose h2 is already d-md */

.eyebrow {
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.22em; color: var(--bone-dim);
  display: inline-flex; align-items: center; gap: 0.6em;
}
.eyebrow::before { content: ""; width: 1.8em; height: 3px; border-radius: 2px; background: var(--spectrum); }
.lead { font-size: var(--step-1); line-height: 1.35; color: var(--ink); max-width: 34ch; }
.muted { color: var(--bone-dim); }
/* Answer-first "in short" facts row on the service pages (AEO quick answer) */
.answer-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1.4rem 2rem; }
.answer-facts .lbl { display: block; font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.1em; color: var(--bone-dim); margin-bottom: 0.35rem; }
.answer-facts p { max-width: 26ch; }
.answer-facts .link { font-family: var(--font-mono); font-size: var(--step--1); }
.accent-text { color: var(--accent); }
/* Every emphasised (italic) word uses the spectrum gradient, site-wide. */
em {
  font-style: italic;
  background: var(--spectrum);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  /* Give the gradient paint-box room so italic overhang (slanted tops and
     bottom-left descenders) isn't clipped. Negative margins keep text flow. */
  padding: 0.06em 0.14em 0.3em; margin: 0 -0.14em;
}
.grad-text {
  background: var(--spectrum); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent; font-style: italic;
  display: inline-block; padding: 0.34em 0.14em 0.32em 16px; margin: -0.34em -0.14em -0.32em -16px;
}

/* ---- Buttons -------------------------------------------------------------- */
.btn {
  --pad: 1.05em; position: relative; display: inline-flex; align-items: center; gap: 0.7em;
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.14em;
  padding: var(--pad) calc(var(--pad) * 1.6);
  border: 1px solid var(--ink); border-radius: 100px; overflow: hidden;
  transition: color 0.5s var(--ease), border-color 0.5s var(--ease); will-change: transform;
}
.btn::before { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--ink); border-radius: 100px; transform: translateY(101%); transition: transform 0.55s var(--ease); }
.btn:hover { color: var(--bone); border-color: var(--ink); }
.btn:hover::before { transform: translateY(0); }
.btn .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--spectrum-conic); }
.btn--solid { background: var(--ink); border-color: var(--ink); color: var(--bone); }
.btn--solid::before { background: var(--spectrum); }
.btn--solid:hover { color: #fff; border-color: transparent; }
.btn--solid .dot { background: var(--c-yellow); }

/* Text link */
.link { position: relative; display: inline-block; font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.14em; }
.link::after { content: ""; position: absolute; left: 0; bottom: -3px; height: 2px; width: 100%; background: var(--spectrum); transform: scaleX(0); transform-origin: right; transition: transform 0.45s var(--ease); }
/* A permanent spectrum rule under a word, for emphasis in running text.
   A background image rather than ::after, because ::after draws one bar the
   width of the whole element: if the phrase wraps, the underline lands under
   one line and floats past the other. A background repeats per line box. */
.uline {
  background-image: var(--spectrum);
  background-repeat: no-repeat;
  background-size: 100% 2px;
  background-position: 0 100%;
  padding-bottom: 3px;
}
html[data-contrast="dark"] .uline { background-image: var(--spectrum-on-dark); }
.link:hover::after { transform: scaleX(1); transform-origin: left; }

/* ---- Custom cursor -------------------------------------------------------- */
.cursor, .cursor-dot { position: fixed; top: 0; left: 0; pointer-events: none; z-index: 9999; border-radius: 50%; }
.cursor { width: 46px; height: 46px; border: 1.5px solid var(--bone); transform: translate(-50%, -50%); opacity: 0; transition: width 0.35s var(--ease), height 0.35s var(--ease), opacity 0.35s var(--ease), background 0.35s var(--ease), border-color 0.35s var(--ease); mix-blend-mode: difference; }
.cursor-dot { width: 8px; height: 8px; background: var(--spectrum-conic); transform: translate(-50%, -50%); transition: opacity 0.25s var(--ease), transform 0.25s var(--ease); }
.cursor.is-hover { opacity: 1; width: 64px; height: 64px; background: var(--bone); border-color: transparent; }
.cursor-dot.is-hover { opacity: 0; }
.cursor.is-hidden, .cursor-dot.is-hidden { opacity: 0; }
@media (hover: none) { .cursor, .cursor-dot { display: none; } }

/* ---- Navigation ----------------------------------------------------------- */
.nav { position: fixed; top: 0; left: 0; right: 0; z-index: 200; display: flex; align-items: center; justify-content: space-between; padding: clamp(1.1rem, 2.2vw, 1.7rem) var(--gutter); color: var(--ink); transition: transform 0.5s var(--ease); }
.nav::before { content: ""; position: absolute; inset: 0; z-index: -1; background: rgba(238, 236, 233, 0.6); border-bottom: 1px solid var(--line-soft); opacity: 0; transition: opacity 0.4s var(--ease); -webkit-backdrop-filter: blur(14px) saturate(1.4); backdrop-filter: blur(14px) saturate(1.4); }
.nav.is-solid::before { opacity: 1; }
.nav.is-hidden { transform: translateY(-115%); }
.nav__logo { font-family: var(--font-display); font-size: 1.5rem; letter-spacing: -0.02em; display: flex; align-items: center; gap: 0.5em; }
.nav__logo .mark { width: 15px; height: 15px; border-radius: 50%; background: var(--spectrum-conic); box-shadow: 0 0 0 1px rgba(30,30,30,.08); }
.nav__logo-img { height: clamp(40px, 4.4vw, 60px); width: auto; display: block; }
.nav__links { display: flex; gap: clamp(1.2rem, 2.5vw, 2.6rem); align-items: center; }
.nav__links a { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.12em; position: relative; }
/* Anchored to the top, not the bottom: the links are two lines now, and a
   line pinned to the bottom of a hinted link was drawn straight through its
   hint text, while the active link (which never has a hint) kept its line
   under the label. 1.5em is the label's own line box, so every underline
   sits in the same place whether the link has a second line or not. */
.nav__links a::after { content: ""; position: absolute; left: 0; right: 0; top: calc(1.5em + 5px); height: 2px; background: var(--spectrum); transform: scaleX(0); transform-origin: right; transition: transform 0.4s var(--ease); }
.nav__links a:hover::after, .nav__links a.active::after { transform: scaleX(1); transform-origin: left; }
/* The same plain words the slide-out menu spells out inline. These used to be
   a tooltip that appeared on hover, which meant a mouse user saw them only if
   they happened to hover before deciding, and a touch user never saw them at
   all: there is no hover on a phone. Launch, Improve, Connect, Create and Care
   are invented words, so the bar was a guessing game for anyone who did not
   hover. They are a second line now, always there.
   ::before, because ::after is already the spectrum underline. */
.nav__links { align-items: flex-start; }
/* The gap is where the hover/active underline lives, so it has to clear the
   label's descenders and the hint's ascenders both. */
.nav__links a { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; }
.nav__links a[data-hint]::before {
  content: attr(data-hint);
  order: 2;                       /* under the label, whatever the source order */
  font-family: var(--font-mono); font-size: 0.64rem;   /* the size it was as a tooltip */
  letter-spacing: 0.02em; text-transform: lowercase; white-space: nowrap;
  color: var(--bone-dim); font-weight: 400;              /* 5.66:1 light, 8.42:1 dark */
}
/* Blog and Contact need no translation, so they have no second line. Topping
   the row out keeps every label on one baseline rather than floating each one
   to the middle of a block only some of them have. */
.nav__toggle { display: none; }

/* When the dark mobile menu is open, keep the nav controls visible on top of it */
body.menu-open .nav { transform: none !important; }
body.menu-open .nav__logo { opacity: 0; visibility: hidden; }
body.menu-open .nav__toggle { color: var(--bone); }

/* Mobile menu — dark, for contrast */
.menu { position: fixed; inset: 0; z-index: 190; background: var(--ink-2); color: var(--bone); display: flex; flex-direction: column; justify-content: center; padding: clamp(4.5rem, 10vh, 7rem) var(--gutter) var(--gutter); clip-path: inset(0 0 100% 0); transition: clip-path 0.7s var(--ease); overflow-y: auto; }
.menu.is-open { clip-path: inset(0 0 0 0); }
.menu a { font-family: var(--font-display); font-size: clamp(1.7rem, 6.5vw, 3.4rem); line-height: 1.08; letter-spacing: -0.03em; padding-block: 0.06em; display: flex; align-items: baseline; gap: 0.5em; transition: color 0.3s var(--ease); }
.menu a:hover { color: var(--c-yellow); }
.menu a .idx { font-family: var(--font-mono); font-size: 0.8rem; letter-spacing: 0; }
/* Launch, Improve, Connect, Create and Care are our words, not the visitor's.
   Four plain ones beside each, so nobody has to guess which door is theirs. */
.menu a .hint {
  font-family: var(--font-mono); font-size: clamp(0.62rem, 0.5rem + 0.4vw, 0.78rem);
  letter-spacing: 0.04em; text-transform: lowercase; color: var(--bone-dim);
  margin-left: 0.15em; white-space: nowrap;
}
@media (max-width: 420px){ .menu a .hint { display: none; } }

/* Optional-field marker and the decoder line under the service chips. */
.field__opt {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--bone-mute); border: 1px solid var(--line-soft);
  border-radius: 100px; padding: 0.1em 0.5em; margin-left: 0.4em; vertical-align: middle;
}
.field__hint { margin-top: 0.7rem; font-size: var(--step--1); color: var(--bone-mute); line-height: 1.5; max-width: 62ch; }
.menu a:nth-child(1) .idx { color: var(--c-red); }
.menu a:nth-child(2) .idx { color: var(--c-green); }
.menu a:nth-child(3) .idx { color: var(--c-blue); }
.menu a:nth-child(4) .idx { color: var(--c-magenta); }
.menu a:nth-child(5) .idx { color: var(--c-orange); }
.menu a:nth-child(6) .idx { color: var(--c-teal); }
.menu a:nth-child(7) .idx { color: var(--c-indigo); }
.menu a:nth-child(8) .idx { color: var(--c-red); }
.menu__foot { margin-top: auto; display: flex; gap: 2rem; flex-wrap: wrap; color: rgba(238,236,233,.6); font-family: var(--font-mono); font-size: var(--step--1); }

@media (max-width: 1150px) {
  .nav__links { display: none; }
  .nav__toggle { display: inline-flex; font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.12em; }
}

/* ---- Marquee -------------------------------------------------------------- */
.marquee { overflow: hidden; white-space: nowrap; border-block: 1px solid var(--line); padding-block: clamp(1rem, 2vw, 1.6rem); }
.marquee__track { display: inline-flex; gap: 3rem; animation: marquee 32s linear infinite; will-change: transform; }
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee span { font-family: var(--font-display); font-size: clamp(1.05rem, 2.3vw, 1.5rem); font-weight: 400; letter-spacing: -0.01em; color: var(--bone-dim); display: inline-flex; align-items: center; gap: 2.4rem; }
.marquee span::after { content: "✳"; font-size: 0.6em; }
.marquee span:nth-child(6n+1)::after { color: var(--c-red); }
.marquee span:nth-child(6n+2)::after { color: var(--c-orange); }
.marquee span:nth-child(6n+3)::after { color: var(--c-green); }
.marquee span:nth-child(6n+4)::after { color: var(--c-teal); }
.marquee span:nth-child(6n+5)::after { color: var(--c-blue); }
.marquee span:nth-child(6n)::after   { color: var(--c-magenta); }
@keyframes marquee { to { transform: translateX(-50%); } }

/* ---- Reveal --------------------------------------------------------------- */
[data-reveal] { opacity: 0; transform: translateY(28px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
[data-reveal].in { opacity: 1; transform: none; }
[data-reveal-delay="1"] { transition-delay: 0.08s; }
[data-reveal-delay="2"] { transition-delay: 0.16s; }
[data-reveal-delay="3"] { transition-delay: 0.24s; }
[data-reveal-delay="4"] { transition-delay: 0.32s; }

.reveal-lines .line { display: block; overflow: hidden; padding-bottom: 0.34em; margin-bottom: -0.34em; }
.reveal-lines .line > span { display: block; transform: translateY(105%); transition: transform 1s var(--ease); }
.reveal-lines.in .line > span { transform: none; }
.reveal-lines.in .line:nth-child(2) > span { transition-delay: 0.08s; }
.reveal-lines.in .line:nth-child(3) > span { transition-delay: 0.16s; }
.reveal-lines.in .line:nth-child(4) > span { transition-delay: 0.24s; }

/* ---- Hero ----------------------------------------------------------------- */
.hero { min-height: 100svh; display: flex; flex-direction: column; justify-content: flex-end; padding-top: clamp(9.5rem, 18vh, 13rem); padding-bottom: clamp(2rem, 5vw, 4rem); position: relative; overflow-x: clip; }
.hero__top { position: absolute; top: clamp(6rem, 14vh, 9rem); left: var(--gutter); right: var(--gutter); display: flex; justify-content: space-between; gap: 2rem; flex-wrap: wrap; }
.hero__title { font-size: var(--step-4); }
/* The headline used to step down a size on phones to keep each line on one
   line. Dropped: every other page's headline is var(--step-4) and nine of the
   thirteen already wrap at 390px, so a smaller hero was the odd one out. */
@media (max-width: 560px) {
  /* On phones the hero behaves like .pagehead does on every other page:
     content starts under the nav instead of being pinned to the bottom of a
     100svh box, and the eyebrows sit in the flow above the headline. Same
     padding-top as .pagehead's mobile value, so the rhythm matches. */
  .hero { min-height: 0; justify-content: flex-start; padding-top: clamp(7.5rem, 17vh, 9.5rem); }
  .hero__top { position: static; inset: auto; gap: 0.3rem 1.5rem; margin-bottom: 0.6rem; }
  .hide-sm { display: none; }
}
.hero__meta { display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 1.5rem; margin-top: clamp(1.5rem, 4vw, 3rem); border-top: 1px solid var(--line); padding-top: 1.5rem; }
.hero__meta p { max-width: 40ch; color: var(--bone-dim); }
.scroll-cue { display: inline-flex; flex-direction: column; align-items: center; gap: 0.5rem; font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.2em; color: var(--bone-dim); }
.scroll-cue .bar { width: 2px; height: 46px; background: var(--line); position: relative; overflow: hidden; border-radius: 2px; }
.scroll-cue .bar::after { content: ""; position: absolute; inset: 0; background: var(--spectrum); background-size: 100% 300%; transform: translateY(-100%); animation: cue 2.4s var(--ease) infinite; }
@keyframes cue { 0% { transform: translateY(-100%); } 45%,100% { transform: translateY(100%); } }
/* Scroll cue sits mid-right, clear of the hero CTAs; hidden where space is tight */
.hero__scroll { position: absolute; right: var(--gutter); top: 50%; transform: translateY(-50%); }
@media (max-width: 980px) { .hero__scroll { display: none; } }

/* Homepage finder link — gentle infinite motion to catch the eye.
   It was --step--1 mono uppercase sitting beside a paragraph of body copy,
   which is smaller than the text it is competing with, so it receded. Now a
   step larger and in Space Mono's 700 weight, with the tracking pulled in a
   little because bold at this size does not need 0.14em to stay readable. */
.finder-cta { font-size: var(--step-0); font-weight: 700; letter-spacing: 0.1em; }

/* A resting underline under the sweeping one. The sweep is absent for about a
   third of its cycle, so for that third the link had no underline at all and
   read as plain text. It matters more than that for anyone on reduced motion:
   the global animation-duration override freezes the sweep at its last
   keyframe, which is scaleX(0), so they never saw an underline. */
.finder-cta::before {
  content: ""; position: absolute; left: 0; bottom: -3px;
  height: 2px; width: 100%; background: var(--spectrum); opacity: 0.4;
}
.finder-cta:hover::before { opacity: 0; }
.finder-cta::after { animation: finderSweep 3.6s var(--ease) infinite; }
@keyframes finderSweep {
  0% { transform: scaleX(0); transform-origin: left; }
  18% { transform: scaleX(1); transform-origin: left; }
  50% { transform: scaleX(1); transform-origin: right; }
  70%, 100% { transform: scaleX(0); transform-origin: right; }
}
.finder-cta:hover::after { animation: none; transform: scaleX(1); transform-origin: left; }
.finder-cta .arr { display: inline-block; animation: finderNudge 1.6s var(--ease) infinite; }
@keyframes finderNudge { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(5px); } }
.finder-cta:hover .arr { animation-play-state: paused; }

/* ---- Media / covers ------------------------------------------------------- */
.media { position: relative; overflow: hidden; background: var(--ink); aspect-ratio: 16 / 9; border: 1px solid var(--line-soft); container-type: inline-size; }
.media__img { position: absolute; inset: 0; transform: scale(1.05); transition: transform 1.1s var(--ease), filter 1.1s var(--ease); background-size: cover; background-position: center; filter: saturate(0.92) contrast(1.02); }
.media:hover .media__img { transform: scale(1); filter: saturate(1.05) contrast(1); }
/* A real <img> rather than the .media__img background, because a background
   image cannot carry alt text, cannot be lazy-loaded natively and cannot
   negotiate AVIF. No hover zoom on these: the portraits are framed
   illustrations and scaling them would crop into the gold border. */
.media__photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Matches the artwork's own 8:5, so nothing is cropped. The illustration
   was square in its first version and the tile was square with it; when the
   art changed the tile had to follow, or a 1:1 crop would have taken about a
   third off each side and both sleeves with it. */
.media--wide { aspect-ratio: 8 / 5; }

/* The blog illustrations. Unlike a screenshot, which can be cropped from the
   bottom and still read, these are single compositions - two sleeves, a paw
   and a table of objects - and a crop takes a hand off the edge. So the tile
   carries the artwork's own 1586x992 and object-fit is contain, not cover:
   nothing is ever cut, and if a future drawing comes in at another ratio it
   letterboxes onto the bone ground rather than losing an edge. The ground is
   bone rather than the default ink because the art sits on cream - a dark
   tile flashed black for as long as the image took to arrive. */
.media--art { background: var(--bone-2); }
.media--art .media__photo { object-fit: contain; }

.media--link { display: flex; cursor: pointer; }

/* The tag pill sits on top of the screenshot, so its job is to label the card,
   not to compete with it. Tags used to be two words; now that they name the
   platform as well ("WordPress · WooCommerce · B2B wholesale") the old size
   drew a black bar across the image. Smaller type, tighter tracking and a
   width cap, so a long tag wraps to a second line instead of spanning the
   picture. */
.media__tag {
  position: absolute; left: 0.7rem; top: 0.7rem; z-index: 3;
  max-width: calc(100% - 1.4rem);
  font-family: var(--font-mono);
  font-size: clamp(0.62rem, 0.58rem + 0.14vw, 0.72rem);
  line-height: 1.25;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--ink); color: var(--bone);
  padding: 0.35em 0.7em;
  border-radius: 100px;
}
/* On the article cover the pill has more room and less to fight with. */
.article-cover .media__tag { font-size: clamp(0.68rem, 0.62rem + 0.18vw, 0.8rem); left: 1rem; top: 1rem; }

/* Typographic gradient covers — brand stand-ins until real photography */
.cover { display: flex; align-items: flex-end; }
/* The label sits bottom-left by default. Placed after .cover deliberately:
   both are single-class selectors, so whichever comes last wins. */
.cover--top { align-items: flex-start; }
.cover::after { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--spectrum); z-index: 2; }
.cover__label { position: relative; z-index: 2; font-family: var(--font-display); font-weight: 340; letter-spacing: -0.02em; line-height: 1.04; color: var(--bone); font-size: clamp(1.05rem, 9cqi, 2.4rem); padding: clamp(0.8rem, 5cqi, 1.8rem); max-width: 16ch; text-wrap: balance; transition: transform 1.1s var(--ease); }
.cover:hover .cover__label { transform: translateY(-3px); }
/* The label was sized for the old tall banner: at full width it set 4rem
   type, which needs about 124px before it wraps, and any two-line title
   would have spilled straight out of a 150px strip. */
.article-cover.cover .cover__label { font-size: clamp(1.4rem, 3.4cqi, 2rem); padding: clamp(0.8rem, 2.5cqi, 1.2rem); }
/* White chip, full vivid --spectrum as the text - the same treatment as `em`
   and `.grad-text` everywhere else on the site, and the same accepted WCAG
   1.4.3 exception noted on --spectrum-text above.

   White text on the eight-stop spectrum was tried and abandoned: white
   measures 1.94:1 on the yellow stop, and orange, green and teal are barely
   better. The only ground that carries white across all eight hues is a
   darkened spectrum, which is the dark slab this started from.

   Two boxes, and that is load-bearing. background-clip:text paints the
   gradient into the element's BACKGROUND layer, so a ground drawn behind the
   text covers the glyphs and the label disappears. Ground on the outer span,
   clipped gradient on the inner one. */
.cover__label--chip {
  background: #fff;
  margin: clamp(0.9rem, 4cqi, 1.6rem);
  padding: 0.42em 0.8em 0.5em;
  border-radius: 12px;
  font-size: clamp(1rem, 4.6cqi, 1.7rem);
  max-width: 18ch;
  width: max-content;
  box-shadow: 0 8px 26px rgba(30, 30, 30, 0.20), inset 0 0 0 1px rgba(30, 30, 30, 0.07);
}
.cover__label--chip > span {
  background-image: var(--spectrum);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
/* Without background-clip:text the glyphs would be invisible. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .cover__label--chip > span { -webkit-text-fill-color: var(--ink); color: var(--ink); }
}

.ph-1 { background: radial-gradient(125% 125% at 14% 16%, rgba(230, 51, 41, 0.50), #1e1e1e 58%); }
.ph-2 { background: radial-gradient(125% 125% at 14% 16%, rgba(47, 116, 208, 0.50), #1e1e1e 58%); }
.ph-3 { background: radial-gradient(125% 125% at 14% 16%, rgba(89, 183, 58, 0.46), #1e1e1e 58%); }
.ph-4 { background: radial-gradient(125% 125% at 14% 16%, rgba(106, 63, 208, 0.52), #1e1e1e 58%); }
.ph-5 { background: radial-gradient(125% 125% at 14% 16%, rgba(18, 176, 166, 0.46), #1e1e1e 58%); }
.ph-6 { background: radial-gradient(125% 125% at 14% 16%, rgba(214, 56, 155, 0.50), #1e1e1e 58%); }

/* ---- Work grid ------------------------------------------------------------ */
.work { display: grid; grid-template-columns: repeat(12, 1fr); gap: clamp(1.5rem, 3vw, 3rem) clamp(1.5rem, 2.5vw, 2.5rem); }
.work__item { grid-column: span 6; display: block; align-self: start; }
.work__meta { margin-top: 1.15rem; }
.work__meta h3 { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; letter-spacing: -0.02em; line-height: 1.06; }
.work__meta .cat { display: block; margin-top: 0.4rem; font-family: var(--font-mono); font-size: var(--step--1); color: var(--bone-dim); text-transform: uppercase; letter-spacing: 0.1em; }
.work__num { display: block; margin-bottom: 0.4rem; font-family: var(--font-mono); font-size: var(--step--1); letter-spacing: 0.08em; color: var(--accent); }
/* Real screenshots get a slow zoom on hover, like the gradient tiles */
.media__photo { transition: transform 1.1s var(--ease); }
.work__item:hover .media--shot .media__photo { transform: scale(1.04); }
/* Anchor screenshots to the top-left so the site's branding (logo, usually
   top-left) stays in frame no matter the tile's aspect ratio. Scoped to
   case-card shots so it never touches the centre-framed team portraits. */
.media--shot .media__photo { object-position: left top; }
/* The ink variants, not the vivid palette. These are 14px mono numerals on
   bone: the brand green came out at 2.16:1 against a 4.5:1 requirement, and
   every theme reproduced the failure because each one swapped the vivid
   token for an equally light one. The step numbers and link-row tags were
   moved to ink for exactly this reason; these two were missed. */
.work__item:nth-child(6n+1) .work__num { color: var(--c-red-ink); }
.work__item:nth-child(6n+2) .work__num { color: var(--c-orange-ink); }
.work__item:nth-child(6n+3) .work__num { color: var(--c-green-ink); }
.work__item:nth-child(6n+4) .work__num { color: var(--c-blue-ink); }
.work__item:nth-child(6n+5) .work__num { color: var(--c-indigo-ink); }
.work__item:nth-child(6n) .work__num   { color: var(--c-magenta-ink); }
@media (max-width: 720px) {
  .work__item, .work__item:nth-child(4n+1), .work__item:nth-child(4n+2), .work__item:nth-child(4n+3), .work__item:nth-child(4n) { grid-column: span 12; }
  /* Stack the title and category instead of sharing a row */
  .work__meta { flex-direction: column; align-items: flex-start; gap: 0.3rem; }
}

/* ---- Editorial "magazine" grid (Success Stories + Shopify work) -----------
   Same 12-col grid, but the tiles run in an asymmetric rhythm, wide 7 paired
   with narrow 5 and an even 6/6, so a long list reads like a spread instead
   of a uniform catalogue. Rows always sum to 12, so there are no gaps. Only
   the .work--mag grids opt in; the homepage rail keeps its own layout.

   No lead tile. There was a full-width first child, but work.php shuffles the
   order on every request, so it only ever promoted whichever story happened to
   land first. The pairs are phased to sum to 12 from item one: they used to
   start 6+7, which only worked because the lead absorbed slot one, and without
   it item 2 wrapped and left a six-column hole. Cycle is (7+5), (5+7), (6+6). */
.work--mag .work__item:nth-child(6n+1) { grid-column: span 7; }
.work--mag .work__item:nth-child(6n+2) { grid-column: span 5; }
.work--mag .work__item:nth-child(6n+3) { grid-column: span 5; }
.work--mag .work__item:nth-child(6n+4) { grid-column: span 7; }
.work--mag .work__item:nth-child(6n+5) { grid-column: span 6; }
.work--mag .work__item:nth-child(6n)   { grid-column: span 6; }
/* Narrower columns get a slightly taller crop so they don't read as stubs */
.work--mag .work__item:nth-child(6n+2) .media,
.work--mag .work__item:nth-child(6n+3) .media { aspect-ratio: 3 / 2; }
@media (max-width: 720px) {
  .work--mag .work__item,
  .work--mag .work__item:nth-child(6n+1),
  .work--mag .work__item:nth-child(6n+2),
  .work--mag .work__item:nth-child(6n+3),
  .work--mag .work__item:nth-child(6n+4),
  .work--mag .work__item:nth-child(6n+5),
  .work--mag .work__item:nth-child(6n) { grid-column: span 12; }
  .work--mag .work__item .media,
  .work--mag .work__item:nth-child(6n+2) .media,
  .work--mag .work__item:nth-child(6n+3) .media { aspect-ratio: 16 / 9; }
}

/* A pair, not a spread. The magazine rhythm above needs a long list to read
   as a rhythm: with exactly two tiles, 7 + 5 and two different media ratios
   just look like one tile failed to grow, with a hole under the short one.
   Equal halves instead, and the media set to 8:5, which is the artwork's own
   1200x750, so the two are the same height and neither is cropped. Bone, not
   ink, behind them: these are illustrations on cream and a dark tile flashed
   black for as long as the image took to arrive. */
#case-own .work__item,
#case-own .work__item:nth-child(6n+1),
#case-own .work__item:nth-child(6n+2) { grid-column: span 6; }
#case-own .work__item .media,
#case-own .work__item:nth-child(6n+2) .media { aspect-ratio: 8 / 5; background: var(--bone-2); }
@media (max-width: 720px) {
  #case-own .work__item,
  #case-own .work__item:nth-child(6n+1),
  #case-own .work__item:nth-child(6n+2) { grid-column: span 12; }
  #case-own .work__item .media,
  #case-own .work__item:nth-child(6n+2) .media { aspect-ratio: 8 / 5; }
}

/* ---- Success-stories rail ------------------------------------------------
   The same rail at every width. On a phone it is a thumb gesture and needs
   nothing else. On desktop it needs buttons, because a mouse has no sideways
   swipe and shift-scroll is a thing almost nobody discovers. The half-visible
   next card does the other half of the work: it says there is more without
   any furniture at all.

   The buttons are built by caseRail() in main.js rather than sitting in the
   HTML, so with no JavaScript the rail is still a plain scrollable strip, and
   they only appear when there is actually something to scroll to.
   --------------------------------------------------------------------- */
.rail { position: relative; }
#case-featured,
.work--rail {
  display: flex; align-items: flex-start; grid-template-columns: none;
  gap: clamp(1rem, 2vw, 1.75rem);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; padding-bottom: 0.75rem;
}
#case-featured::-webkit-scrollbar,
.work--rail::-webkit-scrollbar { display: none; }
#case-featured .work__item,
#case-featured .work__item:nth-child(4n+2),
#case-featured .work__item:nth-child(4n),
.work--rail .work__item,
.work--rail .work__item:nth-child(6n+1),
.work--rail .work__item:nth-child(6n+2),
.work--rail .work__item:nth-child(6n+3),
.work--rail .work__item:nth-child(6n+4),
.work--rail .work__item:nth-child(6n+5),
.work--rail .work__item:nth-child(6n) {
  flex: 0 0 40%; scroll-snap-align: start; align-self: flex-start; grid-column: auto;
}
/* A rail of three on a phone wants most of the screen, not 40% of it. */
@media (max-width: 720px) {
  #case-featured .work__item,
  .work--rail .work__item { flex-basis: 78%; }
}
.rail__btn { display: none; }
@media (min-width: 721px) {
  .rail--on .rail__btn {
    position: absolute; top: var(--rail-btn-y, 34%); transform: translateY(-50%);
    display: grid; place-items: center; width: 48px; height: 48px;
    /* Light, like .prefs-fab: these sit on top of artwork that is usually
       dark, so an ink button would disappear into it. */
    border: 1px solid var(--line); border-radius: 50%;
    background: var(--bone); color: var(--ink);
    font-family: var(--font-mono); font-size: 1.15rem; line-height: 1;
    box-shadow: 0 10px 30px rgba(30, 30, 30, 0.22); cursor: pointer; z-index: 3;
    transition: opacity 0.3s var(--ease), background 0.3s var(--ease), color 0.3s var(--ease);
  }
  .rail--on .rail__btn--prev { left: -16px; }
  .rail--on .rail__btn--next { right: -16px; }
  .rail--on .rail__btn:hover { background: var(--ink); color: var(--bone); border-color: var(--ink); }
  .rail--on .rail__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
  .rail--on .rail__btn:disabled { opacity: 0; pointer-events: none; }
}
/* Phone: wider cards, and the rail bleeds to the screen edges. Placed after
   the block above on purpose, a media query adds no specificity, so this has
   to come later in the file to win. */
@media (max-width: 720px) {
  #case-featured { margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter); scroll-padding-inline: var(--gutter); }
  #case-featured .work__item,
  #case-featured .work__item:nth-child(4n+2),
  #case-featured .work__item:nth-child(4n) { flex-basis: 80%; }
}

/* ---- Services list -------------------------------------------------------- */
.svc { border-top: 1px solid var(--line); }
.svc__row { display: grid; grid-template-columns: 5rem 1fr auto; gap: clamp(1rem, 4vw, 4rem); align-items: center; padding-block: clamp(1.8rem, 4vw, 3rem); border-bottom: 1px solid var(--line); position: relative; transition: padding 0.5s var(--ease), color 0.5s var(--ease); }
.svc__row::before { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--ink); transform: scaleY(0); transform-origin: bottom; transition: transform 0.5s var(--ease); }
.svc__row:hover { color: var(--bone); padding-inline: clamp(1rem, 3vw, 2.5rem); }
.svc__row:hover::before { transform: scaleY(1); transform-origin: top; }
.svc__num { font-family: var(--font-mono); font-size: var(--step--1); }
.svc__row:nth-child(1) .svc__num { color: var(--c-red); }
.svc__row:nth-child(2) .svc__num { color: var(--c-green); }
.svc__row:nth-child(3) .svc__num { color: var(--c-blue); }
.svc__row:nth-child(4) .svc__num { color: var(--c-magenta); }
.svc__row:nth-child(5) .svc__num { color: var(--c-indigo); }
.svc__name { font-family: var(--font-display); font-size: var(--step-2); font-weight: 340; letter-spacing: -0.02em; }
.svc__desc { max-width: 42ch; color: var(--bone-dim); transition: color 0.5s var(--ease); }
.svc__row:hover .svc__desc { color: rgba(238,236,233,.75); }
.svc__arrow { font-size: 1.6rem; transition: transform 0.5s var(--ease); }
.svc__row:hover .svc__arrow { transform: translateX(0.4em) rotate(-45deg); }
@media (max-width: 720px) { .svc__row { grid-template-columns: 1fr; gap: 0.6rem; } .svc__arrow { display: none; } }

/* ---- Statement / band ----------------------------------------------------- */
.statement .display { max-width: 20ch; }
.band { background: var(--ink); color: var(--bone); }
.band .lead { color: var(--bone); }
.band .eyebrow { color: color-mix(in srgb, var(--bone) 70%, transparent); }
.band .btn { border-color: color-mix(in srgb, var(--bone) 38%, transparent); color: var(--bone); }
.band .btn::before { background: var(--bone); }
.band .btn:hover { color: var(--ink); border-color: var(--bone); }

/* ---- Components inside a dark band ---------------------------------------
   .band inverts a section onto the ink surface. Several components were
   styled only for the light surface and need the band's palette here.
   color-mix keeps them tied to the tokens, so a band still inverts correctly
   under the Dark display preference, where --ink and --bone swap roles.
   -------------------------------------------------------------------------- */

/* background-IMAGE, never the `background` shorthand: the shorthand resets
   background-clip and would paint the gradient as a solid block. */
.band :is(em, .grad-text) { background-image: var(--spectrum-on-dark); }

/* The price card takes the band's palette instead of punching a light card
   out of the middle of the section. */
.band .price {
  background: transparent;
  border-color: color-mix(in srgb, var(--bone) 34%, transparent);
}
.band .price .amt,
.band .price li { color: var(--bone); }                     /* 14.14:1 on ink */
.band .price .kicker,
.band .price .muted,
.band .price .amt small { color: color-mix(in srgb, var(--bone) 72%, transparent); }
.band .price li::before { color: var(--c-green); }          /* 6.56:1 on ink */

/* Schedule rows. --accent is a deep teal tuned for the light surface and
   reaches only 3.09:1 here, so the emphasised final date uses the brand teal. */
.band .guide div { border-bottom-color: color-mix(in srgb, var(--bone) 20%, transparent); }
.band .guide .p { color: var(--bone); }
.band .guide .p b { color: var(--c-teal); }                 /* 6.18:1 on ink */
/* A band is always the inverse of the page, so in dark mode it is the LIGHT
   surface, and the brand teal above lands on it at 2.22:1. A darker teal
   there. Not --c-teal-ink: that token flips with the theme, so in dark
   mode it IS the brand teal. And not its light-mode value either, because
   the band ground in dark mode is --ink (#ece9e4), a shade off the page's
   own bone (#eeece9), which drops #0c7771 to 4.45:1 and just misses.
   #0b6f69 is 4.97:1 there. */
html[data-contrast="dark"] .band .guide .p b { color: #0b6f69; }

/* .btn--solid is an ink-filled button, which vanishes on an ink band. Invert
   it: solid bone, outline on hover. The rainbow hover slide is dropped here
   because ink text over the indigo stop is only about 2.4:1. */
.band .btn--solid { background: var(--bone); border-color: var(--bone); color: var(--ink); }
.band .btn--solid::before { display: none; }
.band .btn--solid:hover { background: transparent; color: var(--bone); border-color: var(--bone); }
.band .btn--solid .dot { background: var(--spectrum-conic); }

/* ---- Stats ---------------------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2rem; }
.stat .num { font-family: var(--font-display); font-size: var(--step-3); font-weight: 340; letter-spacing: -0.03em; line-height: 1; }
.stat:nth-child(1) .num { color: var(--c-red-ink); }
.stat:nth-child(2) .num { color: var(--c-green-ink); }
.stat:nth-child(3) .num { color: var(--c-blue-ink); }
.stat:nth-child(4) .num { color: var(--c-magenta-ink); }
.stat .num .accent-text { color: inherit; }
.stat .lab { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.1em; color: var(--bone-dim); margin-top: 0.8rem; display: block; }
@media (max-width: 720px) { .stats { grid-template-columns: repeat(2, 1fr); gap: 2.5rem 1.5rem; } }

/* ---- Two-column ----------------------------------------------------------- */
.two-col { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: clamp(2rem, 6vw, 6rem); align-items: center; }
.two-col--rev { grid-template-columns: 0.9fr 1.1fr; }
@media (max-width: 860px) { .two-col, .two-col--rev { grid-template-columns: 1fr; } }

/* ---- Process steps -------------------------------------------------------- */
.steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--line); border: 1px solid var(--line); }
.step { background: var(--bone); padding: clamp(1.5rem, 3vw, 2.5rem); min-height: 15rem; display: flex; flex-direction: column; transition: background 0.5s var(--ease); }
.step:hover { background: var(--bone-2); }
.step .n { font-family: var(--font-mono); font-size: var(--step--1); }
/* The step numbers used the vivid brand hues straight on bone, which is what
   Lighthouse fails: orange measured 2.33:1 and teal 2.29:1, well under the
   4.5:1 floor for text this size. Same four hues, the text-safe versions. */
.step:nth-child(1) .n { color: var(--c-red-ink); }
.step:nth-child(2) .n { color: var(--c-orange-ink); }
.step:nth-child(3) .n { color: var(--c-teal-ink); }
.step:nth-child(4) .n { color: var(--c-indigo-ink); }
.step :is(h3, h4) { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; margin-top: auto; letter-spacing: -0.02em; }
.step p { color: var(--bone-dim); margin-top: 0.7rem; font-size: var(--step--1); }
@media (max-width: 860px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .steps { grid-template-columns: 1fr; } }

/* ---- Team ----------------------------------------------------------------- */
.team { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 3vw, 2.5rem); }
.team .media { aspect-ratio: 3 / 4; background: var(--bone-2); }
.team :is(h3, h4) { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; margin-top: 1rem; letter-spacing: -0.02em; }
.team .role { font-family: var(--font-mono); font-size: var(--step--1); color: var(--bone-dim); text-transform: uppercase; letter-spacing: 0.1em; }
.team .role { display: block; min-height: 2.9em; }   /* two lines reserved, so a long role never drops one bio below the others */
.team-bio { margin-top: 0.5rem; color: var(--bone-dim); font-size: var(--step--1); line-height: 1.55; }
@media (max-width: 900px) { .team { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .team { grid-template-columns: 1fr; } }

/* ---- Forms ---------------------------------------------------------------- */
.form { display: grid; gap: 2.2rem; max-width: 640px; }
.field { position: relative; }
.field label, .field .field__label { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.12em; color: var(--bone-dim); display: block; margin-bottom: 0.7rem; }
.field input, .field textarea, .field select { width: 100%; background: transparent; border: none; border-bottom: 1px solid var(--line); color: var(--ink); font-family: var(--font-ui); font-size: var(--step-1); padding: 0.6rem 0; transition: border-color 0.4s var(--ease); cursor: none; }
.field textarea { resize: vertical; min-height: 6rem; }
.field input:focus, .field textarea:focus, .field select:focus { border-image: var(--spectrum) 1; }
.field input::placeholder, .field textarea::placeholder { color: var(--bone-mute); }
.field select option { background: var(--bone); color: var(--ink); }
.chips { display: flex; flex-wrap: wrap; gap: 0.7rem; }
.chip { border: 1px solid var(--line); border-radius: 100px; padding: 0.6em 1.1em; font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.08em; transition: all 0.35s var(--ease); }
.chip:hover, .chip.active { background: var(--ink); color: var(--bone); border-color: var(--ink); }

/* ---- Footer --------------------------------------------------------------- */
.footer { padding-top: clamp(4rem, 8vw, 8rem); padding-bottom: 2.5rem; border-top: 1px solid var(--line); }
.footer__cta { font-family: var(--font-display); font-size: var(--step-4); font-weight: 340; letter-spacing: -0.03em; line-height: 0.92; }
.footer__cta a { display: inline-flex; align-items: center; gap: 0.2em; }
.footer__cta a:hover em { color: var(--accent); }
.footer__cta .line { padding-bottom: 0.5em; margin-bottom: -0.5em; }
/* Five columns rather than four. The old Services column ran to nine links
   against four in every other one, because it was two different things in one
   list: the five service stages, and the platform pages people arrive on from
   search. Splitting them along the seam that was already there evens the
   footer up and makes both halves scannable. */
.footer__grid { display: grid; grid-template-columns: 1.7fr 0.85fr 1.15fr 0.95fr 1.1fr; gap: 2rem; margin-top: clamp(3rem, 7vw, 6rem); padding-top: 2.5rem; border-top: 1px solid var(--line); }
.footer__grid :is(h2, h5) { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.12em; color: var(--bone-dim); margin-bottom: 1.2rem; }
.footer__grid a { display: block; padding-block: 0.3rem; }
.footer__grid a:hover { color: var(--accent); }
.footer__bar { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; margin-top: clamp(3rem, 6vw, 5rem); font-family: var(--font-mono); font-size: var(--step--1); color: var(--bone-mute); text-transform: uppercase; letter-spacing: 0.1em; }
/* Five columns need a real screen. Below that the blurb and the tool take a
   full row of their own and the four link columns share what is left. */
@media (max-width: 1200px) {
  .footer__grid { grid-template-columns: repeat(4, 1fr); }
  .footer__grid > :first-child { grid-column: 1 / -1; }
}
@media (max-width: 720px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
}
/* Below this a half column is narrower than hello@digitallysound.ie, and the
   address broke mid-word. One column reads better than a broken address. */
@media (max-width: 480px) {
  .footer__grid { grid-template-columns: 1fr; gap: 2.2rem; }
}
/* A grid column will not shrink below its longest unbreakable word, and
   hello@digitallysound.ie is longer than half a 320px screen, so the whole
   footer pushed the page 14px wide on the smallest phones. Predates the
   five-column change, fixed while we are in here. */
.footer__grid > * { min-width: 0; }
.footer__grid a { overflow-wrap: anywhere; }


/* ---- Free-check nudge ------------------------------------------------------
   A details element rather than a scripted panel, so it opens and closes on
   the keyboard for free and works with JavaScript off. column-reverse puts the
   panel above the tab, which is what a bottom-corner widget needs and what the
   source order cannot give you. Shares the corner and the z-index with
   .sticky-cta, and no page carries both.
   -------------------------------------------------------------------------- */
.nudge {
  position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem);
  z-index: 180; width: min(330px, calc(100vw - 2rem));
  display: flex; flex-direction: column-reverse; align-items: flex-end; gap: 0.6rem;
}
.nudge[hidden] { display: none; }
/* On a phone the notice and the nudge both want the bottom of the screen, and
   the notice has first claim: it is a question the visitor has to answer. One
   ask at a time. The accessibility panel already holds itself back the same
   way. Browsers without :has() get the old overlap for the few seconds the
   notice is up on a first visit, which is where this started. */
body:has(.cookie) .nudge { display: none; }
/* Steps aside for the footer, like .sticky-cta. Hidden rather than faded, so
   it leaves the tab order too and nobody lands on an invisible control. */
.nudge.is-hidden { opacity: 0; visibility: hidden; transform: translateY(0.75rem); pointer-events: none; }
.nudge { transition: opacity 0.35s var(--ease), transform 0.35s var(--ease), visibility 0.35s; }

.nudge__tab {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 0.55em;
  background: var(--ink); color: var(--bone); border-radius: 100px;
  padding: 0.7em 1.1em; font-family: var(--font-mono); font-size: var(--step--1);
  box-shadow: 0 10px 40px rgba(30, 30, 30, 0.18);
  transition: transform 0.3s var(--ease), background 0.3s var(--ease);
}
.nudge__tab::-webkit-details-marker { display: none; }
/* The dot is sized by .btn .dot, and this is a summary rather than a button,
   so it needs its own. flex: none keeps it round when the label is long. */
.nudge__tab .dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--c-yellow); }
.nudge__tab::marker { content: ""; }
.nudge__tab:hover { transform: translateY(-2px); }

/* Tucked away: the same control, quieter. Flagged on <html> rather than on
   the element, so the inline pre-paint script can set it before anything is
   drawn. Setting it from main.js alone painted the loud tab for about 80ms on
   a slow connection before it shrank.

   Tucked away: the same control, quieter. Outline instead of solid, smaller
   text, a two-word label. Loud enough to find on purpose, quiet enough that
   saying "not now" meant something. */
.nudge__min { display: none; }
html[data-nudge="min"] .nudge__full { display: none; }
html[data-nudge="min"] .nudge__min { display: inline; }
html[data-nudge="min"] .nudge__tab {
  background: var(--bone); color: var(--bone-dim);
  border: 1px solid var(--line); padding: 0.45em 0.9em;
  font-size: var(--step--2); box-shadow: none;
}
html[data-nudge="min"] .nudge__tab:hover { color: var(--ink); border-color: var(--accent); }
html[data-nudge="min"] .nudge__tab .dot { background: var(--bone-dim); }
html[data-nudge="min"] .nudge__tab:hover .dot { background: var(--c-yellow); }
.nudge__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* No background change when it opens. --ink-2 is a fixed dark surface that
   does not flip with the theme, which is the trap .menu already carries an
   override for a few hundred lines down: in dark mode --bone is dark too, so
   this was dark text on a dark tab. The panel appearing is feedback enough. */
.nudge[open] .nudge__tab { box-shadow: 0 6px 20px rgba(30, 30, 30, 0.22); }

.nudge__panel {
  background: var(--bone); border: 1px solid var(--line); border-radius: 16px;
  padding: 1.15rem 1.25rem; box-shadow: 0 18px 55px rgba(30, 30, 30, 0.20);
  width: 100%; animation: nudgein 0.3s var(--ease) both;
}
@keyframes nudgein { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.nudge__kicker {
  font-family: var(--font-mono); font-size: var(--step--2); text-transform: uppercase;
  letter-spacing: 0.12em; color: var(--bone-dim); margin-bottom: 0.4rem;
}
.nudge__title { font-family: var(--font-display); font-style: italic; font-size: var(--step-1); line-height: 1.15; }
.nudge__body { font-size: var(--step--1); line-height: 1.55; color: var(--bone-dim); margin: 0.6rem 0 1rem; }
.nudge__panel .btn { width: 100%; justify-content: center; }
.nudge__close {
  display: block; width: 100%; margin-top: 0.6rem; padding: 0.4rem;
  border: none; background: none; cursor: pointer;
  font-family: var(--font-mono); font-size: var(--step--2); color: var(--bone-dim);
  text-decoration: underline; text-underline-offset: 3px;
}
.nudge__close:hover { color: var(--ink); }
.nudge__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

html[data-motion="reduce"] .nudge__tab,
html[data-motion="reduce"] .nudge__tab:hover { transform: none; transition: none; }
html[data-motion="reduce"] .nudge__panel { animation: none; }

/* On a phone the tab spans the width it needs and the panel sits above it. */
/* Full width on a phone, and lifted clear of the display-preferences button,
   which lives in the bottom-left corner and is 38px tall. Side by side there
   is not enough room for both, so they stack. */
@media (max-width: 480px) {
  .nudge { left: 1rem; right: 1rem; width: auto; bottom: 4.2rem; }
  .nudge__tab { font-size: var(--step--2); }
}

/* ---- Page header ---------------------------------------------------------- */
.pagehead { padding-top: clamp(9rem, 20vh, 14rem); padding-bottom: clamp(2rem, 5vw, 4rem); }
.pagehead h1 { font-size: var(--step-4); }
.pagehead .meta { display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 1.5rem; margin-top: clamp(1.5rem, 4vw, 3rem); border-top: 1px solid var(--line); padding-top: 1.5rem; }
/* Tighter top spacing on phones so page headings aren't pushed so far down */
@media (max-width: 640px) {
  .pagehead { padding-top: clamp(6rem, 13vh, 8rem); }
  .article-head { padding-top: clamp(6rem, 13vh, 8rem); }
}

/* ---- Preloader ------------------------------------------------------------ */
.preloader { position: fixed; inset: 0; z-index: 10000; background: var(--bone); display: flex; align-items: center; justify-content: center; padding: var(--gutter); transition: clip-path 1s var(--ease); }
.preloader.done { clip-path: inset(0 0 100% 0); }
/* The equaliser is already in the stylesheet for the band sections. Borrowing
   it here means the name finally has a picture, at no extra weight. */
.preloader__eq .eq { height: clamp(56px, 11vh, 104px); gap: clamp(4px, 0.7vw, 7px); }
.preloader__eq .eq span { width: clamp(3px, 0.5vw, 5px); }
.preloader__foot { position: absolute; left: var(--gutter); right: var(--gutter); bottom: var(--gutter); display: flex; align-items: flex-end; justify-content: space-between; gap: clamp(1rem, 4vw, 3rem); }
.preloader__say { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
.preloader__term { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.2em; color: var(--bone-dim); }
.preloader__plain { font-family: var(--font-display); font-style: italic; font-size: var(--step-1); line-height: 1.15; letter-spacing: -0.01em; max-width: 30ch; text-wrap: balance; }
/* Re-run on every stage change, so each translation arrives rather than blinks */
.preloader__say.is-new .preloader__term,
.preloader__say.is-new .preloader__plain { animation: plsay 0.42s var(--ease) both; }
.preloader__say.is-new .preloader__plain { animation-delay: 0.07s; }
@keyframes plsay { from { opacity: 0; transform: translateY(0.45em); } to { opacity: 1; transform: none; } }
.preloader .count { font-family: var(--font-display); font-size: var(--step-4); line-height: 0.8; font-weight: 340; font-variant-numeric: tabular-nums; }
/* The spectrum strip was a static 4px line. Now it is the progress. */
.preloader__bar { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; overflow: hidden; }
.preloader__bar i { display: block; height: 100%; background: var(--spectrum); transform: scaleX(0); transform-origin: left; will-change: transform; }
@media (max-width: 560px) {
  .preloader__foot { flex-direction: column; align-items: flex-start; gap: 0.9rem; }
  .preloader__plain { font-size: var(--step-0); }
}

/* ---- Utilities ------------------------------------------------------------ */
.stack > * + * { margin-top: 1.5rem; }
.stack-lg > * + * { margin-top: clamp(2rem, 5vw, 4rem); }
.flex-between { display: flex; justify-content: space-between; align-items: flex-end; gap: 2rem; flex-wrap: wrap; }
.mt-s { margin-top: 1rem; } .mt-m { margin-top: 2rem; } .mt-l { margin-top: clamp(2.5rem,6vw,5rem); }
.text-right { text-align: right; }
.badge { display: inline-block; font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.12em; border: 1px solid var(--line); border-radius: 100px; padding: 0.4em 1em; color: var(--bone-dim); }

/* Cookie notice */
.cookie { position: fixed; left: 1.2rem; bottom: 1.2rem; z-index: 9990; width: min(430px, calc(100vw - 2rem)); display: flex; flex-direction: column; gap: 0.9rem; background: var(--bone); border: 1px solid var(--line); border-radius: 16px; padding: 1.15rem 1.25rem; box-shadow: 0 18px 55px rgba(30, 30, 30, 0.20); transform: translateY(180%); transition: transform 0.55s var(--ease); }
.cookie.show { transform: translateY(0); }
.cookie__text { font-size: var(--step--1); line-height: 1.55; color: var(--bone-dim); padding-right: 1.6rem; }
.cookie__text a { text-decoration: underline; color: var(--ink); }
.cookie__btns { display: flex; gap: 0.55rem; justify-content: flex-end; }
.cookie .btn { padding: 0.55em 0.95em; }
.cookie__close { position: absolute; top: 8px; right: 10px; width: 28px; height: 28px; display: grid; place-items: center; border: none; background: none; color: var(--bone-dim); font-size: 1.5rem; line-height: 1; border-radius: 50%; transition: color 0.25s var(--ease), background 0.25s var(--ease); }
.cookie__close:hover { color: var(--ink); background: var(--bone-2); }
@media (max-width: 460px) { .cookie__btns .btn { flex: 1; justify-content: center; } }
.footer__privacy { text-decoration: underline; }
/* Quiet by design: it is a fact, not a boast. Tabular figures so the
   number does not jiggle if it updates when LCP settles. */
.footer__perf { font-variant-numeric: tabular-nums; cursor: help; }

/* ==========================================================================
   SERVICE-PAGE COMPONENTS
   ========================================================================== */

/* Service sub-nav (Launch/Improve/Connect/Create pills) */
.subnav { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.subnav a { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.1em; padding: 0.6em 1.15em; border: 1px solid var(--line); border-radius: 100px; transition: background 0.35s var(--ease), color 0.35s var(--ease), border-color 0.35s var(--ease); }
.subnav a:hover { border-color: var(--ink); }
.subnav a.active { background: var(--ink); color: var(--bone); border-color: var(--ink); }

/* Qualifier cards — "Is this for you?" */
.cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.25rem; }
.card { position: relative; overflow: hidden; border: 1px solid var(--line); border-radius: 16px; padding: clamp(1.4rem, 2.5vw, 2rem); background: var(--bone); display: flex; flex-direction: column; min-height: 15rem; transition: transform 0.5s var(--ease), background 0.5s var(--ease); }
.card::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--spectrum); transform: scaleX(0); transform-origin: left; transition: transform 0.5s var(--ease); }
.card:hover { transform: translateY(-6px); background: var(--bone-2); }
.card:hover::before { transform: scaleX(1); }
.card .k { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.1em; color: var(--bone-dim); }
.card :is(h3, h4) { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; letter-spacing: -0.02em; margin-top: 0.5rem; }
.card ul { margin-top: auto; padding-top: 1.2rem; }
.card li { font-size: var(--step--1); color: var(--bone-dim); padding-block: 0.18rem; }
.card p { margin-top: 0.6rem; font-size: var(--step--1); line-height: 1.55; color: var(--bone-dim); }
@media (max-width: 900px) { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .cards { grid-template-columns: 1fr; } }

/* The bottom-alignment on .step h4 and .card ul is a FOUR-column device: at
   that width the panels are narrow, text wraps, and content nearly fills
   min-height, so aligning the headings tidies the row. Below four columns the
   panels get wide and the content short, and the same auto margin opens a
   void instead - measured at 70-99px on .step and 29-38px on .card in the
   two-column range, which is where it looks most broken.

   So each override is tied to its own grid leaving four columns: .steps at
   860px, .cards at 900px. Placed after both .step and .card are defined,
   since a media query adds no specificity and source order decides.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .step { min-height: 0; }
  .step :is(h3, h4) { margin-top: 0.6rem; }
}
@media (max-width: 900px) {
  .card { min-height: 0; }
  .card ul { margin-top: 0; padding-top: 0.9rem; }
}

/* Tag / pill list — "What we launch / connect" */
.taglist { display: flex; flex-wrap: wrap; gap: 0.8rem; }
.tag { position: relative; border: 1px solid var(--line); border-radius: 100px; padding: 0.7em 1.25em; font-family: var(--font-ui); font-size: var(--step-0); cursor: pointer; user-select: none; transition: background 0.35s var(--ease), color 0.35s var(--ease), border-color 0.35s var(--ease); }
.tag::after { content: "+"; margin-left: 0.55em; font-family: var(--font-mono); color: var(--accent); opacity: 0.7; transition: transform 0.35s var(--ease), color 0.35s var(--ease); display: inline-block; }
.tag:hover, .tag.is-active { background: var(--ink); color: var(--bone); border-color: var(--ink); }
.tag:hover::after, .tag.is-active::after { color: var(--c-yellow); }
.tag.is-active::after { transform: rotate(45deg); }

/* Tag info popover */
.tagpop { position: fixed; z-index: 9997; width: min(300px, calc(100vw - 2rem)); background: var(--bone); border: 1px solid var(--line); border-radius: 14px; padding: 1.1rem 1.2rem; box-shadow: 0 18px 55px rgba(30, 30, 30, 0.20); opacity: 0; transform: translateY(6px) scale(0.98); transition: opacity 0.25s var(--ease), transform 0.25s var(--ease); pointer-events: none; overflow: hidden; }
.tagpop.show { opacity: 1; transform: none; pointer-events: auto; }
.tagpop::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--spectrum); }
.tagpop h5 { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; letter-spacing: -0.01em; margin-bottom: 0.4rem; }
.tagpop p { font-size: var(--step--1); color: var(--bone-dim); line-height: 1.55; }

/* Vertical flow — automation chains */
.flow { display: grid; gap: 0; max-width: 560px; }
.flow li { position: relative; padding: 0.9rem 0 0.9rem 2.2rem; border-left: 2px solid var(--line); }
.flow li:last-child { border-left-color: transparent; }
.flow li::before { content: ""; position: absolute; left: -8px; top: 1.25rem; width: 13px; height: 13px; border-radius: 50%; background: var(--bone); border: 2px solid var(--accent); }
.flow li:nth-child(6n+1)::before { border-color: var(--c-red); }
.flow li:nth-child(6n+2)::before { border-color: var(--c-orange); }
.flow li:nth-child(6n+3)::before { border-color: var(--c-green); }
.flow li:nth-child(6n+4)::before { border-color: var(--c-teal); }
.flow li:nth-child(6n+5)::before { border-color: var(--c-blue); }
.flow li:nth-child(6n)::before   { border-color: var(--c-magenta); }
.flow li strong { font-family: var(--font-display); font-weight: 360; font-size: var(--step-1); letter-spacing: -0.01em; }
.flow li span { display: block; color: var(--bone-dim); font-size: var(--step--1); margin-top: 0.15rem; }

/* Productised price card */
.price { border: 1px solid var(--ink); border-radius: 20px; padding: clamp(1.8rem, 4vw, 3rem); background: var(--bone); }
.price .kicker { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.14em; color: var(--bone-dim); }
.price .amt { font-family: var(--font-display); font-size: var(--step-3); font-weight: 340; letter-spacing: -0.03em; line-height: 1; margin: 0.4rem 0 1.4rem; }
.price .amt small { font-size: 0.3em; color: var(--bone-dim); letter-spacing: 0; }
.price ul { columns: 2; column-gap: 2rem; margin-bottom: 1.8rem; }
.price li { break-inside: avoid; padding-block: 0.35rem; font-size: var(--step-0); }
.price li::before { content: "✓"; color: var(--c-green); font-family: var(--font-mono); margin-right: 0.6em; }
@media (max-width: 520px) { .price ul { columns: 1; } }

/* Care-plan tiers */
.plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; }
.plan { border: 1px solid var(--line); border-radius: 16px; padding: clamp(1.6rem, 3vw, 2.2rem); display: flex; flex-direction: column; transition: transform 0.5s var(--ease); }
.plan:hover { transform: translateY(-6px); }
.plan.featured { border-color: var(--ink); position: relative; }
.plan.featured::after { content: "Most popular"; position: absolute; top: -0.75rem; left: clamp(1.6rem,3vw,2.2rem); background: var(--ink); color: var(--bone); font-family: var(--font-mono); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; padding: 0.35em 0.8em; border-radius: 100px; }
.plan .tier { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.14em; }
/* The -ink variants, not the raw brand colours: brand orange is 2.33:1 and
   brand magenta 3.64:1 on bone, both of which fail at this size. The -ink
   tokens are the same hues tuned to pass, and they flip with the theme. */
.plan:nth-child(1) .tier { color: var(--c-orange-ink); }
.plan:nth-child(2) .tier { color: var(--c-teal-ink); }
.plan:nth-child(3) .tier { color: var(--c-magenta-ink); }
.plan .amt { font-family: var(--font-display); font-size: var(--step-2); font-weight: 340; letter-spacing: -0.02em; margin: 0.5rem 0 1.2rem; }
.plan .amt small { font-size: 0.35em; color: var(--bone-dim); }
.plan ul { margin-bottom: 1.6rem; }
.plan li { padding-block: 0.3rem; font-size: var(--step--1); color: var(--bone-dim); border-bottom: 1px solid var(--line-soft); }
.plan .btn { margin-top: auto; }
@media (max-width: 780px) { .plans { grid-template-columns: 1fr; } }

/* FAQ accordion */
.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); padding: 1.4rem 0; }
.faq summary { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; letter-spacing: -0.01em; cursor: none; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 1.5rem; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--accent); font-family: var(--font-mono); font-size: 1.4rem; transition: transform 0.35s var(--ease); }
.faq details[open] summary::after { content: "–"; }
.faq p { color: var(--bone-dim); margin-top: 1rem; max-width: 74ch; }
.faq-block { margin-top: clamp(2.5rem, 6vw, 4rem); }
.faq-block > h2 { font-family: var(--font-display); font-size: var(--step-2); font-weight: 340; letter-spacing: -0.02em; margin-bottom: 1.2rem; }

/* "What makes us different" strip */
.diff { display: grid; grid-template-columns: repeat(5, 1fr); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; }
.diff div { background: var(--bone); padding: clamp(1.4rem, 2.5vw, 2rem); }
.diff .n { font-family: var(--font-mono); font-size: var(--step--1); }
.diff div:nth-child(1) .n { color: var(--c-red); }
.diff div:nth-child(2) .n { color: var(--c-orange); }
.diff div:nth-child(3) .n { color: var(--c-green); }
.diff div:nth-child(4) .n { color: var(--c-blue); }
.diff div:nth-child(5) .n { color: var(--c-magenta); }
.diff p { margin-top: 0.8rem; font-size: var(--step--1); }
@media (max-width: 900px) { .diff { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .diff { grid-template-columns: 1fr; } }

/* Sticky corner CTA */
.sticky-cta { position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem); z-index: 180; box-shadow: 0 10px 40px rgba(30,30,30,.18); transition: opacity 0.35s var(--ease), transform 0.35s var(--ease), visibility 0.35s; }
/* Hidden, not just transparent: visibility takes it out of the tab order
   too, so nobody lands on an invisible button (see stickyCta in main.js). */
.sticky-cta.is-hidden { opacity: 0; visibility: hidden; transform: translateY(0.75rem); pointer-events: none; }

/* What we actually built, per client. Not .flow: that draws a connecting
   line down the list, which says these things happened in order. They did
   not, they are fourteen separate jobs for fourteen separate clients. Same
   typography, no sequence implied. */
.built { display: grid; gap: 0; }
.built li { padding: 1.05rem 0; border-bottom: 1px solid var(--line); }
.built li strong { display: block; font-family: var(--font-display); font-weight: 360; font-size: var(--step-0); letter-spacing: -0.01em; }
.built li span { display: block; margin-top: 0.2rem; color: var(--bone-dim); font-size: var(--step--1); line-height: 1.5; }
@media (min-width: 760px) {
  .built { grid-template-columns: 1fr 1fr; column-gap: clamp(2rem, 4vw, 3.5rem); }
}

/* Price-guide rows */
.guide { display: grid; gap: 0; }
.guide div { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; padding: 1.1rem 0; border-bottom: 1px solid var(--line); }
.guide .p { font-family: var(--font-mono); }
.guide .p b { color: var(--accent); font-weight: 400; }

/* ==========================================================================
   BLOG + ARTICLES
   ========================================================================== */
.post-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 3vw, 2.5rem); }
.post-card { display: block; }
/* 3/1, not 3/2. The cover is a typographic gradient panel, not a photograph,
   so height buys nothing - it just pushes the headline down the card. A
   letterbox keeps the category legible and puts three posts' worth of title
   and excerpt on screen at once. */
.post-card .media { aspect-ratio: 3 / 1; }
/* ...but a card with real artwork shows all of it. The 3/1 above is right for
   a gradient panel, where height only pushes the headline down; it is wrong
   for a drawing, where it would crop about two thirds of the composition. */
.post-card .media--art { aspect-ratio: 1586 / 992; }
.post-card__meta { display: flex; justify-content: space-between; gap: 1rem; margin-top: 1rem; font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.08em; color: var(--bone-dim); }
.post-card h3 { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; letter-spacing: -0.02em; margin-top: 0.7rem; }
.post-card p { margin-top: 0.6rem; font-size: var(--step-0); }
.post-card:hover h3 { color: var(--accent); }
/* The related-reading grids carry two or three posts, not ten, so the fixed
   three-column track left a dead column on the right - much more obvious now
   the cards are letterboxed. auto-fit collapses the empty tracks, so two
   posts fill the row and three still sit in three. The blog index keeps the
   fixed grid: ten posts fill it properly and the rhythm is the point there. */
#post-related { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }

/* ---- Link hub (links.html) ----------------------------------------------
   .svc__row is the right language for a list of destinations but far too tall
   for twenty of them - one row is up to 3rem of padding, and the whole point
   of this page is seeing everything at a glance. .linkrow is the same idea at
   a quarter of the height: the ink fill on hover, the spectrum-coloured tag
   in the left column, the arrow that leans on the way out. */
.links-head { padding-top: clamp(7rem, 16vh, 10rem); padding-bottom: clamp(1.5rem, 3vw, 2rem); }
.links-sec { padding-block: clamp(1rem, 2vw, 1.5rem); }
.links-cta { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: clamp(1.6rem, 3vw, 2.2rem); }

.links { border-top: 1px solid var(--line); margin-top: .7rem; }
.linkrow {
  display: grid; grid-template-columns: 3.4rem 1fr auto; gap: 1rem;
  align-items: center; padding: .58rem .2rem; border-bottom: 1px solid var(--line);
  position: relative; isolation: isolate; color: inherit; text-decoration: none;
  transition: padding-inline .35s var(--ease), color .35s var(--ease);
}
.linkrow::before {
  content: ""; position: absolute; inset: 0; z-index: -1; background: var(--ink);
  transform: scaleY(0); transform-origin: bottom; transition: transform .35s var(--ease);
}
.linkrow:hover, .linkrow:focus-visible { color: var(--bone); padding-inline: clamp(.6rem, 2vw, 1.2rem); }
.linkrow:hover::before, .linkrow:focus-visible::before { transform: scaleY(1); transform-origin: top; }
.linkrow__tag {
  font-family: var(--font-mono); font-size: var(--step--1); letter-spacing: .04em;
  text-align: center; white-space: nowrap;
}
.linkrow:nth-child(6n+1) .linkrow__tag { color: var(--c-red-ink); }
.linkrow:nth-child(6n+2) .linkrow__tag { color: var(--c-orange); }
.linkrow:nth-child(6n+3) .linkrow__tag { color: var(--c-green); }
.linkrow:nth-child(6n+4) .linkrow__tag { color: var(--c-teal-ink); }
.linkrow:nth-child(6n+5) .linkrow__tag { color: var(--c-blue); }
.linkrow:nth-child(6n)   .linkrow__tag { color: var(--c-magenta); }
.linkrow:hover .linkrow__tag, .linkrow:focus-visible .linkrow__tag { color: var(--bone); }
.linkrow__txt { display: flex; flex-direction: column; gap: .02rem; min-width: 0; line-height: 1.35; }
.linkrow__txt b { font-family: var(--font-display); font-weight: 360; font-size: var(--step-0); letter-spacing: -.01em; }
.linkrow__txt i {
  font-style: normal; font-size: var(--step--1); color: var(--bone-dim);
  transition: color .35s var(--ease);
}
.linkrow:hover .linkrow__txt i, .linkrow:focus-visible .linkrow__txt i { color: rgba(238,236,233,.72); }
.linkrow__go { font-size: 1.1rem; transition: transform .35s var(--ease); }
.linkrow:hover .linkrow__go { transform: translateX(.3em); }
@media (max-width: 520px) {
  .linkrow { grid-template-columns: 2.6rem 1fr auto; gap: .7rem; }
  .linkrow__txt i { font-size: calc(var(--step--1) * .96); }
}

@media (max-width: 900px) { .post-list { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .post-list { grid-template-columns: 1fr; } }

.article-head { padding-top: clamp(9rem, 20vh, 13rem); }
.article-head h1 { max-width: 20ch; }
/* The back link and the eyebrow were both inline-level, so they sat side by
   side on one line and the eyebrow's margin-top did nothing a line box would
   respect. The link is navigation, not part of the title block, so it gets
   its own row; the eyebrow becomes block-level flex so its margin applies. */
.article-head > .link { display: block; width: fit-content; }
.article-head .eyebrow { display: flex; width: fit-content; margin-top: clamp(1.8rem, 3.5vw, 2.8rem); }
.article-meta { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.1em; color: var(--bone-dim); margin-top: 1.2rem; }
/* A banner strip, not a hero. It was 16/8, which on a desktop made the
   category label taller than the first two paragraphs of the article.
   aspect-ratio has to be cleared or it fights the fixed height. */
.article-cover { margin-top: clamp(2rem, 5vw, 3.5rem); aspect-ratio: auto; height: 150px; }
/* A case study's hero was a 150px decorative strip, which is right for the
   typographic gradient panel and useless for a screenshot. When the hero
   actually carries a shot of the site we built, give it room to be looked at.
   Two classes, so it beats the rule above wherever either one moves. */
.article-cover.media--shot { height: auto; aspect-ratio: 1600 / 948; }
/* Same argument for a post's hero illustration as for a case study's shot:
   a 150px strip is a decoration, and this one is worth looking at. But a
   case study's hero IS the payoff, where a post's is the doorway, so this one
   is capped and centred: at full measure a 1586x992 drawing stands 810px tall
   on a desktop and puts the opening paragraph below the fold. Capping the
   width rather than the height keeps the artwork's own ratio, so it is
   scaled down whole instead of letterboxed onto bone bars. */
.article-cover.media--art {
  height: auto; aspect-ratio: 1586 / 992;
  max-width: 840px; margin-left: auto; margin-right: auto;
}
/* The outbound link to the client's live site, rendered from cases.json's
   site field. Sits between the story and the questions, so it needs air
   above it and less below. */
.article-visit { margin-top: clamp(1.6rem, 3vw, 2.4rem); }

/* The "try breaking our site" strip on the homepage. Reuses .chipish, the
   same control the Display panel's presets use, so pressing one here feels
   like the same object - it is the same setting underneath. */
.tryit { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.4rem; }
/* A little air before the undo, without pushing it onto a line of its own. */
.tryit__btn--reset { margin-left: 0.4rem; }
/* A <button> styled as the site's text link, so "All display settings" reads
   as a link without pretending to be a navigation. */
button.link { background: none; border: 0; padding: 0; font: inherit; cursor: pointer; }

/* ---- The middle of a case study, on dark --------------------------------
   Fifty case studies ran as three text blocks in one measure on one ground,
   which is what made them read as a wall. This lifts the "What we built"
   block onto the ink panel the rest of the site already uses for its bands.
   Inherits the prose type; only the ground and the colours change, and the
   spectrum hairline on top ties it to every other band on the site. */
.article-band {
  position: relative;
  background: var(--ink);
  color: var(--bone);
  margin: clamp(2rem, 4vw, 2.8rem) 0;
  padding: clamp(1.5rem, 4vw, 2.4rem);
  border-radius: 2px;
}
.article-band::before {
  content: "";
  position: absolute; inset-inline: 0; inset-block-start: 0;
  height: 2px; background: var(--spectrum);
  border-start-start-radius: 2px; border-start-end-radius: 2px;
}
.article-band > :first-child { margin-top: 0; }
.article-band > :last-child { margin-bottom: 0; }
.article-band h2 { color: var(--bone); }
.article-band p { color: color-mix(in srgb, var(--bone) 88%, transparent); }
.article-band strong { color: var(--bone); }
.article-band a { color: #3fc9bd; }
.article-band li { color: color-mix(in srgb, var(--bone) 88%, transparent); }
.article-band li::marker { color: color-mix(in srgb, var(--bone) 55%, transparent); }

/* ---- Reading rail -------------------------------------------------------
   Half of "this feels long" is not knowing how long. Two pixels of the brand
   spectrum, filling as you scroll. Sits under the header, which is why it is
   fixed rather than sticky. */
.readrail {
  position: fixed; inset-block-start: 0; inset-inline: 0;
  height: 2px; z-index: 60; background: transparent; pointer-events: none;
}
.readrail i { display: block; height: 100%; width: 0; background: var(--spectrum); }
@media (prefers-reduced-motion: no-preference) {
  .readrail i { transition: width .1s linear; }
}
.article-visit .link { font-family: var(--font-mono); font-size: var(--step--1); }
.article-related { margin-top: clamp(1.6rem, 3.5vw, 2.6rem); }
.article-related__links { display: flex; flex-wrap: wrap; gap: 0.6rem 1.6rem; margin-top: 0.7rem; }
.article-related .link { font-family: var(--font-mono); font-size: var(--step--1); }
.article-stats { margin-top: clamp(2rem, 5vw, 3rem); grid-template-columns: repeat(3, 1fr); }
.article-stats .num { font-size: var(--step-2); }
.article { max-width: 44rem; margin-inline: auto; padding-block: clamp(2.5rem, 6vw, 4.5rem); }
.article-foot { display: flex; gap: 1.5rem; align-items: center; flex-wrap: wrap; padding-bottom: clamp(3rem, 7vw, 6rem); border-bottom: 1px solid var(--line); }

/* Long-form prose */
.prose { font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.25rem); line-height: 1.7; }
.prose > * + * { margin-top: 1.4rem; }
.prose h2 { font-family: var(--font-display); font-size: var(--step-2); font-weight: 340; letter-spacing: -0.02em; line-height: 1.05; margin-top: 2.6rem; }
.prose h3 { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; margin-top: 2rem; }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.prose strong { font-weight: 600; }
.prose ul, .prose ol { padding-left: 1.3rem; }
.prose li { margin-block: 0.5rem; }
.prose li::marker { color: var(--accent); }
.prose blockquote { border-left: 3px solid; border-image: var(--spectrum) 1; padding-left: 1.4rem; font-family: var(--font-display); font-size: var(--step-1); font-style: italic; line-height: 1.35; color: var(--ink); }
.prose code { font-family: var(--font-mono); font-size: 0.9em; background: var(--bone-2); padding: 0.15em 0.4em; border-radius: 6px; }
.prose hr { border: none; border-top: 1px solid var(--line); margin-block: 2.4rem; }
.prose img { border-radius: 12px; margin-block: 1.8rem; }
/* Tables in articles (e.g. the pricing comparison). Scrolls on a phone rather
   than squashing, spectrum hairline under the header to keep it on-brand. */
.prose table { width: 100%; border-collapse: collapse; margin-block: 1.8rem; font-size: var(--step--1); display: block; overflow-x: auto; }
.prose thead th { text-align: left; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.8em; font-weight: 400; color: var(--ink); padding: 0 1rem 0.7rem 0; border-bottom: 2px solid; border-image: var(--spectrum) 1; white-space: nowrap; }
.prose tbody td { padding: 0.85rem 1rem 0.85rem 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.prose tbody tr:last-child td { border-bottom: none; }
.prose tbody td:first-child { font-weight: 600; }

/* ==========================================================================
   FIRST-VISIT WELCOME SCREEN
   ========================================================================== */
.welcome { position: fixed; inset: 0; z-index: 10001; background: var(--bone); display: none; flex-direction: column; justify-content: center; overflow-y: auto; padding: clamp(4.5rem, 12vh, 9rem) var(--gutter) clamp(2rem, 6vh, 4rem); }
.welcome.show { display: flex; }
.welcome.closing { clip-path: inset(0 0 100% 0); transition: clip-path 0.9s var(--ease); }
.welcome::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px; background: var(--spectrum); }
.welcome__inner { width: 100%; max-width: var(--maxw); margin-inline: auto; }
.welcome__title { font-size: clamp(2rem, 4.4vw, 4rem); line-height: 1.0; letter-spacing: -0.03em; max-width: 22ch; margin-top: 1.1rem; }
.welcome__sub { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.16em; color: var(--bone-dim); margin-top: clamp(1.5rem, 4vw, 2.5rem); }
.welcome__choices { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; margin-top: 1.5rem; }
.welcome__choice { position: relative; overflow: hidden; display: flex; flex-direction: column; gap: 0.45rem; min-height: 13.5rem; padding: clamp(1.5rem, 3vw, 2.3rem); border: 1px solid var(--line); border-radius: 18px; background: var(--bone); opacity: 0; transform: translateY(26px); transition: opacity 0.7s var(--ease), transform 0.7s var(--ease), background 0.5s var(--ease); }
.welcome.show .welcome__choice { opacity: 1; transform: none; }
.welcome.show .welcome__choice:nth-child(1) { transition-delay: 0.15s; }
.welcome.show .welcome__choice:nth-child(2) { transition-delay: 0.28s; }
.welcome.show .welcome__choice:nth-child(3) { transition-delay: 0.41s; }
.welcome__choice::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--spectrum); transform: scaleX(0); transform-origin: left; transition: transform 0.5s var(--ease); }
.welcome__choice:hover { transform: translateY(-8px); background: var(--bone-2); }
.welcome__choice:hover::before { transform: scaleX(1); }
.welcome__emoji { font-size: 2.4rem; line-height: 1; }
.welcome__k { font-family: var(--font-display); font-size: var(--step-2); font-weight: 340; letter-spacing: -0.02em; }
.welcome__choice:nth-child(1) .welcome__k { color: var(--c-red); }
.welcome__choice:nth-child(2) .welcome__k { color: var(--c-teal); }
.welcome__choice:nth-child(3) .welcome__k { color: var(--c-blue); }
.welcome__d { color: var(--bone-dim); font-size: var(--step-0); }
.welcome__arrow { margin-top: auto; font-size: 1.5rem; transition: transform 0.5s var(--ease); }
.welcome__choice:hover .welcome__arrow { transform: translate(0.35em, -0.35em); }
.welcome__skip { margin-top: clamp(1.8rem, 4vw, 2.8rem); align-self: flex-start; }
@media (max-width: 760px) { .welcome__choices { grid-template-columns: 1fr; } .welcome__choice { min-height: 0; } }

/* ==========================================================================
   PROJECT ESTIMATOR
   ========================================================================== */
.calc { display: grid; grid-template-columns: 1fr 380px; gap: clamp(2rem, 4vw, 4rem); align-items: start; }
@media (max-width: 980px) { .calc { grid-template-columns: 1fr; } }

.calc__paths { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
@media (max-width: 720px) { .calc__paths { grid-template-columns: 1fr 1fr; } }
.pathbtn { position: relative; overflow: hidden; text-align: left; border: 1px solid var(--line); border-radius: 16px; padding: 1.2rem; background: var(--bone); cursor: pointer; transition: transform 0.4s var(--ease), background 0.4s var(--ease), color 0.4s var(--ease); }
.pathbtn::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--spectrum); transform: scaleX(0); transform-origin: left; transition: transform 0.4s var(--ease); }
.pathbtn:hover { transform: translateY(-4px); }
.pathbtn.active { background: var(--ink); color: var(--bone); border-color: var(--ink); }
.pathbtn.active::before { transform: scaleX(1); }
.pathbtn .e { font-size: 1.6rem; line-height: 1; }
/* h2, not h4: these buttons are the first level under the page h1, and the
   jump straight to h4 was a heading-order violation on every load. */
.pathbtn h2 { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; letter-spacing: -0.02em; margin-top: 0.4rem; }
.pathbtn p { font-size: var(--step--1); color: var(--bone-dim); margin-top: 0.2rem; }
.pathbtn.active p { color: rgba(238, 236, 233, 0.7); }

.calc__groups { margin-top: clamp(2rem, 4vw, 3rem); display: grid; gap: clamp(1.6rem, 3vw, 2.4rem); }
.calc__groups:empty { display: none; }
.cgroup h3 { font-family: var(--font-display); font-size: var(--step-1); font-weight: 360; letter-spacing: -0.01em; margin-bottom: 0.2rem; }
.cgroup .help { font-size: var(--step--1); color: var(--bone-dim); margin-bottom: 1rem; }
.opts { display: grid; gap: 0.7rem; }
.opts.two { grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .opts.two { grid-template-columns: 1fr; } }
.opt { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; border: 1px solid var(--line); border-radius: 12px; padding: 0.9rem 1.1rem; cursor: pointer; transition: border-color 0.3s var(--ease), background 0.3s var(--ease); }
.opt:hover { border-color: var(--ink); }
.opt.sel { border-color: var(--ink); background: var(--bone-2); }
.opt__l { display: flex; align-items: flex-start; gap: 0.8rem; }
.opt .box { width: 20px; height: 20px; margin-top: 2px; border: 1px solid var(--line); border-radius: 6px; flex-shrink: 0; display: grid; place-items: center; transition: background 0.25s, border-color 0.25s; }
.opt.radio .box { border-radius: 50%; }
.opt.sel .box { background: var(--ink); border-color: var(--ink); }
.opt .box::after { content: ""; width: 7px; height: 7px; border-radius: inherit; background: var(--bone); opacity: 0; transition: opacity 0.2s; }
.opt.sel .box::after { opacity: 1; }
.opt .name { font-size: var(--step-0); display: flex; flex-direction: column; gap: 3px; }
.opt__desc { font-size: var(--step--1); color: var(--bone-dim); line-height: 1.4; max-width: 46ch; }
.opt .price { font-family: var(--font-mono); font-size: var(--step--1); color: var(--bone-dim); white-space: nowrap; }

/* ---- Estimator: plain-English summary and the shareable link ------------ */
/* The figure alone made people guess at what it covered. This is the same
   estimate in words, and it is what gets shared and emailed. */
.calc__prose { margin-top: 1.2rem; padding-top: 1.2rem; border-top: 1px solid var(--line); font-size: var(--step--1); line-height: 1.6; color: var(--ink); }
.calc__prose p { margin-bottom: 0.7rem; }
.calc__prose p:last-child { margin-bottom: 0; color: var(--bone-dim); }
.calc__prose .calc__prose-lead { color: var(--bone-dim); margin-bottom: 0.5rem; }
.calc__prose ul { margin: 0 0 0.9rem; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 0.35rem; }
.calc__prose li { display: grid; grid-template-columns: 0.8rem 1fr; gap: 0.5rem; }
.calc__prose li::before { content: "\2022"; color: var(--accent); }
.calc__shared { font-family: var(--font-mono); font-size: var(--step--1); color: var(--accent); margin-bottom: 1rem; line-height: 1.5; }
#calc-share { margin-top: 0.6rem; }
.calc__shareurl { width: 100%; margin-top: 0.7rem; padding: 0.6rem 0.8rem; font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink); background: var(--bone); border: 1px solid var(--line); border-radius: 6px; }

.calc__result { position: sticky; top: 5.5rem; border: 1px solid var(--ink); border-radius: 20px; padding: clamp(1.5rem, 3vw, 2rem); background: var(--bone); }
.calc__result .kicker { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.14em; color: var(--bone-dim); }
.calc__big { font-family: var(--font-display); font-size: var(--step-3); font-weight: 340; line-height: 1; letter-spacing: -0.03em; margin-top: 0.4rem; }
.calc__from { color: var(--bone-dim); margin-top: 0.5rem; font-size: var(--step-0); }
.calc__mo { color: var(--accent); font-size: var(--step--1); margin-top: 0.3rem; }
.calc__bd { margin: 1.4rem 0; }
.calc__bd div { display: flex; justify-content: space-between; gap: 1rem; padding: 0.45rem 0; border-bottom: 1px solid var(--line-soft); font-size: var(--step--1); }
.calc__bd .l { color: var(--bone-dim); }
.calc__bd .v { font-family: var(--font-mono); white-space: nowrap; }
.calc__result .btn { width: 100%; justify-content: center; }
.calc__note { font-size: var(--step--1); color: var(--bone-mute); margin-top: 1rem; line-height: 1.55; }
.calc__empty { color: var(--bone-dim); }

/* ==========================================================================
   AMBIENT MOTION — hero glow + sound-wave equalizer
   ========================================================================== */
.hero::before {
  content: ""; position: absolute; inset: -25%; z-index: -1; pointer-events: none; filter: blur(70px);
  background:
    radial-gradient(30% 38% at 22% 28%, rgba(230, 51, 41, 0.30), transparent 60%),
    radial-gradient(26% 34% at 78% 24%, rgba(244, 183, 14, 0.28), transparent 60%),
    radial-gradient(34% 40% at 68% 74%, rgba(47, 116, 208, 0.28), transparent 62%),
    radial-gradient(30% 38% at 28% 80%, rgba(89, 183, 58, 0.24), transparent 60%),
    radial-gradient(26% 32% at 52% 52%, rgba(106, 63, 208, 0.24), transparent 60%);
  background-repeat: no-repeat; opacity: 0.7; will-change: transform;
  animation: heroMesh 30s ease-in-out infinite;
}
@keyframes heroMesh {
  0%   { transform: translate3d(-3%, -2%, 0) scale(1.08) rotate(0deg); }
  33%  { transform: translate3d(3%, 1%, 0) scale(1.2) rotate(8deg); }
  66%  { transform: translate3d(-1%, 4%, 0) scale(1.12) rotate(-6deg); }
  100% { transform: translate3d(-3%, -2%, 0) scale(1.08) rotate(0deg); }
}

.eq-wrap { pointer-events: none; }
.eq-wrap--band { margin-top: clamp(2rem, 5vw, 3.2rem); }
.eq { display: inline-flex; align-items: flex-end; gap: 5px; height: 46px; }
.eq span { width: 4px; height: 100%; border-radius: 3px; transform: scaleY(0.16); transform-origin: bottom; animation: eqp 1.5s ease-in-out infinite; will-change: transform; }
@keyframes eqp { 0%, 100% { transform: scaleY(0.16); } 50% { transform: scaleY(1); } }
.eq span:nth-child(8n+1) { background: var(--c-red); animation-duration: 1.3s; animation-delay: -0.1s; }
.eq span:nth-child(8n+2) { background: var(--c-orange); animation-duration: 1.7s; animation-delay: -0.5s; }
.eq span:nth-child(8n+3) { background: var(--c-yellow); animation-duration: 1.1s; animation-delay: -0.9s; }
.eq span:nth-child(8n+4) { background: var(--c-green); animation-duration: 1.9s; animation-delay: -0.3s; }
.eq span:nth-child(8n+5) { background: var(--c-teal); animation-duration: 1.4s; animation-delay: -0.7s; }
.eq span:nth-child(8n+6) { background: var(--c-blue); animation-duration: 1.6s; animation-delay: -0.2s; }
.eq span:nth-child(8n+7) { background: var(--c-indigo); animation-duration: 1.25s; animation-delay: -0.6s; }
.eq span:nth-child(8n)   { background: var(--c-magenta); animation-duration: 1.8s; animation-delay: -0.4s; }

/* ==========================================================================
   IN-PLACE PAGE COPY EDITOR (?edit)
   ========================================================================== */
body.pe-on { cursor: auto; }
body.pe-on .cursor, body.pe-on .cursor-dot { display: none !important; }
.pe-edit { outline: 1px dashed rgba(30, 30, 30, 0.35); outline-offset: 3px; border-radius: 3px; transition: outline-color 0.2s, background 0.2s; }
.pe-edit:hover { outline-color: var(--accent); }
.pe-edit:focus { outline: 2px solid var(--accent); background: rgba(255, 61, 31, 0.06); }
.pe-bar { position: fixed; left: 50%; bottom: 1.2rem; transform: translateX(-50%); z-index: 10002; display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; justify-content: center; max-width: calc(100vw - 2rem); background: var(--ink); color: var(--bone); padding: 0.6rem 0.8rem; border-radius: 100px; box-shadow: 0 12px 44px rgba(30, 30, 30, 0.28); font-family: var(--font-mono); font-size: var(--step--1); }
.pe-bar .pe-lbl { padding-inline: 0.4rem; }
.pe-bar .pe-lbl b { color: var(--c-yellow); }
.pe-bar #pe-count { color: var(--bone-dim); }
.pe-bar button { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.08em; padding: 0.5em 0.9em; border-radius: 100px; border: 1px solid rgba(238, 236, 233, 0.3); color: var(--bone); }
.pe-bar button:hover { background: var(--bone); color: var(--ink); }
.pe-bar .pe-save { background: var(--bone); color: var(--ink); border-color: var(--bone); }
.pe-bar .pe-save[disabled] { opacity: 0.5; cursor: progress; }
.pe-bar .pe-signin { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.08em; padding: 0.5em 0.9em; border-radius: 100px; border: 1px solid var(--c-yellow); background: var(--c-yellow); color: var(--ink); }
.pe-bar .pe-signin:hover { background: var(--bone); border-color: var(--bone); }
.pe-bar .pe-msg { padding-inline: 0.4rem; color: var(--bone-dim); max-width: 46ch; }
.pe-bar .pe-msg b { color: var(--c-yellow); }
.pe-bar .pe-msg--bad { color: var(--bone); }
.pe-bar .pe-msg--bad b { color: #ff7a63; }
.pe-bar .pe-unsaved { color: #ff7a63; }
.pe-bar--locked { border: 1px solid #ff7a63; }
.edit-chip {
  position: fixed; left: 1.2rem; bottom: 4.7rem; z-index: 9992;
  display: inline-flex; align-items: center; gap: 0.45rem;
  background: var(--ink); color: var(--bone); border-radius: 100px;
  padding: 0.55em 1em; font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.08em;
  box-shadow: 0 10px 34px rgba(30, 30, 30, 0.22);
  opacity: 0; transform: translateY(0.5rem); transition: opacity 0.4s var(--ease), transform 0.4s var(--ease), background 0.2s;
}
.edit-chip.is-in { opacity: 1; transform: none; }
.edit-chip:hover { background: var(--accent); }
.edit-chip__pen { font-size: 1.05em; }
@media (max-width: 520px) { .edit-chip { bottom: 4.4rem; padding: 0.5em 0.85em; } }

/* ==========================================================================
   Generic hidden guard. Some "recent work" sections in launch.html /
   create.html stay hidden via the `hidden` attribute until real content
   replaces their placeholders. Success Stories (work.html) is now live.
   ========================================================================== */
[hidden] { display: none !important; }

/* ---- Accessibility utilities ---------------------------------------------- */

/* Visually hidden, but still announced by screen readers. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Skip link - off-screen until focused, then pinned top-left. Lets keyboard
   users jump the fixed header instead of tabbing the whole nav on every page.
   Sits above the nav (200) but below the preloader/welcome modals (10000+). */
.skip-link {
  position: fixed; top: 0; left: 0; z-index: 300;
  width: 1px; height: 1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  background: var(--ink); color: var(--bone);
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.12em;
  border-bottom-right-radius: 6px;
}
.skip-link:focus {
  width: auto; height: auto; padding: 0.9em 1.4em; overflow: visible;
  clip: auto; clip-path: none;
  outline: 3px solid var(--focus-ring-invert); outline-offset: -6px;
}

/* Give the skip-link target somewhere to land clear of the fixed header. */
:target { scroll-margin-top: 6rem; }

/* ---- Focus visibility -----------------------------------------------------
   The stylesheet previously had two :focus rules in 56KB and stripped the
   outline off every form field, leaving keyboard users with no idea where
   they were. :focus-visible shows a ring for keyboard and AT focus but not
   for mouse clicks, so the design stays clean either way.
   -------------------------------------------------------------------------- */
:where(a, button, input, textarea, select, summary,
       [tabindex]:not([tabindex="-1"])):focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
  border-radius: 3px;
}

/* The mobile menu is a dark surface - invert the ring so it stays visible. */
.menu :where(a, button):focus-visible {
  outline-color: var(--focus-ring-invert);
}

/* Form fields sit flush against a baseline border; tighten the offset. */
.field :where(input, textarea, select):focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Chips are real buttons now; keep the ring clear of the pill edge. */
.chip:focus-visible { outline-offset: 4px; }

/* Estimator rows are radios and checkboxes (role set in estimate.js); the
   ring follows their 12px corner rather than the default 3px. */
.opt:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 3px; border-radius: 12px; }

/* Older browsers without :focus-visible still get a visible ring. */
@supports not selector(:focus-visible) {
  :where(a, button, input, textarea, select, summary,
         [tabindex]:not([tabindex="-1"])):focus {
    outline: 3px solid var(--focus-ring); outline-offset: 3px;
  }
}

/* When the menu is closed it is inert and fully hidden from AT (see main.js).
   clip-path alone only hid it visually - its links stayed in the tab order. */
.menu[hidden] { display: none !important; }

/* ==========================================================================
   DISPLAY PREFERENCES
   A first-party preferences panel - NOT a third-party accessibility overlay,
   and it makes no compliance claim. The site's semantics, focus order,
   contrast and labelling are fixed in the markup; this only lets a visitor
   tune presentation on top of that.

   Presets are named for what they change, not for a diagnosis. A stylesheet
   cannot make a page "epilepsy safe": the setting only applies after the
   visitor finds and clicks it, by which point any flashing has played.

   Attributes land on <html> before first paint via the inline script in each
   page's <head>, so moving between pages never flashes the wrong state.
   ========================================================================== */

/* ---- Text ----------------------------------------------------------------- */
html[data-text="large"]  { font-size: 112.5%; }
html[data-text="larger"] { font-size: 125%; }

/* Body copy only: headings set their own leading and should keep it. */
html[data-leading="roomy"] :is(p, li, dd, blockquote) { line-height: 1.85; }
html[data-leading="loose"] :is(p, li, dd, blockquote) { line-height: 2.15; }

/* Not applied to the mono/uppercase labels, which are already tracked out. */
html[data-tracking="wide"]  :is(p, li, dd, blockquote) { letter-spacing: 0.022em; }
html[data-tracking="wider"] :is(p, li, dd, blockquote) { letter-spacing: 0.055em; }
html[data-words="wide"]     :is(p, li, dd, blockquote) { word-spacing: 0.18em; }

/* A wide, open, universally installed stack. Deliberately not OpenDyslexic:
   the research does not support it improving reading for dyslexic readers,
   so it would be a claim we cannot back. It is OFL-licensed and could be
   self-hosted later as an extra choice if wanted. */
html[data-font="plain"] {
  --font-display: Verdana, Tahoma, "DejaVu Sans", Geneva, sans-serif;
  --font-ui:      Verdana, Tahoma, "DejaVu Sans", Geneva, sans-serif;
  --font-mono:    "DejaVu Sans Mono", Consolas, "Courier New", monospace;
}

html[data-align="left"] :is(p, li, h1, h2, h3, h4, h5, h6, figcaption, blockquote,
                            .display, .lead, .eyebrow, .welcome__title) {
  text-align: left !important;
  text-wrap: initial;
}

/* ---- Contrast ------------------------------------------------------------- */
html[data-contrast="higher"] {
  --bone-dim:  #33312c;
  --bone-mute: #3a3833;
  --line:      rgba(30, 30, 30, 0.42);
  --line-soft: rgba(30, 30, 30, 0.26);
  --accent:    #095853;   /* 7.03:1 on bone */
}
/* background-IMAGE, never the `background` shorthand: the shorthand resets
   background-clip to border-box, which undoes the clip-to-text set on `em`
   and paints the gradient as a solid block with the letters invisible. */
html[data-contrast="higher"] em,
html[data-contrast="higher"] .grad-text { background-image: var(--spectrum-text); }
html[data-contrast="higher"] .cover__label--chip > span { background-image: var(--spectrum-text); }
html[data-contrast="higher"] .nav::before { background: var(--bone); }

/* Dark. Every value below was contrast-checked against the dark surface:
   body text 14.51:1, secondary 8.42:1, tertiary 6.36:1, accent 6.44:1. */
html[data-contrast="dark"] {
  --c-red-ink:#e94b43;
  --c-orange-ink:#f47b20;
  --c-green-ink:#59b73a;
  --c-teal-ink:#12b0a6;
  --c-blue-ink:#4885d6;
  --c-indigo-ink:#9171dc;
  --c-magenta-ink:#da4ca5;
  --bone:      #1a1917;
  --bone-2:    #232220;
  --bone-3:    #2c2b28;
  --ink:       #ece9e4;
  --bone-dim:  #b8b3aa;
  --bone-mute: #a09b92;
  --line:      rgba(236, 233, 228, 0.20);
  --line-soft: rgba(236, 233, 228, 0.10);
  --accent:    #12b0a6;   /* the unmodified brand teal - 6.51:1 on the dark surface */
  --spectrum-text: var(--spectrum-on-dark);
  color-scheme: dark;
}
/* Spots where the light-only design hardcoded a light-on-dark pair: with
   --bone now dark, these would be dark-on-dark without the override. */
html[data-contrast="dark"] .menu { color: var(--ink); }
html[data-contrast="dark"] .cursor { border-color: var(--ink); }
html[data-contrast="dark"] .nav::before { background: rgba(26, 25, 23, 0.72); }
html[data-contrast="dark"] .preloader,
html[data-contrast="dark"] .welcome { background: var(--bone); }
html[data-contrast="dark"] em,
html[data-contrast="dark"] .grad-text { background-image: var(--spectrum-text); }
html[data-contrast="dark"] :is(.cookie, .tagpop, .prefs) { box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55); }

/* ---- Colour intensity -----------------------------------------------------
   Recomputed tokens rather than a CSS filter: a filter on the root element
   creates a containing block, which would break every position:fixed
   element on the site (nav, menu, cursor, this panel). Greys for "None"
   are luminance-matched, so contrast relationships survive. */
html[data-saturation="low"] {
  --c-red-ink:#a2514d;
  --c-orange-ink:#8e5f3b;
  --c-green-ink:#517146;
  --c-teal-ink:#34726e;
  --c-blue-ink:#506b90;
  --c-indigo-ink:#725ea3;
  --c-magenta-ink:#99527e;
  --c-red:     #b2615d;
  --c-orange:  #ba835a;
  --c-yellow:  #b5994d;
  --c-green:   #6a955c;
  --c-teal:    #3d8580;
  --c-blue:    #5b7aa4;
  --c-indigo:  #7a67a8;
  --c-magenta: #ab6390;
  --c-pink:    #cf7fa1;
  --spectrum: linear-gradient(90deg, #b2615d, #ba835a, #b5994d, #6a955c, #3d8580, #5b7aa4, #7a67a8, #ab6390);
  --spectrum-conic: conic-gradient(from 210deg, #b2615d, #ba835a, #b5994d, #6a955c, #3d8580, #5b7aa4, #7a67a8, #ab6390, #b2615d);
}
html[data-saturation="high"] {
  --c-red-ink:#d30b00;
  --c-orange-ink:#ae4a00;
  --c-green-ink:#2b7811;
  --c-teal-ink:#00766f;
  --c-blue-ink:#0a64dc;
  --c-indigo-ink:#5d1ef1;
  --c-magenta-ink:#c9047f;
  --c-red:     #ff1d10;
  --c-orange:  #ff7915;
  --c-yellow:  #ffbc03;
  --c-green:   #4bd31e;
  --c-teal:    #00c2b6;
  --c-blue:    #0b6ff4;
  --c-indigo:  #5d1ef1;
  --c-magenta: #fa14a4;
  --c-pink:    #ff4f9a;
  --spectrum: linear-gradient(90deg, #ff1d10, #ff7915, #ffbc03, #4bd31e, #00c2b6, #0b6ff4, #5d1ef1, #fa14a4);
  --spectrum-conic: conic-gradient(from 210deg, #ff1d10, #ff7915, #ffbc03, #4bd31e, #00c2b6, #0b6ff4, #5d1ef1, #fa14a4, #ff1d10);
}
html[data-saturation="mono"] {
  --c-red-ink:#696969;
  --c-orange-ink:#656565;
  --c-green-ink:#616161;
  --c-teal-ink:#656565;
  --c-blue-ink:#5d5d5d;
  --c-indigo-ink:#5e5e5e;
  --c-magenta-ink:#696969;
  --c-red:     #7a7a7a;
  --c-orange:  #9d9d9d;
  --c-yellow:  #c1c1c1;
  --c-green:   #a2a2a2;
  --c-teal:    #9d9d9d;
  --c-blue:    #757575;
  --c-indigo:  #5e5e5e;
  --c-magenta: #7a7a7a;
  --c-pink:    #939393;
  --spectrum: linear-gradient(90deg, #7a7a7a, #9d9d9d, #c1c1c1, #a2a2a2, #9d9d9d, #757575, #5e5e5e, #7a7a7a);
  --spectrum-conic: conic-gradient(from 210deg, #7a7a7a, #9d9d9d, #c1c1c1, #a2a2a2, #9d9d9d, #757575, #5e5e5e, #7a7a7a, #7a7a7a);
  --accent: #6b6b6b;
}

/* Dark: a white chip would be a glare panel on a dark page. The chip takes
   the raised dark surface and the text the lifted ramp, same as .band. */
html[data-contrast="dark"] .cover__label--chip {
  background: var(--bone-2);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.45), inset 0 0 0 1px var(--line-soft);
}
html[data-contrast="dark"] .cover__label--chip > span { background-image: var(--spectrum-on-dark); }

/* Mono: gradient text goes to plain ink rather than pale grey. */
html[data-saturation="mono"] em,
html[data-saturation="mono"] .cover__label--chip > span,
html[data-saturation="mono"] .grad-text {
  background-image: none;
  -webkit-text-fill-color: currentColor;
  color: var(--ink);
}

/* ---- Links and headings ---------------------------------------------------
   a { text-decoration: none } is global and .link only underlines on hover,
   so several links are otherwise distinguished by colour alone. */
html[data-links="underline"] :is(.nav__links a, .menu a, .footer__grid a, .link, .prose a, .cookie__text a),
html[data-links="highlight"] :is(.nav__links a, .menu a, .footer__grid a, .link, .prose a, .cookie__text a) {
  text-decoration: underline;
  text-underline-offset: 3px;
}
html[data-links="highlight"] :is(.nav__links a, .menu a, .footer__grid a, .link, .prose a) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}
html[data-headings="highlight"] :is(h1, h2, h3, h4, h5, h6) {
  outline: 2px dashed var(--accent);
  outline-offset: 5px;
  border-radius: 3px;
}

/* ---- Pointer -------------------------------------------------------------- */
html[data-pointer="system"] body,
html[data-pointer="system"] .field :is(input, textarea, select) { cursor: auto; }
html[data-pointer="system"] :is(a, button, .chip, summary, label) { cursor: pointer; }
html[data-pointer="large"] body,
html[data-pointer="large"] .field :is(input, textarea, select),
html[data-pointer="large"] :is(a, button, .chip, summary, label) { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2244%22%20height%3D%2244%22%20viewBox%3D%220%200%2044%2044%22%3E%3Cpath%20d%3D%22M7%203%20L7%2035%20L15%2027%20L20%2039%20L26%2036.5%20L21%2025%20L33%2025%20Z%22%20fill%3D%22%23111%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222.5%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E") 6 3, auto; }
html[data-pointer="system"] :is(.cursor, .cursor-dot),
html[data-pointer="large"]  :is(.cursor, .cursor-dot) { display: none !important; }

/* ---- Reading aids ---------------------------------------------------------
   One element, repositioned from the mouse OR the keyboard focus, so it is
   useful without a pointer. Below the panel (9995) so the panel stays usable
   while a mask is on. */
.prefs-aid { position: fixed; inset: 0; z-index: 9993; pointer-events: none; --aid-y: 50vh; }
.prefs-aid > * { position: absolute; left: 0; right: 0; }
html[data-aid="guide"] .prefs-aid__band {
  top: calc(var(--aid-y) - 1.5px); height: 3px;
  background: var(--accent); opacity: 0.85;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.5);
}
html[data-aid="guide"] :is(.prefs-aid__over, .prefs-aid__under) { display: none; }
html[data-aid="mask"] .prefs-aid__over  { top: 0; height: calc(var(--aid-y) - 2.3rem); }
html[data-aid="mask"] .prefs-aid__under { top: calc(var(--aid-y) + 2.3rem); bottom: 0; }
html[data-aid="mask"] :is(.prefs-aid__over, .prefs-aid__under) { background: rgba(12, 12, 10, 0.66); }
html[data-aid="mask"] .prefs-aid__band { display: none; }
html[data-motion="full"] .prefs-aid > * { transition: top 0.12s linear, height 0.12s linear; }

/* ---- Trigger --------------------------------------------------------------
   Bottom-left, held back until the cookie notice has been answered, then
   faded in where the notice was. visibility:hidden also keeps it out of the
   tab order until then, so it is never a phantom stop. */
.prefs-fab {
  position: fixed; left: 1.2rem; bottom: 1.2rem; z-index: 9994;
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: var(--bone); color: var(--ink);
  border: 1px solid var(--line); border-radius: 100px;
  padding: 0.7em 1.1em;
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.1em;
  box-shadow: 0 10px 34px rgba(30, 30, 30, 0.16);
  opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease),
              visibility 0.45s var(--ease), background 0.3s var(--ease), color 0.3s var(--ease);
}
.prefs-fab.is-in { opacity: 1; visibility: visible; transform: none; }
.prefs-fab:hover { background: var(--ink); color: var(--bone); }
.prefs-fab__mark {
  width: 1.15em; height: 1.15em; flex: none; border-radius: 50%;
  background: var(--spectrum-conic);
}

/* ---- Panel ---------------------------------------------------------------- */
.prefs {
  position: fixed; left: 1.2rem; bottom: 4.9rem; z-index: 9995;
  width: min(22rem, calc(100vw - 2.4rem));
  max-height: min(34rem, calc(100vh - 7.5rem)); overflow-y: auto;
  background: var(--bone); color: var(--ink);
  border: 1px solid var(--line); border-radius: 18px;
  padding: 1.3rem 1.35rem 0;
  box-shadow: 0 20px 60px rgba(30, 30, 30, 0.24);
}
.prefs__title {
  font-family: var(--font-display); font-size: 1.35rem;
  letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.3rem;
}
.prefs__intro { font-size: var(--step--1); line-height: 1.5; color: var(--bone-dim); margin-bottom: 1.1rem; }
.prefs fieldset { border: 0; padding: 0; margin: 0 0 1rem; }
.prefs legend {
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--bone-dim); margin-bottom: 0.5rem; padding: 0;
}
.prefs-sect { border-top: 1px solid var(--line-soft); padding-top: 0.95rem; margin-bottom: 0.2rem; }
.prefs-sect__h {
  font-family: var(--font-display); font-size: 1rem; font-weight: 400;
  letter-spacing: -0.01em; margin-bottom: 0.75rem;
}
.prefs__row { position: relative; display: flex; flex-wrap: wrap; gap: 0.4rem; }
/* The radio keeps its native keyboard behaviour and stays in the
   accessibility tree; only its box is hidden. pointer-events:none matters:
   these are absolutely positioned and stack, so they would otherwise
   intercept each other's clicks. The <label for> is the pointer target. */
.prefs__row input {
  position: absolute; width: 1px; height: 1px;
  margin: 0; padding: 0; border: 0;
  opacity: 0; pointer-events: none;
}
.prefs__row label, .chipish {
  border: 1px solid var(--line); border-radius: 100px;
  padding: 0.45em 0.85em;
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: 0.06em; cursor: pointer; color: inherit;
  transition: background 0.25s var(--ease), color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.prefs__row label:hover, .chipish:hover { border-color: var(--ink); }
.prefs__row input:checked + label,
.chipish[aria-pressed="true"] { background: var(--ink); color: var(--bone); border-color: var(--ink); }
.prefs__row input:focus-visible + label { outline: 3px solid var(--focus-ring); outline-offset: 3px; }
.prefs__note { font-size: 0.72rem; line-height: 1.45; color: var(--bone-dim); margin-top: 0.4rem; }
.prefs-presets .prefs__row { gap: 0.35rem; }

.prefs-struct { margin-top: 0.7rem; }
.prefs-struct ul { list-style: none; margin: 0; padding: 0; }
.prefs-struct li { margin: 0; }
.prefs-struct a {
  display: block; padding: 0.3rem 0; font-size: var(--step--1);
  line-height: 1.35; color: var(--ink); text-decoration: underline;
  text-underline-offset: 2px;
}
.prefs-struct li.lvl-h2 a { padding-left: 0.9rem; }
.prefs-struct li.lvl-h3 a { padding-left: 1.8rem; color: var(--bone-dim); }

/* Sticky: the panel scrolls internally at the larger text sizes, and Reset
   and Close have to stay reachable. */
.prefs__foot {
  position: sticky; bottom: 0; z-index: 1;
  display: flex; justify-content: space-between; align-items: center;
  gap: 0.6rem; border-top: 1px solid var(--line-soft);
  padding: 0.9rem 0 1.15rem; margin-top: 0.4rem;
  background: var(--bone);
}
.prefs__foot button {
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.1em;
  border: 1px solid var(--line); border-radius: 100px; padding: 0.5em 1em;
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}
.prefs__foot button:hover { background: var(--ink); color: var(--bone); border-color: var(--ink); }

@media (max-width: 620px) {
  .prefs { left: 0.8rem; width: min(21rem, calc(100vw - 1.6rem)); bottom: 4.5rem; }
  .prefs-fab { left: 0.8rem; bottom: 0.8rem; }
}
@media print { .prefs, .prefs-fab, .prefs-aid { display: none !important; } }

/* ==========================================================================
   MINI FORM (estimator -> email us)
   The estimator used to hand its summary to the contact page via
   localStorage, which meant a page change and a long form for what is
   really one question. This is a small modal that asks for an email and
   posts the summary straight to contact.php.
   Above every other layer, including the page editor bar (10002).
   ========================================================================== */
.mini { position: fixed; inset: 0; z-index: 10003; display: grid; place-items: center; padding: var(--gutter); }
.mini__scrim { position: absolute; inset: 0; background: rgba(12, 12, 10, 0.58); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.mini__card {
  position: relative; width: min(31rem, 100%);
  max-height: calc(100vh - 3rem); overflow-y: auto;
  background: var(--bone); color: var(--ink);
  border: 1px solid var(--line); border-radius: 18px;
  padding: 1.7rem 1.6rem 1.45rem;
  box-shadow: 0 26px 74px rgba(12, 12, 10, 0.42);
}
.mini__x {
  position: absolute; top: 9px; right: 11px;
  width: 30px; height: 30px; display: grid; place-items: center;
  border: none; background: none; color: var(--bone-dim);
  font-size: 1.6rem; line-height: 1; border-radius: 50%;
  transition: color 0.25s var(--ease), background 0.25s var(--ease);
}
.mini__x:hover { color: var(--ink); background: var(--bone-2); }
.mini__t {
  font-family: var(--font-display); font-size: clamp(1.4rem, 3.4vw, 1.8rem);
  line-height: 1.12; letter-spacing: -0.02em; margin-bottom: 0.5rem; padding-right: 1.5rem;
}
.mini__d { font-size: var(--step-0); line-height: 1.55; color: var(--bone-dim); margin-bottom: 1.25rem; }
.mini__form .field { margin-bottom: 1rem; }
.mini__err {
  font-size: var(--step--1); line-height: 1.5; color: var(--accent);
  border-left: 3px solid var(--accent); padding: 0.35rem 0 0.35rem 0.7rem;
  margin-bottom: 0.9rem;
}
.mini__fine { font-size: 0.74rem; line-height: 1.5; color: var(--bone-mute); margin-top: 0.9rem; }
.mini__fine a { text-decoration: underline; text-underline-offset: 2px; }
.mini__done { display: flex; flex-direction: column; gap: 0.7rem; }
.mini__tick { width: 2.4rem; height: 2.4rem; border-radius: 50%; background: var(--spectrum-conic); }
.mini__alt { margin-top: 0.8rem; font-size: var(--step--1); }
/* The honeypot wrapper is inline-styled off-screen and aria-hidden, matching
   the contact form. Nothing needed here. */

/* ---- Reduced motion -------------------------------------------------------
   data-motion is resolved once, before first paint, by the inline script in
   each page's <head>: it is the OS prefers-reduced-motion setting unless the
   visitor has overridden it in the display preferences panel. Keying off the
   attribute keeps these rules in exactly one place.
   -------------------------------------------------------------------------- */
html[data-motion="reduce"] * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
html[data-motion="reduce"] [data-reveal],
html[data-motion="reduce"] .reveal-lines .line > span,
html[data-motion="reduce"] .welcome__choice { opacity: 1 !important; transform: none !important; }
html[data-motion="reduce"] .cursor, html[data-motion="reduce"] .cursor-dot { display: none; }
html[data-motion="reduce"] body { cursor: auto; }
html[data-motion="reduce"] .marquee__track,
html[data-motion="reduce"]::before,
html[data-motion="reduce"] .hero::before,
html[data-motion="reduce"] .ph-1, html[data-motion="reduce"] .ph-2, html[data-motion="reduce"] .ph-3,
html[data-motion="reduce"] .ph-4, html[data-motion="reduce"] .ph-5, html[data-motion="reduce"] .ph-6 { animation: none !important; }
html[data-motion="reduce"] .eq-wrap { display: none; }
html[data-motion="reduce"] .divider::before { transform: scaleX(1) !important; }

/* Fallback when JavaScript has not set the attribute: honour the OS directly. */
@media (prefers-reduced-motion: reduce) {
  html:not([data-motion]) *, html:not([data-motion]) *::before, html:not([data-motion]) *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important; scroll-behavior: auto !important;
  }
  html:not([data-motion]) [data-reveal],
  html:not([data-motion]) .reveal-lines .line > span,
  html:not([data-motion]) .welcome__choice { opacity: 1 !important; transform: none !important; }
  html:not([data-motion]) .cursor, html:not([data-motion]) .cursor-dot { display: none; }
  html:not([data-motion]) body { cursor: auto; }
  html:not([data-motion]) .eq-wrap { display: none; }
}

/* ---- Dictionary definitions (about page) --------------------------------
   Two entries side by side. Deliberately quieter than the page around it:
   the surrounding sections carry the display type, so these stay at body
   size and let the words do the work.
   Appended at the end on purpose: new class names, nothing above them to
   lose a specificity tie to.
   ---------------------------------------------------------------------- */
.defns { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(1rem, 2.2vw, 1.6rem); }   /* stretch, so both cards end level */
@media (max-width: 780px) { .defns { grid-template-columns: 1fr; } }
.defn {
  position: relative; overflow: hidden;
  background: var(--bone-2); border: 1px solid var(--line); border-radius: 16px;
  padding: clamp(1.2rem, 2.2vw, 1.7rem);
  padding-left: clamp(1.5rem, 2.6vw, 2rem);
}
.defn::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--spectrum); }
.defn__head { display: flex; align-items: baseline; gap: 0.5rem 0.7rem; flex-wrap: wrap; padding-bottom: 0.8rem; border-bottom: 1px solid var(--line); }
.defn__word { font-family: var(--font-display); font-size: clamp(1.7rem, 2.6vw, 2.2rem); font-weight: 340; letter-spacing: -0.03em; line-height: 1; }
.defn__say { font-family: var(--font-mono); font-size: var(--step--1); color: var(--bone-dim); }
.defn__pos { font-family: var(--font-display); font-style: italic; font-size: var(--step--1); color: var(--bone-dim); }
.defn__senses { list-style: none; margin: 0; padding: 0; }
.defn__senses > li {
  display: grid; grid-template-columns: 1.4rem 1fr; column-gap: 0.6rem;
  padding: 1rem 0; border-bottom: 1px solid var(--line-soft);
}
.defn__senses > li:last-child { border-bottom: 0; padding-bottom: 0.2rem; }
.defn__num { grid-row: 1 / span 3; font-family: var(--font-mono); font-size: var(--step-0); color: var(--accent); line-height: 1.5; }
.defn__tag { grid-column: 2; font-family: var(--font-mono); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--bone-mute); margin-bottom: 0.3rem; }
.defn__gloss { grid-column: 2; font-size: var(--step-0); line-height: 1.55; }
.defn__eg { grid-column: 2; font-family: var(--font-display); font-style: italic; font-size: var(--step-0); color: var(--bone-dim); margin-top: 0.45rem; }
.defn__note { margin-top: clamp(1.4rem, 3vw, 2.2rem); max-width: 68ch; color: var(--bone-dim); line-height: 1.6; }

/* ---- Client words --------------------------------------------------------
   A hairline grid on the line colour, same idiom as .steps, rather than three
   more rounded cards. The quote sits in the display italic the site already
   uses for pull quotes (.prose blockquote, .defn__eg), so it reads as someone
   else speaking without needing a giant quote mark to announce it.

   No Review or AggregateRating markup anywhere near this. Google treats
   reviews about your own business, shown on your own site, as self-serving
   and ineligible for the star feature, so the schema would be all risk and
   no upside.

   Appended at the end on purpose: new class names, nothing above them to
   lose a specificity tie to.
   ---------------------------------------------------------------------- */
.says { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--line); border-block: 1px solid var(--line); }
.say { background: var(--bone); padding: clamp(1.4rem, 3vw, 2.2rem); display: flex; flex-direction: column; gap: clamp(1.1rem, 2.5vw, 1.6rem); container-type: inline-size; }
.say blockquote { margin: 0; font-family: var(--font-display); font-style: italic; font-size: clamp(1.15rem, 7cqi, 1.75rem); line-height: 1.32; letter-spacing: -0.01em; text-wrap: pretty; }
.say__who { margin-top: auto; font-family: var(--font-mono); font-size: var(--step--1); letter-spacing: 0.08em; display: flex; flex-direction: column; gap: 0.25rem; }
.say__who::before { content: ""; width: 1.8em; height: 3px; border-radius: 2px; background: var(--spectrum); margin-bottom: 0.5rem; }
.say__who b { font-weight: 400; text-transform: uppercase; }
.say__who span { color: var(--bone-dim); }
@media (max-width: 960px) { .says { grid-template-columns: 1fr; } }

/* ---- Free website check -------------------------------------------------
   A results page more than a form: the input is one line and then gets out of
   the way. Traffic-lit the same three ways the NCT report is, so the free
   sample and the paid thing speak the same language. */
.wcheck { display: flex; flex-wrap: wrap; gap: 0.8rem; align-items: center; }
.wcheck__input {
  flex: 1 1 22ch; min-width: 0;
  font-family: var(--font-mono); font-size: var(--step-0);
  padding: 0.85em 1.1em; border: 1px solid var(--line); border-radius: 100px;
  background: var(--bone-2); color: var(--ink);
}
.wcheck__input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-color: var(--ink); }
.wcheck__go { flex: 0 0 auto; }
.wcheck__out { margin-top: clamp(2rem, 4vw, 3rem); }
.wcheck__wait, .wcheck__err { font-family: var(--font-mono); font-size: var(--step--1); color: var(--bone-dim); }
.wcheck__err { color: var(--c-red-ink); }

.wcheck__head { padding-bottom: clamp(1.4rem, 3vw, 2rem); border-bottom: 1px solid var(--line-soft); }
.wcheck__score { margin-top: 0.4rem; }
.wcheck__tally { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1.1rem 0 0.9rem; }
.wcheck__pill {
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.08em;
  padding: 0.4em 0.9em; border-radius: 100px; border: 1px solid var(--line);
}
.wcheck__pill.is-fail { color: var(--c-red-ink); border-color: var(--c-red-ink); }
.wcheck__pill.is-warn { color: var(--c-orange-ink); border-color: var(--c-orange-ink); }
.wcheck__pill.is-pass { color: var(--c-teal-ink); border-color: var(--c-teal-ink); }

.wcheck__area { margin-top: clamp(2rem, 4vw, 2.8rem); }
.wcheck__list { margin-top: 1.1rem; display: grid; gap: 0.9rem; }
.wcheck__row { display: flex; gap: 0.9rem; align-items: flex-start; }
.wcheck__dot { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%; margin-top: 0.45em; background: var(--bone-3); }
.wcheck__row.is-fail .wcheck__dot { background: var(--c-red-ink); }
.wcheck__row.is-warn .wcheck__dot { background: var(--c-orange-ink); }
.wcheck__row.is-pass .wcheck__dot { background: var(--c-teal-ink); }
.wcheck__label { font-weight: 500; }
.wcheck__verdict {
  font-family: var(--font-mono); font-size: var(--step--2, 0.74rem);
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--bone-dim); margin-left: 0.5em;
}
.wcheck__row.is-fail .wcheck__verdict { color: var(--c-red-ink); }
.wcheck__row.is-warn .wcheck__verdict { color: var(--c-orange-ink); }
.wcheck__detail { color: var(--bone-dim); margin-top: 0.15rem; max-width: 62ch; }
.wcheck__foot { margin-top: clamp(2rem, 4vw, 2.8rem); padding-top: 1.4rem; border-top: 1px solid var(--line-soft); max-width: 62ch; }
@media (max-width: 560px) { .wcheck__go { width: 100%; justify-content: center; } }

/* The privacy note on the free check. Closed by default because most people
   will not care, prominent enough to find because the ones who do care are
   exactly the ones deciding whether to trust us with their address. */
.wcheck__privacy { margin-top: 1rem; border-top: 1px solid var(--line-soft); padding-top: 1rem; max-width: 66ch; }
.wcheck__privacy > summary {
  cursor: pointer; list-style: none;
  font-family: var(--font-mono); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--bone-dim);
}
.wcheck__privacy > summary::-webkit-details-marker { display: none; }
.wcheck__privacy > summary::before { content: "+"; color: var(--accent); font-weight: 700; margin-right: 0.55em; }
.wcheck__privacy[open] > summary::before { content: "\2212"; }
.wcheck__privacy > summary:hover { color: var(--ink); }
.wcheck__privacy > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
.wcheck__privacy > div { margin-top: 0.9rem; display: grid; gap: 0.7rem; }
.wcheck__privacy p { font-size: var(--step--1); color: var(--bone-dim); }
.wcheck__privacy strong { color: var(--ink); }
.wcheck__privacy code { font-family: var(--font-mono); font-size: 0.92em; }

/* The written summary at the top of a check. Arrives a couple of seconds
   after the findings, so it is styled to look like it belongs there rather
   than like something that popped in. */
.wcheck__note { margin-top: 1.4rem; padding: 1.1rem 1.3rem; background: var(--bone-2); border: 1px solid var(--line-soft); border-radius: 14px; max-width: 64ch; }
.wcheck__note-kicker { font-family: var(--font-mono); font-size: var(--step--2, 0.74rem); text-transform: uppercase; letter-spacing: 0.1em; color: var(--bone-dim); margin-bottom: 0.5rem; }
.wcheck__note p + p { margin-top: 0.7rem; }
.wcheck__note-wait { font-family: var(--font-mono); font-size: var(--step--1); color: var(--bone-dim); }

/* ==========================================================================
   "Long read. Summarise it for me"
   --------------------------------------------------------------------------
   A quiet line under the page head, on the handful of pages long enough to
   justify it. Deliberately not a badge, a robot, or anything that announces
   itself as AI before it has done anything useful: it reads as one more
   control on the page until it is pressed, and it costs nothing to ignore.

   It renders hidden and main.js unhides it, so a browser with no JavaScript
   never sees a button that cannot do anything.
   ========================================================================== */
.tldr { margin-top: clamp(1.4rem, 4vw, 2.4rem); border-top: 1px solid var(--line); }
.tldr[hidden] { display: none; }

.tldr__btn {
  width: 100%; display: flex; align-items: center; gap: 0.75em;
  padding: 0.95rem 0; background: none; border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: var(--step--1);
  color: var(--bone-dim); text-align: left;
  transition: color 0.3s var(--ease);
}
.tldr__btn:hover { color: var(--ink); }
.tldr__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
.tldr__text { flex: 1 1 auto; }
/* No opacity here. Fading --bone-dim at this size takes it under 4.5:1
   against the page, which axe catches and a tired reader feels. */
.tldr__len { flex: none; font-size: var(--step--2); }
/* The word count is the justification for the button, not information anyone
   needs, so it is the first thing to go when there is no room for it. */
@media (max-width: 560px) { .tldr__len { display: none; } }

/* A plus that becomes a minus. Drawn rather than an icon file: two rules and
   no request. */
.tldr__mark {
  flex: none; position: relative; width: 1.6em; height: 1.6em; border-radius: 50%;
  border: 1px solid var(--line); transition: border-color 0.3s var(--ease);
}
.tldr__mark::before,
.tldr__mark::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 0.55em; height: 1px; background: currentColor;
  transform: translate(-50%, -50%);
}
.tldr__mark::after { transform: translate(-50%, -50%) rotate(90deg); transition: transform 0.4s var(--ease), opacity 0.25s linear; }
.tldr__btn:hover .tldr__mark { border-color: var(--accent); }
.tldr__btn[aria-expanded="true"] .tldr__mark::after { transform: translate(-50%, -50%) rotate(0deg); opacity: 0; }

.tldr__panel[hidden] { display: none; }
.tldr__body {
  margin-bottom: 1.5rem; padding: 1.2rem 1.4rem;
  background: var(--bone-2); border: 1px solid var(--line-soft); border-radius: 14px;
  max-width: 64ch;
}
.tldr__lead { font-family: var(--font-display); font-weight: 360; font-size: var(--step-1); line-height: 1.28; letter-spacing: -0.015em; }
.tldr__points { list-style: none; margin: 1.1rem 0 0; padding: 0; display: grid; gap: 0.65rem; }
.tldr__points li { position: relative; padding-left: 1.5rem; color: var(--bone-dim); font-size: var(--step--1); line-height: 1.55; }
.tldr__points li::before { content: ""; position: absolute; left: 0.2rem; top: 0.68em; width: 0.6rem; height: 1px; background: var(--accent); }
/* Said once, plainly, under the thing it applies to. A summary of a page is
   worth having; it is not the page, and pretending otherwise would be the
   exact behaviour this site spends four thousand words arguing against. */
.tldr__note { margin-top: 1.2rem; font-family: var(--font-mono); font-size: var(--step--2); color: var(--bone-dim); line-height: 1.5; }

/* Three lines of nothing while it thinks. Only ever seen by the first visitor
   to a page since it was last edited: everybody after that is served the
   answer off disk and the panel is filled before it has finished opening. */
.tldr__wait { display: grid; gap: 0.6rem; }
.tldr__wait span { display: block; height: 0.72rem; border-radius: 4px; background: var(--line); opacity: 0.55; animation: tldrpulse 1.4s ease-in-out infinite; }
.tldr__wait span:nth-child(1) { width: 92%; }
.tldr__wait span:nth-child(2) { width: 78%; animation-delay: 0.18s; }
.tldr__wait span:nth-child(3) { width: 85%; animation-delay: 0.36s; }
.tldr__waitmsg { font-family: var(--font-mono); font-size: var(--step--2); color: var(--bone-dim); margin-top: 0.4rem; }
@keyframes tldrpulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.75; } }
html[data-motion="reduce"] .tldr__wait span { animation: none; }
html[data-motion="reduce"] .tldr__mark::after { transition: none; }
.tldr__cta { display: inline-block; margin-top: 1.3rem; }


/* ==========================================================================
   Services as a grid, on the homepage only
   --------------------------------------------------------------------------
   Five full-width rows ran to 1,650px: a sixth of the entire homepage spent
   on five links, and the single biggest block on the page. As cards they sit
   in two rows at about a third of the height, and every word of the copy
   still fits.

   Scoped to the modifier rather than changed on .svc itself, because the same
   component is used on improve.html and services.html, where the page is
   short enough that the full-width rows are the better read. Below 680px it
   stays exactly as it was: one column, the original three-column row inside
   each item, nothing to re-learn on a phone.
   ========================================================================== */
@media (min-width: 680px) {
  .svc--grid { display: grid; grid-template-columns: 1fr 1fr; column-gap: clamp(1.5rem, 3vw, 3rem); border-top: none; }
  .svc--grid .svc__row {
    display: flex; flex-direction: column; align-items: flex-start; gap: 0.45rem;
    border-top: 1px solid var(--line); border-bottom: none;
    padding: clamp(1.3rem, 3vw, 1.9rem) clamp(0.9rem, 2vw, 1.3rem);
    /* The hover fill is a ::before at z-index -1; isolating the card keeps it
       behind this card's own text and in front of nothing else. */
    isolation: isolate;
  }
  /* The rows grew their inline padding on hover. In a grid that nudges the
     text sideways inside a fixed cell, so the fill alone does the work. */
  .svc--grid .svc__row:hover { padding-inline: clamp(0.9rem, 2vw, 1.3rem); }
  .svc--grid .svc__name { font-size: var(--step-1); }
  .svc--grid .svc__desc { max-width: none; font-size: var(--step--1); line-height: 1.55; }
  .svc--grid .svc__arrow {
    position: absolute; font-size: 1.1rem;
    top: clamp(1.3rem, 3vw, 1.9rem); right: clamp(0.9rem, 2vw, 1.3rem);
  }
  .svc--grid .svc__row:hover .svc__arrow { transform: translateX(0.3em) rotate(-45deg); }
}
@media (min-width: 1040px) { .svc--grid { grid-template-columns: repeat(3, 1fr); } }
html[data-motion="reduce"] .svc--grid .svc__row,
html[data-motion="reduce"] .svc--grid .svc__arrow { transition: none; }
