/* front.css - version three of the front page: the cinema grid.
 *
 * Modelled on how the video labs present themselves, and specifically on three things worth
 * taking from them:
 *
 *   Runway    a dark editorial page where full-bleed footage replaces whitespace, one
 *             geometric sans at one weight, line-height 1.0 with negative tracking so a
 *             headline reads as a film title, no gradients, no shadows, and depth from
 *             alternating dark and light sections rather than from CSS.
 *   Sora      the prompt box as the hero's primitive: the thing you came to do, in the
 *             first screen, before any argument.
 *   Captions  proof before features - one input shown as several outputs - and capability
 *             lines of one sentence rather than paragraphs.
 *
 * The palette is monochrome by necessity rather than fashion: every frame in the grid is
 * someone else's footage, and a page with its own hue fights it. Two greys and two grounds.
 *
 * One exception, added deliberately: a single blue, and it is spent only on the primary
 * action. The reason is that white was doing two jobs at once — it was the headline and it
 * was the button — so the two competed for the same first look and the button lost. Giving
 * the action its own hue settles that without putting colour anywhere near the footage.
 * Nothing decorative is blue. If a new blue thing is not clickable, it is a mistake.
 *
 * ── two themes ──────────────────────────────────────────────────────────────
 *
 * The page is black by default and there is a light version behind a toggle. Dark is the
 * default rather than the system's preference, and that is a decision: this page is a
 * cinema, every frame on it is a film, and a visitor who has expressed no opinion should
 * see the room the work was graded for. An explicit choice is remembered and beats
 * everything; `prefers-color-scheme` is deliberately not consulted, because a page that
 * opens white on a Mac at noon and black on the same Mac at six is not one page.
 *
 * The tokens are named for the job rather than for the colour, which is the whole reason
 * the swap is a dozen lines instead of a second stylesheet. `--ground` is whatever the page
 * sits on and `--fg` is whatever is written on it — in the dark theme those are black and
 * white, and in the light one they are the other way round. The two names that still say a
 * colour, `--cloud` and `--ink`, mean it: they are the alternating band's own pair and it
 * stays a pale band with dark type in both themes, because a section that inverts along
 * with the page is a section that has stopped alternating.
 *
 * `--invert-*` is the third pair, and it exists because a solid button is the one thing that
 * is neither: it is the page's colours swapped over, so it reads as filled in both themes.
 */

:root {
  --ground: #000000;
  --surface: #0f0f10;
  --border: #27272a;
  --fg: #ffffff;
  --slate: #767d88;
  --muted: #a7a7a7;
  --cloud: #e9ecf2;
  --ink: #18181b;
  --ink-soft: #52525b;
  --line-light: #c9ccd1;

  /* A filled button: the page's own pair, swapped. */
  --invert-bg: #ffffff;
  --invert-fg: #000000;
  --invert-bg-hover: #e9ecf2;

  /* Three steps of one hue, each with the contrast its job needs.
     --blue      large shapes and marks on black
     --blue-text small text on black, 7.0:1
     --blue-deep button ground under white text, 6.3:1 — the fill, because #4f7cff
                 under white type is 3.6:1 and fails at button sizes */
  --blue: #4f7cff;
  --blue-text: #8fb0ff;
  --blue-deep: #1d4ed8;
  --blue-deep-hover: #1e40af;

  color-scheme: dark;
}

/* The light theme. Only the tokens change; not one rule below this block knows there are
   two themes, which is the property that has to survive every future edit.

   The greys are not the dark values inverted arithmetically — that produces a page whose
   secondary text is too faint to read. Each one is chosen for its contrast against the new
   ground: --muted is 8.9:1, --slate 5.4:1, both against white. And --blue-text moves the
   other way from every other token: a tint that reads at 7:1 on black is 2.1:1 on white, so
   the light theme uses the deep step for small blue text instead. */
:root[data-theme="light"] {
  --ground: #ffffff;
  --surface: #f6f7f9;
  --border: #e2e5ea;
  --fg: #101114;
  --slate: #646b76;
  --muted: #43494f;
  --line-light: #d6d9df;

  --invert-bg: #101114;
  --invert-fg: #ffffff;
  --invert-bg-hover: #2b2f36;

  --blue-text: #1d4ed8;

  color-scheme: light;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--fg);
  font-family: "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* One family, one weight for display type, tight leading, negative tracking. */
h1, h2, h3 { font-weight: 400; margin: 0; letter-spacing: -0.03em; line-height: 1; }
h1 { font-size: clamp(2.6rem, 6.4vw, 4.6rem); }
h2 { font-size: clamp(1.9rem, 3.6vw, 2.6rem); }
h3 { font-size: 1.25rem; }
p { margin: 0; }

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

.wrap { width: min(1600px, 92vw); margin: 0 auto; }
.wrap-narrow { width: min(1100px, 92vw); margin: 0 auto; }

.label {
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.035em;
  text-transform: uppercase;
  color: var(--slate);
}
/* The hero's opening line is the one non-interactive place the accent is allowed: it reads
   as a signature over the headline. Every other .label on the page stays grey. */
.hero .label { color: var(--blue-text); }

.mono {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
}

.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;
}

:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.on-light :focus-visible { outline-color: var(--ink); }

/* ── nav: transparent, over the hero, and it scrolls away with it ─────────── */
.nav {
  position: absolute;
  inset: 0 0 auto;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 2rem;
  padding: 1.4rem 4vw;
}
.brand { display: inline-flex; align-items: center; gap: 0.6rem; font-size: 1.0625rem; letter-spacing: -0.02em; }
.brand-mark {
  width: 22px; height: 22px; border-radius: 4px;
  background: var(--blue); color: #ffffff;
  display: grid; place-items: center; font-size: 0.6rem; padding-left: 2px;
}
.nav-links { display: flex; gap: 1.6rem; margin-left: auto; font-size: 0.9375rem; color: var(--muted); }
.nav-links a:hover { color: var(--blue-text); }
.nav-cta { display: flex; align-items: center; gap: 0.9rem; font-size: 0.9375rem; }
.nav-cta .ghost { color: var(--muted); }
.nav-cta .ghost:hover { color: var(--blue-text); }
@media (max-width: 820px) { .nav-links { display: none; } }

/* ── buttons: 4px radius, no shadow, deliberately quiet ──────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font: inherit; font-size: 0.875rem; font-weight: 500;
  padding: 0.6rem 1.05rem; border-radius: 4px; border: 1px solid transparent;
  cursor: pointer; white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
/* The primary action, and the reason the palette gained a colour at all. */
.btn-light { background: var(--blue-deep); color: #ffffff; }
.btn-light:hover { background: var(--blue-deep-hover); }
.btn-line { border-color: var(--border); color: var(--fg); }
.btn-line:hover { border-color: var(--fg); }

/* ── hero: black ground, the argument left, three of our own frames right ──
 *
 * Two earlier shapes are recorded in index.html: a YouTube background (leaks the player's
 * chrome) and full-bleed stills (collide with the films' burned-in subtitles). Plates beside
 * the words keep the media and lose both problems.
 */
.hero {
  border-bottom: 1px solid var(--border);
  padding: 8.5rem 0 5.5rem;
}
/* Two columns, and the copy is centred against the film now rather than pinned to the top.
   It was pinned because three stacked plates were twice the height of the words beside them;
   with one film the two columns are close enough that centring is what reads as deliberate. */
.hero-grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 4.5rem;
  align-items: center;
}
.hero-inner { min-width: 0; }
.hero h1 { max-width: 15ch; margin: 1rem 0 1.3rem; }
.hero-sub { color: var(--muted); font-size: 1.125rem; max-width: 46ch; line-height: 1.55; }
.hero-note { color: var(--slate); font-size: 0.875rem; margin-top: 1.4rem; max-width: 56ch; }

/* The other two ways in. Buttons rather than a sentence with a link in it: "no website yet"
   is the commonest reason somebody closes this tab, and the answer to it was set in the
   smallest grey on the page, inside a paragraph, which is where an answer goes to be missed.
   Outlined rather than filled, because there is one primary action above them and a first
   screen with three equal buttons has none. */
.hero-ways {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 0.6rem; margin-top: 1.3rem;
}
.hero-ways-or { color: var(--slate); font-size: 0.875rem; margin-right: 0.2rem; }
.hero-ways .btn { font-size: 0.9375rem; padding: 0.7rem 1.15rem; }

/* One film, and the label above it doing the work the three plates used to do: saying what
   this is before anybody presses it. Modelled on entrops.ai, where a small pill over the
   frame ("vidéo explicative · lecture au clic") removes every question a bare poster raises.
   Ours says something different, because we are not showing an explainer about us - we are
   showing a film this product made, which is the better proof and the only honest one. */
.hero-film { margin: 0; min-width: 0; }
.film-label {
  display: inline-flex; align-items: center; gap: 0.5rem;
  margin-bottom: 0.8rem; padding: 0.4rem 0.85rem;
  border: 1px solid var(--border); border-radius: 999px;
  color: var(--muted); font-size: 0.8125rem;
}
.film-glyph { color: var(--blue-text); font-size: 0.6rem; }
/* Under the caption, quieter than it. It answers the one question showing a customer's film
   instead of our own raises, and the answer happens to be the strongest claim on the page —
   so it gets to be a sentence rather than nothing at all, and no more than a sentence. */
.film-note { margin-top: 0.5rem; color: var(--slate); font-size: 0.875rem; }
/* Grey until hovered: the page is monochrome, and the colour arriving is the reward for
   reaching for the film rather than decoration applied to it from the start.

   Not cropped, deliberately. Two attempts at hiding the burned-in French subtitles failed:
   object-position cannot trim a 16:9 poster inside a 16:9 frame, and zooming past them cut
   the sentence in half at the sides, which reads as broken rather than as a crop. This is a
   real frame of a real film; the caption under it names the language, so French type on
   screen is the point instead of a mistake. The grade keeps it quiet. */
.hero-film .shot img {
  filter: grayscale(1) contrast(1.02) brightness(0.82);
  transition: filter 0.35s ease;
}
.hero-film:hover .shot img { filter: none; }
@media (max-width: 980px) {
  .hero { padding: 6.5rem 0 3.5rem; }
  .hero-grid { grid-template-columns: 1fr; gap: 2.8rem; }
}

/* ── the box: a real form, so it works before any script does ────────────── */
.box { margin: 2.2rem 0 0; max-width: 620px; }
.box-row {
  display: flex; align-items: stretch; gap: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  backdrop-filter: blur(10px);
  overflow: hidden;
}
.box-row:focus-within { border-color: var(--fg); }
.box-row input {
  flex: 1; min-width: 0;
  border: 0; background: transparent; color: var(--fg);
  font: inherit; font-size: 1.0625rem;
  padding: 1.15rem 1.2rem;
}
.box-row input::placeholder { color: var(--slate); }
.box-row input:focus { outline: none; }
/* The one thing on this page anybody came to press, and it was the same size as the field
   beside it and the same weight as a nav link. Bigger, and the accent rather than the
   inverted pair the other filled buttons use: on a first screen with one action, the action
   should not have to be found. */
.box-row button {
  flex: 0 0 auto;
  border: 0;
  background: var(--blue-deep); color: #ffffff;
  font: inherit; font-size: 1.0625rem; font-weight: 500;
  padding: 0 1.8rem; cursor: pointer;
  transition: background 0.15s ease;
}
.box-row button:hover { background: var(--blue-deep-hover); }
/* The length: one slider over the range the backend actually accepts, rather than four
 * preset buttons. A range input submits its value in a plain GET form with no script involved,
 * so this keeps the "paste it and go with JavaScript off" promise that the radios were there
 * for. What does need the script is the readout, so front.js inserts it rather than the markup
 * shipping a number that would go stale the moment the handle moved without it. The two end
 * labels are always true either way.
 */
.lengths { display: flex; align-items: center; gap: 0.65rem; margin-top: 1rem; }
.lengths > label { color: var(--slate); font-size: 0.8125rem; }
.lengths .len-end { color: var(--slate); }
/* Once front.js has added the live value, the two end labels are noise. */
.lengths.has-readout .len-end { display: none; }
.lengths .len-now {
  min-width: 3.6em; text-align: right;
  color: var(--fg);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.8125rem;
  /* Digits that do not shift the track as the number changes width. */
  font-variant-numeric: tabular-nums;
}

.lengths input[type="range"] {
  flex: 1; min-width: 0; height: 20px; margin: 0;
  -webkit-appearance: none; appearance: none; background: transparent; cursor: pointer;
}
.lengths input[type="range"]::-webkit-slider-runnable-track {
  height: 2px; background: var(--border); border-radius: 2px;
}
.lengths input[type="range"]::-moz-range-track {
  height: 2px; background: var(--border); border-radius: 2px;
}
.lengths input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 14px; height: 14px; margin-top: -6px;
  border-radius: 50%; background: var(--fg); border: 0;
  transition: transform 0.12s ease;
}
.lengths input[type="range"]::-moz-range-thumb {
  width: 14px; height: 14px;
  border-radius: 50%; background: var(--fg); border: 0;
}
.lengths input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.15); }
.lengths input[type="range"]:focus-visible { outline: none; }
.lengths input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--fg);
}
.lengths input[type="range"]:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--fg);
}

@media (max-width: 560px) {
  .box-row { flex-direction: column; }
  .box-row button { border-left: 0; border-top: 1px solid var(--border); padding: 0.9rem; }
  /* The end labels are the first thing to go: the live readout says the same thing better. */
  .lengths .len-end { display: none; }
}

/* ── sections ────────────────────────────────────────────────────────────── */
section { position: relative; }
.band { padding: 5.5rem 0; border-bottom: 1px solid var(--border); }
.band-light {
  background: var(--cloud);
  color: var(--ink);
  border-bottom: 1px solid var(--line-light);
}
.band-light .label { color: var(--ink-soft); }
.band-head { display: grid; grid-template-columns: 1fr 1fr; gap: 2.4rem; align-items: end; margin-bottom: 2.6rem; }
.band-head p { color: var(--slate); max-width: 46ch; }
.band-light .band-head p { color: var(--ink-soft); }
@media (max-width: 820px) { .band-head { grid-template-columns: 1fr; gap: 1rem; } }

/* ── the grid: one large frame, four small, magazine style ───────────────── */
.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.film { margin: 0; }
.shot {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
}
.frame-lead { grid-column: span 4; }
.frame-lead .shot { aspect-ratio: 21 / 9; }
.frame-half { grid-column: span 2; }
@media (max-width: 900px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
  .frame-lead { grid-column: span 2; }
  .frame-lead .shot { aspect-ratio: 16 / 9; }
  .frame-half { grid-column: span 2; }
}

.shot a.play {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0; padding: 0; cursor: pointer;
  background: transparent; color: var(--fg);
  text-align: left; font: inherit;
}
.shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.shot .veil {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.06) 0%, rgba(0, 0, 0, 0.34) 100%);
  transition: background 0.25s ease;
}
.shot a.play:hover .veil { background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.18) 100%); }
/* The ring goes on the veil: an inset shadow on the button paints under the poster. */
.shot a.play:focus-visible { outline: none; }
.shot a.play:focus-visible .veil { box-shadow: inset 0 0 0 2px var(--fg), inset 0 0 0 4px var(--ground); }
.cap {
  display: flex; align-items: baseline; gap: 0.7rem;
  margin-top: 0.55rem; font-size: 0.9375rem; color: var(--muted);
}
.cap b { font-weight: 400; color: var(--fg); letter-spacing: -0.01em; }
.cap-meta { margin-left: auto; color: var(--slate); }
.shot .glyph {
  position: absolute; left: 1rem; top: 0.9rem;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(0, 0, 0, 0.45);
  display: grid; place-items: center; font-size: 0.7rem; padding-left: 2px;
  transition: background 0.2s ease, color 0.2s ease;
}
.shot a.play:hover .glyph { background: var(--blue-deep); color: #ffffff; border-color: var(--blue); }
.shot iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ── the pipeline strip, above the three columns ─────────────────────────────
 *
 * One inline SVG, 1970 by 150 natural, scaled to the wrap: at the 1600 the wrap reaches on a
 * wide screen its labels land near 14px, and it degrades gracefully down to about 1000, which
 * is where 92vw stops being enough for a strip this long. Below 820 it is withdrawn entirely
 * and .flow-steps, the same eight stages written out, stops being screen-reader-only and
 * becomes the visible version. The list is in the markup at every width, which is why the SVG
 * is aria-hidden: one of the two is always the accessible copy, never both.
 */
.flow { margin: 0 0 3rem; }
.flow-svg { display: block; width: 100%; height: auto; }
/* Mermaid names its label groups `.label`, which on this page is the small grey uppercase
   eyebrow above every heading. Unreset, every node in the diagram renders as spaced capitals
   at thirteen pixels. The diagram's own <style> block, scoped to its id, sets the family, the
   size and the fill it wants, so all that is needed here is to stop the page's rule reaching
   in. Any future generated SVG on this page needs the same guard. */
.flow-svg .label, .flow-svg .nodeLabel, .flow-svg .edgeLabel {
  font-size: inherit; font-weight: 400; letter-spacing: normal;
  text-transform: none; color: inherit;
}
.flow-steps {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); border: 0;
}
@media (max-width: 820px) {
  .flow-svg { display: none; }
  .flow-steps {
    position: static; width: auto; height: auto; margin: 0; padding: 0 0 0 1.4rem;
    overflow: visible; clip: auto; color: var(--ink-soft);
    display: grid; gap: 0.55rem;
  }
  .flow-steps li::marker { color: var(--slate); font-variant-numeric: tabular-nums; }
}

/* ── three columns of prose, on the light band ───────────────────────────── */
.cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3rem 2.4rem; }
.cols > div > .label { display: block; margin-bottom: 0.9rem; }
.cols h3 { margin-bottom: 0.7rem; font-size: 1.6rem; letter-spacing: -0.02em; }
.cols p { color: var(--ink-soft); }
.cols p + p { margin-top: 0.7rem; }
@media (max-width: 900px) { .cols { grid-template-columns: 1fr; gap: 2.2rem; } }

/* ── the inventory: five stages, sixteen lines, hairlines only ───────────────
 *
 * <details> rather than a scripted accordion, for the same reason the FAQ is: the browser
 * already implements a disclosure, and nothing on this page may depend on a module loading.
 * The first one ships open so the section is never five closed bars with no content in the
 * first response, which is what a crawler and a reader both meet.
 */
.stages { border-top: 1px solid var(--border); }
.stages details { border-bottom: 1px solid var(--border); }
.stages summary {
  list-style: none; cursor: pointer;
  display: grid; grid-template-columns: 5.5rem 1fr auto; align-items: baseline; gap: 1rem;
  padding: 1.15rem 0;
}
.stages summary::-webkit-details-marker { display: none; }
.stages .st-n { color: var(--slate); }
.stages .st-t { font-size: 1.25rem; letter-spacing: -0.02em; }
/* The count doubles as the open/closed affordance: a bare number would read as decoration. */
.stages .st-c { color: var(--slate); }
.stages .st-c::after { content: " +"; }
.stages details[open] .st-c { color: var(--fg); }
.stages details[open] .st-c::after { content: " –"; }
.stages ol {
  list-style: none; counter-reset: line;
  margin: 0; padding: 0 0 1.4rem;
  display: grid; gap: 0.65rem;
}
.stages li {
  counter-increment: line;
  display: grid; grid-template-columns: 5.5rem 1fr; gap: 1rem;
  color: var(--muted); font-size: 0.9375rem; max-width: 88ch;
}
.stages li::before {
  content: counter(line, decimal-leading-zero);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.78rem; color: var(--slate);
}
@media (max-width: 620px) {
  .stages summary { grid-template-columns: 1fr auto; }
  .stages .st-n { grid-column: 1 / -1; }
  .stages li { grid-template-columns: 2.2rem 1fr; gap: 0.6rem; }
}

/* ── the rate card, as a table ───────────────────────────────────────────── */
.rates { width: 100%; border-collapse: collapse; }
.rates th, .rates td { text-align: left; padding: 1.15rem 1rem; border-bottom: 1px solid var(--border); vertical-align: baseline; }
.rates thead th { color: var(--slate); font-weight: 500; font-size: 0.8125rem; letter-spacing: 0.035em; text-transform: uppercase; }
.rates td:first-child { width: 12rem; }
.rates .price { font-size: 1.5rem; letter-spacing: -0.02em; white-space: nowrap; }
.rates .per { color: var(--slate); font-size: 0.875rem; }
.rates td:last-child { text-align: right; white-space: nowrap; }
.rates tbody tr:hover { background: var(--surface); }
.rate-note { color: var(--slate); font-size: 0.875rem; margin-top: 1.2rem; }
@media (max-width: 760px) {
  .rates thead { display: none; }
  .rates tr { display: grid; grid-template-columns: 1fr auto; gap: 0 1rem; padding: 1.1rem 0; border-bottom: 1px solid var(--border); }
  .rates td { border: 0; padding: 0.1rem 0; }
  .rates td:first-child { width: auto; }
  .rates td:nth-child(3) { grid-column: 1 / -1; color: var(--slate); }
  /* And the button spans the row, or its x position depends on how long the description is. */
  .rates td:last-child { grid-column: 1 / -1; text-align: left; margin-top: 0.4rem; }
}

/* ── faq ─────────────────────────────────────────────────────────────────── */
.faq { border-top: 1px solid var(--border); max-width: 820px; }
.faq details { border-bottom: 1px solid var(--border); }
.faq summary {
  list-style: none; cursor: pointer; padding: 1.15rem 0;
  display: flex; justify-content: space-between; gap: 1.5rem; font-size: 1.0625rem;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--slate); font-size: 1.3rem; line-height: 1; }
.faq details[open] summary::after { content: "–"; color: var(--fg); }
.faq p { color: var(--slate); padding: 0 0 1.3rem; max-width: 68ch; }

/* ── Kali ────────────────────────────────────────────────────────────────────
 *
 * Monochrome like everything else, and quiet on purpose: a mascot is the one element on a page
 * like this that will happily become the loudest thing on it. She gets a size and a hairline,
 * no colour of her own, and the tally light on her head is the only pixel on the page allowed
 * to be red - which is why it only lights when something is actually wrong.
 */
.kali-intro { display: grid; grid-template-columns: auto 1fr; gap: 1.6rem; align-items: start; margin-bottom: 2.4rem; }
.kali-intro .label { display: block; margin-bottom: 0.5rem; }
.kali-sub { color: var(--slate); max-width: 62ch; margin-top: 0.9rem; }
.kali-mascot { display: block; width: 40px; height: 40px; }
.kali-mascot-lg { width: 84px; height: 84px; }
@media (max-width: 620px) { .kali-intro { grid-template-columns: 1fr; gap: 1rem; } }

.kali-lanes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.kali-lane {
  display: grid; align-content: start; gap: 0.4rem;
  border: 1px solid var(--border); border-radius: 8px;
  padding: 1.3rem 1.3rem 1.5rem;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.kali-lane:hover { border-color: var(--fg); background: var(--surface); }
.kali-lane .lane-n { color: var(--slate); }
.kali-lane .lane-t { font-size: 1.0625rem; letter-spacing: -0.01em; }
.kali-lane .lane-p { color: var(--slate); font-size: 0.9375rem; }
.kali-note { color: var(--slate); margin-top: 1.4rem; max-width: 80ch; }
@media (max-width: 820px) { .kali-lanes { grid-template-columns: 1fr; } }

/* The launcher. An anchor to #kali until the module upgrades it, so it is never a dead pill. */
.kali-launch {
  position: fixed; right: 1.4rem; bottom: 1.4rem; z-index: 60;
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.5rem 0.95rem 0.5rem 0.55rem;
  border: 1px solid var(--border); border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 92%, transparent); backdrop-filter: blur(10px);
  font-size: 0.875rem; font-weight: 500;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.kali-launch:hover { border-color: var(--fg); }
.kali-launch .kali-mascot { width: 28px; height: 28px; }
/* Without the module there is no face, and a pill of pure padding looks like a mistake. */
.kali-launch:not(:has(.kali-launch-face)) { padding-left: 0.95rem; }

.kali-panel {
  position: fixed; right: 1.4rem; bottom: 5.2rem; z-index: 61;
  width: min(400px, calc(100vw - 2.8rem));
  max-height: min(620px, calc(100vh - 7.5rem));
  display: flex; flex-direction: column;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface);
  overflow: hidden;
}
/* `display: flex` above outranks the user agent's `[hidden] { display: none }`, so without this
   the panel is open from the moment the module appends it. */
.kali-panel[hidden] { display: none; }
.kali-head { display: flex; align-items: center; gap: 0.7rem; padding: 0.85rem 0.9rem; border-bottom: 1px solid var(--border); }
.kali-who { display: grid; line-height: 1.25; }
.kali-who b { font-weight: 500; }
.kali-who small { color: var(--slate); font-size: 0.8125rem; }
.kali-x { margin-left: auto; background: none; border: 0; color: var(--slate); font: inherit; font-size: 1.35rem; line-height: 1; cursor: pointer; padding: 0 0.2rem; }
.kali-x:hover { color: var(--fg); }
.kali-tabs { display: flex; border-bottom: 1px solid var(--border); }
.kali-tabs button {
  flex: 1; background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--slate); font: inherit; font-size: 0.8125rem; padding: 0.65rem 0.4rem; cursor: pointer;
}
.kali-tabs button[aria-selected="true"] { color: var(--fg); border-bottom-color: var(--fg); }
.kali-body { padding: 1rem 0.9rem 1.1rem; overflow-y: auto; }
.kali-body label:not(.sr-only) { display: block; color: var(--slate); font-size: 0.8125rem; margin: 0 0 0.4rem; }
.kali-body label:not(.sr-only) + textarea, .kali-body label:not(.sr-only) + input { margin-bottom: 0.9rem; }
.kali-body input, .kali-body textarea {
  width: 100%; font: inherit; font-size: 0.9375rem; color: var(--fg);
  background: var(--ground); border: 1px solid var(--border); border-radius: 6px;
  padding: 0.6rem 0.7rem; resize: vertical;
}
.kali-body input:focus, .kali-body textarea:focus { outline: none; border-color: var(--fg); }
#kali-ask-form { display: flex; gap: 0.5rem; }
/* `display: flex` above outranks the user agent's `[hidden] { display: none }`, exactly as
   it does for the panel itself. Without this the question box stays on screen after Kali
   has handed over to a person — which is an invitation to ask a fourth question and be
   refused, and worse than never having offered. */
#kali-ask-form[hidden] { display: none; }
#kali-ask-form button { flex: 0 0 auto; font: inherit; font-size: 0.875rem; font-weight: 500; border: 0; border-radius: 6px; background: var(--invert-bg); color: var(--invert-fg); padding: 0 1rem; cursor: pointer; }
.kali-answer:not(:empty) { margin-top: 0.9rem; border-top: 1px solid var(--border); padding-top: 0.9rem; }
.kali-a-t { font-size: 0.9375rem; margin-bottom: 0.35rem; }
.kali-a-b { color: var(--muted); font-size: 0.9375rem; }
.kali-a-l { display: inline-block; margin-top: 0.6rem; color: var(--slate); font-size: 0.8125rem; text-decoration: underline; }
.kali-a-l:hover { color: var(--fg); }
.kali-fine { color: var(--slate); font-size: 0.8125rem; margin: 0.2rem 0 0.9rem; }
.kali-send { width: 100%; }
@media (max-width: 480px) {
  .kali-panel { right: 0.7rem; left: 0.7rem; width: auto; bottom: 4.8rem; }
  .kali-launch { right: 0.7rem; bottom: 0.7rem; }
}

/* ── closing ─────────────────────────────────────────────────────────────── */
.close { padding: 6rem 0; text-align: center; }
.close h2 { max-width: 24ch; margin: 0 auto 1.6rem; }
.close .box { margin-left: auto; margin-right: auto; }
.close .lengths { justify-content: center; }

footer {
  padding: 2.4rem 0 3.4rem;
  border-top: 1px solid var(--border);
  color: var(--slate);
  font-size: 0.875rem;
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem; align-items: baseline;
}
footer a:hover { color: var(--fg); }
footer .spacer { margin-left: auto; }

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

/* ── the theme switch ────────────────────────────────────────────────────────
 *
 * A glyph in the nav, not a labelled control. It sits beside "Sign in" and "Start", which
 * are the two things anybody came here to press, and a third word competing with them for a
 * preference most people never change would be the wrong trade. Built by theme.js rather
 * than shipped in the markup — see the note at the top of that file for why this one control
 * is allowed not to exist without a script.
 */
.theme-toggle {
  display: inline-grid; place-items: center;
  width: 30px; height: 30px;
  border: 1px solid var(--border); border-radius: 999px;
  background: transparent; color: var(--muted);
  font: inherit; font-size: 0.9rem; line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.theme-toggle:hover { border-color: var(--fg); color: var(--fg); }
