/* ==========================================================================
   Case studies — the shared sheet.

   One file, every case study. This is deliberately NOT a fourth copy of
   index.html's inline <style>: the root pages each own their own block for
   historical reasons (see PORTFOLIO_ARCHITECTURE.md §10), and the fix for
   that is not to add a fifth copy. Everything a /work/<project>/ page needs
   — tokens, nav, footer, and the case-study vocabulary itself — lives here
   once and is linked by every case study.

   Scope rules, so this stays safe to change:
     - Every case-study-specific class is prefixed `cs-`.
     - The chrome classes it shares with the root pages (.nav, .foot,
       .eyebrow, .sr-only, .reveal, .themeswitch host) are declared here
       because a case study is served standalone and links no other page's
       stylesheet. They are copies in the sense that they must MATCH the
       root pages, not extend them — if you change the nav on index.html,
       change it here too. scripts/check-site.py enforces that every case
       study carries the chrome; it cannot enforce that it looks identical.
     - NEO's overrides are NOT here. They live in themes/theme.css with
       every other NEO rule, exactly as the theme architecture requires.

   The reading ground is the theme's own ground — red for SIGNAL, warm
   off-white for NEO — because a case study that quietly switched to a
   neutral page would stop being part of this site. Body copy uses --muted,
   which was measured at 4.89:1 on SIGNAL's red in Phase 2.
   ========================================================================== */

:root {
  --bg:     #ff0000;
  --ink:    #000000;
  --accent: #ffffff;
  --muted:  rgba(0,0,0,0.86);
  --faint:  rgba(0,0,0,0.80);
  --line:   rgba(255,255,255,0.55);
  --edge:   clamp(1.25rem, 6vw, 6.5rem);
  --font:   "Bricolage Grotesque", system-ui, -apple-system, sans-serif;
  --mono:   ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  /* The reading measure. One number, used by every prose block, so the
     column never drifts section to section. */
  --measure: 66ch;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: var(--font); font-weight: 400;
  -webkit-font-smoothing: antialiased; overflow-x: hidden;
}
a { color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }
img { max-width: 100%; }
::selection { background: #fff; color: #000; }

.eyebrow {
  font-size: 0.68rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--faint); font-weight: 500; margin: 0;
}
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Skip link — a case study is long enough that tabbing past the nav on
   every visit is a real cost. */
.skip {
  position: absolute; left: var(--edge); top: 0; z-index: 100;
  transform: translateY(-140%);
  background: #fff; color: #000; padding: 0.7rem 1.1rem;
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.04em;
  transition: transform 0.2s ease;
}
.skip:focus-visible { transform: translateY(0.6rem); outline: 2px solid #000; outline-offset: 2px; }

/* ------------------------------------------------------------------ nav
   Matches index.html's nav. The homepage starts transparent and toggles
   `.is-stuck` on scroll; a case study has no hero field to be transparent
   over, so it declares `.nav--solid` and is opaque from the first pixel.
   NEO answers both states in themes/theme.css. */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 60;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.3rem var(--edge);
  background: rgba(255,0,0,0); border-bottom: 1px solid transparent;
}
.nav--solid, .nav.is-stuck {
  background: rgba(255,0,0,0.78);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom-color: var(--line);
}
.nav__mark { font-weight: 800; font-size: 1.05rem; letter-spacing: -0.02em; color: #000; }
.nav__mark i { color: var(--accent); font-style: normal; }
.nav__links { display: flex; align-items: center; gap: clamp(1.1rem, 4vw, 1.8rem); }
.nav__links a { font-size: 0.85rem; color: #000; position: relative; transition: color 0.2s ease; }
.nav__links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 0.3s cubic-bezier(.2,.8,.3,1);
}
.nav__links a:hover { color: var(--accent); }
.nav__links a:hover::after { transform: scaleX(1); }

/* ------------------------------------------------- reactive background
   The two hosts the theme controller mounts into. Without these rules the
   component's own `height: 100%` resolves against a body with no height,
   the container collapses to <page width> x 0, and the renderer draws a
   canvas two pixels tall — which is exactly what every case study and the
   404 were doing until Phase 14. SIGNAL's field was simply absent on five
   of the seven pages, while all five still downloaded 1.28 MB of WebGL,
   compiled the shaders and ran a render loop forever to paint it.

   Fixed, behind everything, and never interactive: the sections above sit
   on it at rgba(255,0,0,0.74-0.9), exactly as on the homepage. */
#pixelblast,
#neobg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* --------------------------------------------------------------- reveal */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.65s ease, transform 0.65s cubic-bezier(.2,.8,.3,1); }
.reveal.in { opacity: 1; transform: none; }

/* ==========================================================================
   Case study
   ========================================================================== */

.cs { position: relative; z-index: 2; }

/* ----------------------------------------------------------------- hero */
.cs-hero {
  position: relative;
  padding: clamp(7rem, 16vh, 10rem) var(--edge) clamp(2.5rem, 6vw, 4rem);
  background: rgba(255,0,0,0.74);
}
.cs-hero__inner { max-width: 1200px; margin: 0 auto; }
.cs-back {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  font-weight: 600; color: var(--faint); margin-bottom: clamp(1.6rem, 5vw, 2.4rem);
  transition: color 0.2s ease;
}
.cs-back:hover { color: var(--accent); }
.cs-hero h1 {
  margin: 0.6rem 0 0; font-weight: 700; line-height: 0.95;
  font-size: clamp(2.6rem, 10vw, 6rem); letter-spacing: -0.045em; color: #000;
}
.cs-hero h1 i { color: var(--accent); font-style: normal; }
.cs-standfirst {
  margin: clamp(1.3rem, 4vw, 1.9rem) 0 0; max-width: 46ch;
  font-size: clamp(1.05rem, 4vw, 1.35rem); line-height: 1.55; color: var(--muted);
}

/* The fact block. A definition list because that is what it is: labelled
   values, not a table and not a paragraph pretending to be one. */
.cs-meta {
  display: grid; gap: 1.4rem 2.4rem; margin: clamp(2.2rem, 6vw, 3.2rem) 0 0;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  padding-top: 1.6rem; border-top: 1px solid var(--line);
}
.cs-meta > div { min-width: 0; }
.cs-meta dt {
  font-size: 0.6rem; letter-spacing: 0.18em; text-transform: uppercase;
  font-weight: 600; color: var(--faint); margin: 0 0 0.45rem;
}
/* A URL in a fact list is one unbreakable token. At 1440 the column is
   wide enough to hold it and at 1024 the grid gives it its own row, but
   between those it overflowed the page — caught at exactly 1280. */
.cs-meta dd {
  margin: 0; font-size: 0.92rem; line-height: 1.5; color: #000;
  overflow-wrap: anywhere;
}
.cs-meta dd a { border-bottom: 1px solid var(--line); }
.cs-meta dd a:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* ------------------------------------------------------------- sections */
.cs-section {
  position: relative; z-index: 2;
  padding: clamp(3rem, 7.5vh, 5rem) var(--edge);
  background: rgba(255,0,0,0.74);

  /* NOT content-visibility: auto — tested in Phase 14 and reverted.

     It does what it promises: the browser skips layout and paint for
     sections outside the viewport, which on a 17,100 px case study is most
     of them. It also broke two things these pages depend on, measurably:

       scrollHeight   10,908 px on load, 17,100 px after scrolling to the
                      bottom — the scrollbar changes size under the reader
       section index  all 10 anchors landed 300-1,900 px away from their
                      heading instead of at it

     The cause is contain-intrinsic-size: the browser needs an estimate for
     every section it has not rendered yet, and one number cannot be right
     for a section that is 400 px tall at 1440 and 1,400 px tall at 390.
     Per-section measured values would fix the anchors at exactly one
     viewport width and go stale the first time the copy changed.

     The work it would have saved is real but it is paint on content nobody
     is looking at; the navigation it broke is something people use. */
}
.cs-section--tint { background: rgba(255,0,0,0.86); }
.cs-section__inner { max-width: 1200px; margin: 0 auto; }
.cs-section__head {
  display: flex; align-items: baseline; gap: 1rem;
  padding-bottom: 1.1rem; border-bottom: 1px solid var(--line);
  margin-bottom: clamp(1.8rem, 5vw, 2.6rem);
}
.cs-section__num {
  font-family: var(--mono); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.12em; color: var(--accent); flex: 0 0 auto;
}
.cs-section__head h2 {
  margin: 0; font-weight: 700; line-height: 1;
  font-size: clamp(1.6rem, 5.4vw, 2.5rem); letter-spacing: -0.035em; color: #000;
}
.cs-section__head h2 i { color: var(--accent); font-style: normal; }

/* A jump from the section index lands the heading under the fixed nav
   unless it reserves the space itself. */
.cs-section__head h2, .cs-hero h1, #cs-main { scroll-margin-top: 5.5rem; }

/* ---------------------------------------------------------------- prose */
.cs-prose { max-width: var(--measure); }
/* Paragraph separation has to beat the line gap or the column reads as one
   block. Body line-height is 1.72 (~28px at the clamped size), so the gap
   between blocks is set above that in absolute terms, not in em. */
.cs-prose > * + * { margin-top: 1.9rem; }
.cs-prose p { margin: 0; font-size: clamp(0.97rem, 3.6vw, 1.03rem); line-height: 1.72; color: var(--muted); }
.cs-prose h3 {
  margin: 2.6rem 0 0; font-size: clamp(1.05rem, 4vw, 1.2rem); font-weight: 700;
  letter-spacing: -0.02em; color: #000; line-height: 1.3;
}
.cs-prose h3 + p { margin-top: 0.75rem; }
.cs-prose strong { font-weight: 700; color: #000; }
.cs-prose em { font-style: italic; }
.cs-prose a { color: #000; border-bottom: 1px solid var(--line); transition: color 0.2s ease, border-color 0.2s ease; }
.cs-prose a:hover { color: var(--accent); border-bottom-color: var(--accent); }
.cs-prose ul, .cs-prose ol { margin: 0; padding-left: 1.2rem; }
.cs-prose li { font-size: clamp(0.97rem, 3.6vw, 1.03rem); line-height: 1.7; color: var(--muted); margin-top: 0.45em; }
.cs-prose li::marker { color: var(--accent); }

/* An inline literal — a label from the interface, a filename, a token.
   Monospaced and outlined so a run of them in a paragraph reads as
   "these are things from the product", not as emphasis. */
.cs-kbd, .cs-prose code, .cs-figure figcaption code {
  font-family: var(--mono); font-size: 0.82em;
  letter-spacing: 0.02em; color: #000;
  border: 1px solid var(--line); border-radius: 2px;
  padding: 0.08em 0.36em;
  /* NOT `white-space: nowrap`. It is right for a short label like KM 11
     and catastrophic for a long one: a 60-character literal quoted from a
     page footer pushed the document 20px wider than the viewport between
     900px and 1100px, and nowhere else, because that is the only band
     where the column is narrow enough to matter and wide enough not to
     have already reflowed. Wrapping a literal across two lines looks
     slightly odd; a horizontal scrollbar is worse. */
  overflow-wrap: anywhere;
}

/* A first paragraph that carries the section on its own. */
.cs-lede {
  max-width: 34ch; margin: 0 0 clamp(1.6rem, 4vw, 2.2rem);
  font-size: clamp(1.25rem, 5vw, 1.9rem); font-weight: 400;
  line-height: 1.32; letter-spacing: -0.025em; color: #000;
}

/* --------------------------------------------------------------- figures */
.cs-figure { margin: clamp(2rem, 5vw, 3rem) 0 0; }
.cs-figure img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); background: rgba(0,0,0,0.18);
}
.cs-figure figcaption {
  margin-top: 0.8rem; font-size: 0.78rem; line-height: 1.55; color: var(--faint);
  max-width: 62ch;
}
.cs-figure figcaption b {
  display: inline; font-weight: 600; color: #000;
  letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.66rem;
  margin-right: 0.5rem;
}
.cs-figure--pair { display: grid; gap: clamp(0.8rem, 2vw, 1.2rem); }
@media (min-width: 820px) {
  .cs-figure--pair { grid-template-columns: 1fr 1fr; align-items: start; }
  .cs-figure--pair figcaption { grid-column: 1 / -1; }
}
/* A phone-shaped shot next to its own explanation rather than blown up to
   the width of a desktop screenshot. */
.cs-figure--aside { display: grid; gap: clamp(1.2rem, 3vw, 2.4rem); align-items: center; }
@media (min-width: 820px) {
  .cs-figure--aside { grid-template-columns: minmax(0, 260px) minmax(0, 1fr); }
  .cs-figure--aside figcaption { margin-top: 0; }
}

/* A source/confidence line under a figure or a stat block. Small on
   purpose: it must be readable and must not compete. */
.cs-note {
  margin: 0.9rem 0 0;
  font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase;
  font-weight: 500; color: var(--faint);
}

/* ----------------------------------------------------------------- stats */
.cs-stats {
  display: grid; gap: 1px; margin: clamp(2rem, 5vw, 2.8rem) 0 0;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  background: var(--line); border: 1px solid var(--line);
}
.cs-stat { background: rgba(255,0,0,0.92); padding: clamp(1.1rem, 3vw, 1.6rem); }
.cs-stat b {
  display: block; font-size: clamp(1.7rem, 6vw, 2.6rem); font-weight: 700;
  letter-spacing: -0.04em; line-height: 1; color: #000;
}
.cs-stat b s { text-decoration: none; font-size: 0.5em; vertical-align: super; color: var(--accent); }
.cs-stat span {
  display: block; margin-top: 0.6rem;
  font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase;
  font-weight: 600; color: var(--faint);
}
.cs-stat p { margin: 0.55rem 0 0; font-size: 0.82rem; line-height: 1.55; color: var(--muted); }

/* ------------------------------------------------------- numbered blocks
   Decisions, systems, IA layers — the same shape each time: a number, a
   title, a short explanation. */
.cs-items { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); }
.cs-items > li { background: rgba(255,0,0,0.92); padding: clamp(1.2rem, 3.4vw, 1.9rem); }
.cs-items h3 {
  margin: 0.7rem 0 0; font-size: clamp(1.02rem, 4vw, 1.18rem); font-weight: 700;
  letter-spacing: -0.02em; color: #000; line-height: 1.25;
}
.cs-items p { margin: 0.55rem 0 0; font-size: 0.9rem; line-height: 1.65; color: var(--muted); max-width: 52ch; }
.cs-items__num { font-family: var(--mono); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.12em; color: var(--accent); }
@media (min-width: 820px) { .cs-items--2 { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1040px) { .cs-items--3 { grid-template-columns: repeat(3, 1fr); } }

/* --------------------------------------------------------------- callout
   Constraints, caveats, "what this is not". Marked, not hidden. */
.cs-callout {
  margin: clamp(2rem, 5vw, 2.8rem) 0 0; max-width: var(--measure);
  border: 1px solid var(--line); border-left-width: 4px;
  padding: clamp(1.1rem, 3vw, 1.5rem);
  /* Tinted with white, not black. A black tint reads as "set back" and is
     the obvious choice, but it darkens the ground under black text: at
     rgba(0,0,0,0.06) it took --muted from 4.89:1 to 4.41:1 and --faint to
     4.18:1, both under WCAG AA, which is exactly the failure Phase 2
     removed from the rest of the site. Lifting the panel instead keeps
     both tokens above AA (4.91:1 / 4.61:1) and still separates it. */
  background: rgba(255,255,255,0.06);
}
.cs-callout p:first-child { margin-top: 0; }
.cs-callout p { margin: 0.8rem 0 0; font-size: 0.9rem; line-height: 1.68; color: var(--muted); }
.cs-callout b {
  display: block; margin-bottom: 0.5rem;
  font-size: 0.62rem; letter-spacing: 0.18em; text-transform: uppercase;
  font-weight: 700; color: #000;
}

/* ----------------------------------------------------------------- quote */
.cs-quote { margin: clamp(2.4rem, 6vw, 3.4rem) 0 0; max-width: 30ch; }
.cs-quote blockquote {
  margin: 0; font-size: clamp(1.25rem, 5vw, 1.85rem); line-height: 1.34;
  letter-spacing: -0.025em; color: #000; font-weight: 400;
}
.cs-quote cite { display: block; margin-top: 1rem; font-style: normal; font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint); }

/* ------------------------------------------------------------ section index
   A case study long enough to need one gets a flat row of links in the
   hero rather than a sticky sidebar — it costs no layout, reads as part of
   the fact block above it, and does not follow the reader down the page
   competing with the content. */
.cs-index {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.4rem;
  margin: clamp(1.6rem, 4vw, 2.2rem) 0 0;
  padding-top: 1.4rem; border-top: 1px solid var(--line);
  list-style: none;
}
.cs-index li { margin: 0; }
.cs-index a {
  font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase;
  font-weight: 600; color: var(--faint);
  position: relative; padding-bottom: 3px; transition: color 0.2s ease;
}
.cs-index a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 0.3s cubic-bezier(.2,.8,.3,1);
}
.cs-index a:hover { color: var(--accent); }
.cs-index a:hover::after { transform: scaleX(1); }
.cs-index b { font-family: var(--mono); font-weight: 700; margin-right: 0.45rem; color: var(--accent); }

/* ---------------------------------------------------------------- the flow
   Question on the left, the page that answers it on the right. Used for
   information architecture, where the honest shape of the thing is "these
   are the questions, in this order" rather than a box-and-arrow tree. */
.cs-flow { list-style: none; margin: clamp(1.8rem, 5vw, 2.4rem) 0 0; padding: 0; }
.cs-flow > li {
  display: grid; gap: 0.4rem 2rem; align-items: baseline;
  padding: clamp(1rem, 2.6vw, 1.3rem) 0;
  border-top: 1px solid var(--line);
}
.cs-flow > li:last-child { border-bottom: 1px solid var(--line); }
@media (min-width: 760px) {
  .cs-flow > li { grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); }
}
.cs-flow q {
  quotes: none; font-size: clamp(1.02rem, 4vw, 1.25rem); line-height: 1.32;
  letter-spacing: -0.02em; color: #000;
}
.cs-flow div { min-width: 0; }
.cs-flow code {
  display: inline-block; font-family: var(--mono); font-size: 0.72rem;
  letter-spacing: 0.04em; color: #000;
  border: 1px solid var(--line); padding: 0.15rem 0.45rem; margin-bottom: 0.5rem;
}
.cs-flow p { margin: 0; font-size: 0.88rem; line-height: 1.6; color: var(--muted); }

/* ==========================================================================
   Evidence

   A gallery of real artefacts, each with a label that says what it is and
   when. The rule the whole component exists to enforce: nothing goes in
   here that was made to illustrate a point. Every image is a file that
   already existed, screenshotted as it is.
   ========================================================================== */
.cs-evid {
  list-style: none; margin: clamp(1.8rem, 5vw, 2.6rem) 0 0; padding: 0;
  display: grid; gap: clamp(0.9rem, 2.4vw, 1.4rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
}
.cs-evid > li { min-width: 0; }
/* The trigger is a real button: it is reachable by keyboard, it activates
   on Enter and Space for free, and it announces itself as a control rather
   than as an image that happens to be clickable. */
.cs-evid__open {
  display: block; width: 100%; padding: 0; margin: 0; text-align: left;
  background: none; border: 0; color: inherit; font: inherit; cursor: zoom-in;
}
.cs-evid__shot {
  display: block; position: relative; overflow: hidden;
  aspect-ratio: 16 / 10; border: 1px solid var(--line); background: rgba(0,0,0,0.18);
}
.cs-evid__shot img {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center;
  transition: transform 0.6s cubic-bezier(.19,1,.22,1);
}
@media (hover: hover) {
  .cs-evid__open:hover .cs-evid__shot img { transform: scale(1.04); }
  .cs-evid__open:hover .cs-evid__ver { background: #fff; }
}
.cs-evid__open:focus-visible { outline: 2px solid #000; outline-offset: 3px; }
.cs-evid__ver {
  position: absolute; top: 0; left: 0; z-index: 1;
  font-family: var(--mono); font-size: 0.64rem; font-weight: 700; letter-spacing: 0.1em;
  background: rgba(255,255,255,0.9); color: #000; padding: 0.25rem 0.5rem;
  transition: background 0.3s ease;
}
.cs-evid__label {
  display: block; margin-top: 0.7rem;
  font-size: 0.86rem; line-height: 1.4; font-weight: 700; color: #000;
}
.cs-evid__note { display: block; margin-top: 0.35rem; font-size: 0.78rem; line-height: 1.5; color: var(--muted); }
.cs-evid__meta {
  display: block; margin-top: 0.5rem;
  font-size: 0.58rem; letter-spacing: 0.14em; text-transform: uppercase;
  font-weight: 600; color: var(--faint);
}

/* ---------------------------------------------------------------- lightbox */
.lb {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  gap: clamp(0.4rem, 2vw, 1.4rem);
  padding: clamp(3.5rem, 8vh, 5rem) clamp(0.8rem, 4vw, 3rem);
  background: rgba(0,0,0,0.92);
  opacity: 0; transition: opacity 0.18s ease;
}
.lb.is-open { opacity: 1; }
.lb[hidden] { display: none; }
.lb__figure { margin: 0; max-width: 100%; max-height: 100%; display: flex; flex-direction: column; min-width: 0; }
.lb__img {
  display: block; max-width: 100%; max-height: calc(100vh - 11rem);
  width: auto; height: auto; object-fit: contain; background: #111;
}
.lb__cap {
  margin-top: 0.9rem; font-size: 0.82rem; line-height: 1.55;
  color: rgba(255,255,255,0.86); max-width: 70ch;
}
.lb__close, .lb__nav {
  flex: 0 0 auto; background: none; border: 1px solid rgba(255,255,255,0.4);
  color: #fff; cursor: pointer; line-height: 1;
  display: grid; place-items: center;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.lb__close {
  position: absolute; top: clamp(0.8rem, 3vw, 1.6rem); right: clamp(0.8rem, 3vw, 1.6rem);
  width: 44px; height: 44px; font-size: 1.5rem;
}
.lb__nav { width: 44px; height: 64px; font-size: 1.6rem; }
.lb__close:hover, .lb__nav:hover,
.lb__close:focus-visible, .lb__nav:focus-visible { background: #fff; color: #000; border-color: #fff; }
.lb__close:focus-visible, .lb__nav:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
@media (max-width: 620px) {
  .lb__nav { position: absolute; bottom: clamp(0.8rem, 3vw, 1.6rem); }
  .lb__nav--prev { left: clamp(0.8rem, 3vw, 1.6rem); }
  .lb__nav--next { right: clamp(0.8rem, 3vw, 1.6rem); }
  .lb__img { max-height: calc(100vh - 15rem); }
}
@media (prefers-reduced-motion: reduce) {
  .lb { transition: none !important; }
  .cs-evid__shot img { transition: none !important; transform: none !important; }
}

/* ------------------------------------------------------------------- CTA */
.cs-cta {
  position: relative; z-index: 2;
  padding: clamp(3.4rem, 9vh, 6rem) var(--edge);
  background: rgba(255,0,0,0.9);
  border-top: 1px solid var(--line);
}
.cs-cta__inner { max-width: 1200px; margin: 0 auto; }
.cs-cta h2 {
  margin: 0.6rem 0 0; font-weight: 700; line-height: 1;
  font-size: clamp(1.9rem, 6.4vw, 2.9rem); letter-spacing: -0.035em; color: #000;
}
.cs-cta h2 i { color: var(--accent); font-style: normal; }
.cs-cta p { margin: 1rem 0 0; max-width: 48ch; font-size: 0.97rem; line-height: 1.7; color: var(--muted); }
.cs-actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: clamp(1.8rem, 5vw, 2.4rem); }
.cs-btn {
  display: inline-flex; align-items: center; gap: 0.8rem;
  padding: 0.95rem 1.4rem; font-size: 0.74rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase;
  border: 1px solid var(--line); color: #000;
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease, transform 0.3s cubic-bezier(.19,1,.22,1);
}
.cs-btn span { font-size: 1rem; line-height: 1; letter-spacing: 0; }
.cs-btn--primary { background: #fff; border-color: #fff; }
.cs-btn:hover { background: #000; color: #fff; border-color: #000; transform: translateY(-2px); }
.cs-btn:focus-visible { outline: 2px solid #000; outline-offset: 3px; }

/* ---------------------------------------------------------------- footer */
.foot {
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding: 2rem var(--edge) 2.4rem;
  background: rgba(255,0,0,0.9);
  border-top: 1px solid var(--line);
  font-size: 0.78rem; color: var(--faint);
}
.foot__nav { display: flex; align-items: center; gap: 1.4rem; flex-wrap: wrap; }
.foot__nav a, .foot__mail { color: var(--faint); position: relative; transition: color 0.2s ease; }
.foot__nav a::after, .foot__mail::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 0.3s cubic-bezier(.2,.8,.3,1);
}
.foot__nav a:hover, .foot__mail:hover { color: var(--accent); }
.foot__nav a:hover::after, .foot__mail:hover::after { transform: scaleX(1); }

/* ------------------------------------------------------------------ focus
   One visible focus ring for everything, never removed, only replaced.
   Black is 5.25:1 against SIGNAL's red and 18.59:1 against NEO's cream,
   so a single declaration clears WCAG 1.4.11's 3:1 minimum in both
   themes. (Chrome's default ring is 1.50:1 on this red — measured.) */
a:focus-visible, button:focus-visible {
  outline: 2px solid #000; outline-offset: 3px;
}

/* WCAG 2.5.8 Target Size (Minimum, AA): 24x24 CSS px. The nav, footer,
   section index and back link all set type at 14-19px tall. The hit area
   is expanded with a pseudo-element rather than padding, because padding
   would move the ::after underline these already draw and change the
   layout. ::before is invisible and costs no layout. */
.nav__links a, .foot__nav a, .foot__mail,
.cs-back, .cs-index a, .cs-meta dd a, .cs-prose a { position: relative; }
.nav__links a::before, .foot__nav a::before, .foot__mail::before,
.cs-back::before, .cs-index a::before, .cs-meta dd a::before {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; height: 24px; transform: translateY(-50%);
}

/* --------------------------------------------------------- reduced motion */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .cs-btn, .cs-back, .nav__links a, .skip { transition: none !important; }
  .cs-btn:hover { transform: none !important; }
}
/* The no-JS fallback is a <noscript><style> block in the page itself, the
   same way index.html and about.html do it — a class would need JS to
   remove, which is exactly what is missing in that case. */
