/* EONCORE tri-voice — one blue base, accent + type register per voice.
   IBM Plex is the shared open-source superfamily: Sans/Condensed/Serif for
   plain, Mono for tl;dr, and Sans + Mono for spec.
   Teal buttons are constant across all three.
   A fourth voice, consultancy, was removed on 2026-09-09; its content and
   rules are in docs/parked-consultancy-voice.md. */

:root{
  --bg:#0b0e14;
  --fg:#d6e2f0;
  --dim:#6b7a90;
  --line:#1b2431;
  --card:#111722;

  /* constant teal action color */
  --teal:#1fb6a8;
  --teal-2:#2fd3c3;

  /* per-voice, overridden below */
  --accent:#4c8dff;
  --accent-2:#7ab8ff;
  --gold:#d4af37;
  --silver:#b8c2cc;

  --font:"IBM Plex Mono", ui-monospace, monospace;
  --font-display:"IBM Plex Mono", ui-monospace, monospace;
  --mono:"IBM Plex Mono", ui-monospace, monospace;
  --fsize:15px;
  --lh:1.65;
  --ls:-.01em;
}

/* ---------- per-voice theming ---------- */
html[data-voice="honest"]{
  --accent:#b8c2cc;   /* silver */
  --accent-2:#d4dde6;
  /* Body copy is the serif; the headings stay on the condensed sans through
     --font-display, so the page reads editorial without losing the engineered
     title. Only this voice: caveman is mono and spec is sans, and that is what
     tells them apart. */
  --font:"IBM Plex Serif", Georgia, serif;
  --font-display:"IBM Plex Sans Condensed", "Arial Narrow", sans-serif;
  --mono:"IBM Plex Mono", ui-monospace, monospace;
  --fsize:15.5px;
  --lh:1.6;
  --ls:normal;
}
html[data-voice="caveman"]{
  --accent:#4c8dff;   /* blue */
  --accent-2:#7ab8ff;
  /* At 14px the tracking goes back to normal and the leading goes up: negative
     tracking is something a monospace can carry at 17px and not at 14, where it
     closes the counters that make it legible in the first place. The measure is
     capped in ch further down, which is the part that actually decides whether
     a screen of this reads. */
  --font:"IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --font-display:"IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --mono:"IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --fsize:14px;
  --lh:1.72;
  --ls:normal;
}
html[data-voice="ai"]{
  --accent:#54e0a6;   /* doc green */
  --accent-2:#8ff0c8;
  --font:"IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --font-display:"IBM Plex Sans Condensed", "Arial Narrow", sans-serif;
  --mono:"IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --fsize:14.5px;
  --lh:1.55;
  --ls:normal;
}

/* ---------- voice visibility ---------- */
.v,.vi{display:none}
html[data-voice="honest"] .v[data-v="honest"],
html[data-voice="caveman"] .v[data-v="caveman"],
html[data-voice="ai"] .v[data-v="ai"]{display:block}
html[data-voice="honest"] .vi[data-v="honest"],
html[data-voice="caveman"] .vi[data-v="caveman"],
html[data-voice="ai"] .vi[data-v="ai"]{display:inline}

*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}

body{
  position:relative;
  background:var(--bg);
  color:var(--fg);
  font-family:var(--font);
  font-size:var(--fsize);
  line-height:var(--lh);
  letter-spacing:var(--ls);
  padding:0 20px;
  max-width:680px;
  margin:0 auto;
  overflow-wrap:break-word;
}

p,li,a,label,input,textarea,button,nav{
  font-family:var(--font);
}
/* display face carries the voice personality on headings + hero + plaques */
h1,h2,h3,.sub,p.lead,.plaque-title{
  font-family:var(--font-display);
}


/* ---------- header / topbar ---------- */
header{
  position:sticky;
  top:0;
  z-index:20;
  padding:20px 0 10px;
  /* the full-width border and background live on ::before */
  border-bottom:0;
}
.topbar{
  display:flex;
  align-items:center;
  justify-content:space-between;
  flex-wrap:wrap;
  /* row gap first: on a phone the pills wrap under the logo, and 12px there
     read as cramped. Desktop is one row, so only the 12px column gap applies. */
  gap:18px 12px;
}
.brand{display:inline-block;text-decoration:none}
.brand img{display:block;height:26px;width:auto}

/* ---------- voice switcher pills ---------- */
.voiceswitch{
  display:inline-flex;
  border:1px solid var(--line);
  border-radius:999px;
  overflow:hidden;
  background:var(--card);
}
.voiceswitch button{
  font-family:var(--font);
  font-size:11.5px;
  letter-spacing:.02em;
  padding:4px 10px;
  border:0;
  background:transparent;
  color:var(--dim);
  cursor:pointer;
  border-right:1px solid var(--line);
  transition:color .12s, background .12s;
}
.voiceswitch button:last-child{border-right:0}
.voiceswitch button:hover{color:var(--fg)}
.voiceswitch button.active{
  color:var(--bg);
  background:var(--accent);
  font-weight:700;
}

nav{
  margin-top:14px;
  display:flex;
  flex-wrap:wrap;
  gap:4px 14px;
}
nav a{color:var(--dim);text-decoration:none;padding:2px 0;font-size:14px}
nav a:hover{color:var(--fg)}
nav a.active{color:var(--accent)}
nav a::before{content:"› ";color:var(--line)}

/* ---------- nav dropdown (Solutions) ---------- */
nav details{position:relative}
nav details summary{
  color:var(--dim);cursor:pointer;list-style:none;
  padding:2px 0;font-size:14px;
}
nav details summary::-webkit-details-marker{display:none}
nav details summary::before{content:"› ";color:var(--line)}
nav details summary:hover{color:var(--fg)}
nav details[open] summary{color:var(--accent)}
nav .nav-dd-menu{
  position:absolute;top:100%;left:0;margin-top:8px;
  background:var(--card);border:1px solid var(--line);border-radius:8px;
  padding:8px;display:flex;flex-direction:column;gap:4px;
  min-width:220px;z-index:20;
}
nav .nav-dd-menu a{
  color:var(--dim);font-size:14px;padding:6px 8px;border-radius:4px;
}
nav .nav-dd-menu a::before{content:none}
nav .nav-dd-menu a:hover{color:var(--fg);background:var(--line)}
nav .nav-dd-menu a.active{color:var(--accent)}

/* ---------- content ---------- */
main{padding:44px 0 24px}

h1{
  font-weight:700;
  letter-spacing:.01em;
  color:var(--fg);
  margin:0 0 6px;
  font-size:calc(var(--fsize) + 8px);
}
.sub{
  font-weight:400;
  color:var(--accent);
  margin-top:8px;
  margin-bottom:144px;
  font-size:var(--fsize);
}
/* Plain uses the condensed member of the same Plex family for its titles.
   The narrow, engineered shapes keep the force of a display face, while the
   common family gives headings, prose and data one visual origin. */
html[data-voice="honest"] h1{
  font-weight:700;
  text-transform:uppercase;
}
html[data-voice="honest"] .sub{
  font-weight:400;
  text-transform:none;
  font-size:16px;
}
h2{
  font-weight:700;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--accent);
  margin:168px 0 8px;
  padding-bottom:8px;
  border-bottom:1px solid var(--line);
  font-size:calc(var(--fsize) - 1px);
}
h2.h2-step{
  text-transform:none;
  letter-spacing:normal;
  border-bottom:none;
  font-size:calc(var(--fsize) + 6px);
  color:var(--fg);
  line-height:1.2;
  margin:0;
  padding-bottom:0;
}
.step-head{margin:120px 0 8px}
h2.no-line + .step-head{margin-top:8px}
.step-label{
  display:block;
  font-size:12px;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--dim);
  margin:0 0 4px;
}
h3{font-weight:700;color:var(--fg);margin:0 0 4px;font-size:var(--fsize)}
p{margin:0 0 22px;color:var(--fg)}
p.lead{color:var(--fg);font-weight:700;font-size:calc(var(--fsize) + 2px)}
p.lead::before{content:"> ";color:var(--accent)}
/* caveman keeps the terminal prompt; other voices drop it */
html[data-voice="honest"] p.lead::before{content:""}
.dim{color:var(--dim)}
strong{color:var(--fg);font-weight:700}
em{color:var(--accent-2);font-style:italic}
a{color:var(--accent)}

ul{list-style:none;margin:0 0 26px}
li{padding:2px 0 2px 22px;position:relative;color:var(--fg)}
li::before{content:"—";position:absolute;left:0;color:var(--accent)}

/* Plain voice: list items had 2px of padding and no margin, so the gap between
   them was literally 0 and a page like cmb.html stacked thirteen of them into a
   wall. Measured against the card treatment on the home page, which reads calm:
   that is 32 characters per line, dim body copy, 15px of padding. The lists are
   60 characters per line at full brightness with nothing between them.
   So: air between the items, for every list.
   And for items that open with a bold phrase, the rest of the sentence takes the
   dim colour, which is the card's rhythm — bright title, quiet explanation.
   The :has(> strong) is the point of this rule and not a flourish: dimming every
   li was tried first and it flattened the lists that have no bold opener, since
   nothing bright was left in them. This way those are untouched and only keep the
   extra air. :has() is already relied on elsewhere in this file for the diptych
   hover, so it is not a new requirement. */
html[data-voice="honest"] li{
  padding:0 0 0 22px;
  margin:0 0 12px;
}
/* ...except in a list marked .specs. This rule infers intent from markup: it
   assumes a <strong> at the start of an item is a label. That holds for
   sixteen of the seventeen lists in this voice, but not for "Under the hood"
   on eon-path.html, where <strong> marks algorithm names inside the sentence
   ("ML-KEM-768 hybrid with X25519") and some items carry two of them and
   others none. Dimming there produced bold-dim-bold in one line and a list
   that was half dim, half bright. A cleverer selector cannot save it, because
   that list is inconsistent in itself — it has to be treated as a unit, hence
   the class. If a future list bolds terms mid-sentence rather than opening
   with a label, give it class="specs" too. */
html[data-voice="honest"] ul:not(.specs) li:has(> strong){color:var(--dim)}
html[data-voice="honest"] ul:not(.specs) li:has(> strong) strong{color:var(--fg)}

/* ---------- cards ---------- */
.card{
  display:block;
  background:var(--card);
  border:1px solid var(--line);
  border-left:3px solid var(--accent);
  border-radius:8px;
  padding:15px 16px;
  margin:0 0 20px;
  text-decoration:none;
  color:var(--fg);
}
.card:hover{border-left-color:var(--accent-2)}
.card.accent-right{border-left:none;border-right:3px solid var(--accent)}
.card.accent-right:hover{border-right-color:var(--accent-2)}

/* ---------- quote ---------- */
blockquote{
  background:var(--card);
  border-left:3px solid var(--accent);
  border-radius:8px;
  padding:24px 26px;
  margin:0 0 26px;
}
blockquote p{margin:0 0 12px; font-style:italic; font-size:calc(var(--fsize) + 1px); color:var(--fg)}
blockquote p:last-child{margin-bottom:0; font-style:normal; font-size:calc(var(--fsize) - 1px)}
.card.static{border-left-color:var(--line)}
.card.static:hover{border-left-color:var(--line)}
.card h3{color:var(--fg)}
.card p{color:var(--dim);margin:6px 0 0}
.card .go{color:var(--teal);margin-top:10px;display:inline-block;font-weight:700}

/* ---------- key/value strip ---------- */
.kv{border:1px solid var(--line);border-radius:8px;overflow:hidden;margin:0 0 16px}
.kv div{
  display:flex;justify-content:space-between;gap:16px;
  padding:9px 14px;border-bottom:1px solid var(--line);
}
.kv div:last-child{border-bottom:0}
.kv span:first-child{color:var(--dim)}
.kv span:last-child{color:var(--fg);text-align:right}

/* ---------- buttons (teal, constant) ---------- */
.btns{display:flex;flex-wrap:wrap;gap:14px;margin:32px 0 16px}
hr.section-line{border:0;border-top:1px solid var(--line);margin:56px 0 80px}
h2.no-line{border-bottom:none; margin-top:220px}
.hl{background:color-mix(in srgb, var(--accent) 30%, transparent); color:var(--fg); font-weight:700; padding:1px 4px; border-radius:3px}

/* ---------- client logos (hero) ---------- */
.client-logos{margin:0 0 56px;text-align:center}
.client-logos-label{
  display:block;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--accent);margin-bottom:18px;font-size:calc(var(--fsize) - 1px);
}
.client-logos-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:40px}
.client-logos-row img{
  height:64px;width:auto;display:block;
}
.client-logos-row img[src*="garansys"]{height:52px}
html[data-voice="honest"] p.lead{
  font-family:var(--font);
  font-weight:400;
  letter-spacing:0;
  text-transform:none;
  font-size:17px;
  line-height:1.5;
  margin:24px 0 22px;
  color:var(--accent);
  text-wrap:balance;
}
html[data-voice="honest"] p.lead .hl{
  background:none;
  padding:0;
  font-weight:400;
  font-style:italic;
  color:var(--fg);
}
.btn{
  text-decoration:none;font-weight:700;
  padding:11px 18px;border-radius:8px;
  border:1px solid var(--teal);
  color:var(--bg);background:var(--teal);
  transition:background .12s, border-color .12s;
}
.btn:hover{background:var(--teal-2);border-color:var(--teal-2)}
.btn.ghost{background:transparent;color:var(--teal);border-color:var(--line)}
.btn.ghost:hover{border-color:var(--teal)}
/* caveman shell prompt on buttons only */
html[data-voice="caveman"] .btn::before{content:"$ ";opacity:.55}

/* ---------- blog list ---------- */
.post{display:block;padding:14px 0;border-bottom:1px solid var(--line);text-decoration:none}
.post .date{color:var(--dim);font-size:13px}
.post h3{color:var(--fg);margin:4px 0 6px}
.post p{color:var(--dim);margin:0}

/* The consent banner styles itself: consent.js injects them, because the
   banner also runs on /hello/, which does not load this stylesheet. */

/* ---------- cookie table ---------- */
.ctable{width:100%;border-collapse:collapse;margin:0 0 16px;font-size:13.5px}
.ctable th,.ctable td{
  text-align:left;padding:9px 12px;border-bottom:1px solid var(--line);
  vertical-align:top;
}
.ctable thead th{color:var(--dim);font-weight:600;white-space:nowrap}
.ctable tbody tr:last-child td{border-bottom:0}
.ctable code{color:var(--accent-2);overflow-wrap:anywhere}
.ctable-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:8px;margin:0 0 16px}
.ctable-wrap .ctable{margin:0}

/* ---------- form ---------- */
form{margin:8px 0}
label{display:block;color:var(--dim);margin:14px 0 5px}
label::before{content:"# ";color:var(--line)}
html[data-voice="honest"] label::before{content:""}
input,textarea,select{
  width:100%;background:var(--card);border:1px solid var(--line);
  border-radius:8px;color:var(--fg);padding:11px 12px;font-size:var(--fsize);
}
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--teal)}
/* Honeypot on the contact form. Off-screen rather than display:none or
   hidden: a field a bot cannot see is a field a bot does not fill in, and
   filling it in is the whole signal api/contact.php looks for. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
button[type=submit]{
  margin-top:16px;font-weight:700;padding:11px 22px;border:0;
  border-radius:8px;background:var(--teal);color:var(--bg);cursor:pointer;
}
button[type=submit]:hover{background:var(--teal-2)}

/* ---------- footer ---------- */
footer{
  margin-top:110px;padding:32px 0 56px;
  border-top:1px solid var(--line);color:var(--dim);font-size:13.5px;
}
footer a{color:var(--dim)}
/* The company identification. Smaller and dimmer than the rest of the footer:
   it has to be there and it has to be readable, but it is not something anyone
   came to the page for. max-width keeps it to two lines on a desktop rather
   than one very long one. */
footer .legal{
  margin:14px 0 0;
  max-width:62ch;
  font-size:12.5px;
  line-height:1.55;
  opacity:.75;
}
footer .eu{margin-top:10px}
footer .eu img{max-width:100%;height:auto;opacity:.9}

/* ================= paintings — the "cryptography atelier" ================= */
/* One primitive, three voice expressions. SVG inherits --accent via currentColor. */
figure{margin:0}
.art-svg{display:block;width:100%;height:auto;color:var(--accent)}
/* A rendered piece rather than a drawn one: same box as .art-svg, no
   currentColor to inherit because a raster carries its own. */
.art-photo{display:block;width:100%;height:auto}

/* consultancy: framed gallery piece + museum plaque */
.painting{margin:40px 0 56px}
.painting-frame{
  position:relative;
  background:
    radial-gradient(120% 120% at 50% 0%, rgba(255,255,255,0.03), transparent 60%),
    var(--card);
  border:1px solid var(--line);
  border-radius:4px;
  padding:22px 20px 18px;
  box-shadow:0 1px 0 rgba(255,255,255,0.04) inset, 0 24px 60px -40px #000;
}
/* thin inner mat line — the frame within the frame */
.painting-frame::after{
  content:"";position:absolute;inset:10px;
  border:1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  border-radius:2px;pointer-events:none;
}
.plaque{
  display:flex;flex-direction:column;gap:2px;
  margin:14px auto 0;width:max-content;max-width:100%;
  padding:8px 16px;text-align:center;
  border-top:1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.plaque-title{
  font-style:italic;letter-spacing:.02em;color:var(--fg);
  font-size:calc(var(--fsize) - 1px);
}
.plaque-meta{
  font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);
}

/* no-nonsense: art as bare diagram, no ornament */
.painting-bare{margin:20px 0 22px}
.painting-bare .art-svg,
.painting-bare .art-photo{max-width:440px}
.painting-bare figcaption{
  margin-top:8px;font-size:12.5px;color:var(--dim);letter-spacing:.02em;
}

/* caveman: art as terminal glyphs */
.painting-ascii-wrap{margin:18px 0 22px}
.art-ascii{
  font-family:var(--font);color:var(--accent);
  font-size:13px;line-height:1.35;white-space:pre;overflow-x:auto;
  border-left:3px solid var(--line);padding:8px 0 8px 14px;margin:0;
}
.ascii-cap{display:block;color:var(--dim);margin-top:6px;font-size:13px}

/* AI / documentation voice — dense, structured, machine-readable */
.v[data-v="ai"] code{
  font-family:var(--mono);font-size:.88em;
  background:var(--card);border:1px solid var(--line);
  border-radius:4px;padding:1px 5px;color:var(--accent-2);
}
.v[data-v="ai"] h2{
  text-transform:none;letter-spacing:.01em;
  font-size:calc(var(--fsize) + 3px);color:var(--fg);
  border-bottom:1px solid var(--line);
}
.v[data-v="ai"] h3{color:var(--accent);font-size:var(--fsize)}
.v[data-v="ai"] p,.v[data-v="ai"] li{color:#c2cfdd}
/* spec table: key/value parameter rows */
.spec{border:1px solid var(--line);border-radius:6px;overflow:hidden;margin:0 0 16px;font-family:var(--mono);font-size:13px}
.spec div{display:flex;gap:16px;padding:8px 12px;border-bottom:1px solid var(--line)}
.spec div:last-child{border-bottom:0}
.spec .k{color:var(--accent);flex:0 0 42%;min-width:0;overflow-wrap:anywhere}
.spec .val{color:var(--fg);text-align:right;margin-left:auto;overflow-wrap:anywhere}
/* doc note / status line */
.docnote{border-left:3px solid var(--accent);background:var(--card);padding:10px 14px;border-radius:0 6px 6px 0;margin:0 0 16px;font-size:13.5px;color:var(--dim)}
.docnote code{color:var(--accent-2)}

/* About — "The Artists": framed portrait miniatures (consultancy) */
.portraits{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(92px,1fr));
  gap:16px;margin:22px 0 28px;
}
.portrait{text-align:center}
.portrait-frame{
  aspect-ratio:1;display:grid;place-items:center;position:relative;
  background:radial-gradient(120% 120% at 50% 0%, rgba(255,255,255,0.04), transparent 60%), var(--card);
  border:1px solid var(--line);border-radius:3px;box-shadow:0 16px 40px -34px #000;
}
.portrait-frame::after{
  content:"";position:absolute;inset:6px;border-radius:2px;
  border:1px solid color-mix(in srgb, var(--accent) 45%, transparent);
}
.portrait-frame b{font-style:italic;font-weight:400;font-size:22px;color:var(--accent);letter-spacing:.02em}
.portrait figcaption{margin-top:8px;display:flex;flex-direction:column;gap:1px}
.portrait .nm{font-size:12.5px;color:var(--fg);font-weight:700}
.portrait .rl{font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}

@media (max-width:520px){
  /* row gap first: the pills sit under the logo here, and they were crowding it */
  .topbar{gap:16px 10px}
  .voiceswitch{width:100%}
  .voiceswitch button{flex:1 1 0;padding:5px 3px;font-size:10.5px;letter-spacing:0;text-align:center;white-space:nowrap}
  .painting-frame{padding:16px 12px 12px}
  .art-ascii{font-size:11px}
}

/* cards: single column by default (mobile), grid on desktop */
.cards{display:block}
.step-with-art{display:flex;flex-direction:column;gap:24px}
.step-with-art .step-art{margin:0}
.step-with-art .step-art .art-svg{margin-left:auto}
.cards.cards-stack{display:flex;flex-direction:column}
@media (min-width:900px){
  /* real 2-column grid: one fixed track (cards), one flexible track (art).
     default = art left, cards right; .cards-first flips the track order. */
  .step-with-art{display:grid; grid-template-columns:1fr 400px; align-items:start; gap:40px}
  .step-with-art.cards-first{grid-template-columns:400px 1fr}
  .step-with-art .step-art{margin:0; min-width:0}
  .step-with-art .step-art .art-svg{width:100%; height:auto; max-width:none; margin-left:0}
  .step-with-art .cards-stack{min-width:0}
  /* field and braid read as too wide at the full column width — narrower than the
     diptych, which is meant to fill the column. */
  .step-with-art .painting-bare.step-art:has(.fd-ink) .art-svg,
  .step-with-art .painting-bare.step-art:has(.br-ink) .art-svg{max-width:380px; margin-left:auto}
  /* CMB's benchmark row: more breathing room between the steps and the radar,
     and the radar centred within its own column rather than pinned to its edge. */
  .step-with-art.cmb-benchmark{gap:130px}
  .step-with-art.cmb-benchmark .step-art .art-svg{max-width:340px; margin:0 auto}
  /* these rows hold one short card, so with align-items:start the card sits near
     the row's top while the (taller) art fills the whole row height below it —
     its own vertical centre ends up well below the card's. Pull the art up so the
     two centres line up. Offsets are measured, not derived: card height minus art
     height, halved. */
  .step-with-art .painting-bare.step-art:has(.fd-ink){margin-top:-64px}
  .step-with-art .painting-bare.step-art:has(.br-ink){margin-top:-33px}
}
.cards .card{margin:0 0 20px}

/* The Kartis plans. The price line uses the same mono face and dim key colour
   as the .kv and .spec strips, one step up in size — a plan card is read for
   its number first, and inventing a third type treatment for it would not
   make that clearer. auto-fit rather than a fixed column count, so adding or
   dropping a plan does not leave an empty track next to the ones that are
   left. */
.cards-tiers .price{
  font-family:var(--mono);font-size:calc(var(--fsize) + 2px);font-weight:700;
  color:var(--fg);margin:8px 0 0;
}
.cards-tiers .price .unit,
.cards-tiers .price .pre{font-size:12.5px;font-weight:400;color:var(--dim)}
.cards-tiers .price .unit{margin-left:7px}
.cards-tiers .price .pre{margin-right:7px}
.cards-tiers .card p{margin-top:10px}
@media (min-width:900px){
  /* .cards below sets two columns for everyone; auto-fit keeps two cards wide
     and lets a third one in without another rule. */
  .cards.cards-tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:20px;align-items:start}
  .cards.cards-tiers .card{margin:0;height:100%}
}
}

/* ================= DESKTOP — a distinct laptop layout ================= */
@media (min-width:900px){
  body{max-width:1120px; padding:0 56px}

  /* roomier header */
  header{padding:30px 0 0}
  nav{margin-top:20px; gap:6px 24px; padding-bottom:14px}
  nav a{font-size:15px}
  .voiceswitch button{font-size:12px; padding:5px 13px}

  main{
    padding:52px 0 8px;
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    column-gap:56px;
  }
  /* HERO row: page title (+ tagline) on the left, the painting on the right */
  main > h1{grid-column:1; grid-row:1; align-self:center; margin:0; max-width:34ch}
  main > .painting,
  main > .painting-bare,
  main > .painting-ascii-wrap{grid-column:2; grid-row:1; margin:0; align-self:center}
  /* all running content spans full width below the hero, in a readable column */
  main > div.v:not(.painting-ascii-wrap){grid-column:1 / -1; grid-row:2; max-width:820px}
  /* 820px is a judgement call for a proportional face, where character width
     varies. Caveman is monospace, so ch is exact and the column can be stated
     in what actually matters: 820px at 14px is 97 characters to a line, half
     again past what anyone reads without losing their place. */
  html[data-voice="caveman"] main > div.v:not(.painting-ascii-wrap){max-width:74ch}

  /* bigger hero type */
  h1{font-size:calc(var(--fsize) + 30px); line-height:1.05}
  .sub{font-size:calc(var(--fsize) + 3px); margin-top:12px}
  p.lead{font-size:calc(var(--fsize) + 7px); line-height:1.4}
  h2{margin-top:44px}

  /* hero painting sizing */
  .painting-frame{padding:30px 30px 20px}

  /* two-up cards on desktop */
  .cards{display:grid; grid-template-columns:1fr 1fr; gap:18px; align-items:start}
  .cards .card{margin:0; height:100%}

  /* kv / form / plaque stay comfortable, not full-bleed */
  .kv, form{max-width:620px}

  .portraits{grid-template-columns:repeat(5,1fr)}

  /* AI/docs voice: no two-pane hero — a single clean documentation column */
  html[data-voice="ai"] main{display:block; max-width:840px; margin-inline:auto}
  html[data-voice="ai"] main > div.v{max-width:none}
  html[data-voice="ai"] h1{font-size:calc(var(--fsize) + 18px)}
  html[data-voice="ai"] .cards{grid-template-columns:1fr 1fr}
}

/* very wide: cap the reading column, keep hero art from getting huge */
@media (min-width:1240px){
  main > .painting .art-svg,
  main > .painting-bare .art-svg,
  main > .painting-bare .art-photo{max-width:560px; margin-left:auto}
  /* let step 1's diptych bleed past the 820px reading column, same trick as the hero */
  .step-with-art.bleed-right,
  .step-with-art.cmb-benchmark{max-width:1008px; margin-right:-188px}
  /* same bleed: the why-statement and the trusted-by row read as stuck in a
     narrower left column otherwise, off-centre on a wide page. */
  html[data-voice="honest"] p.lead,
  .client-logos{max-width:1008px; margin-right:-188px}
}


/* The sticky bar's background has to reach past the 680px reading column, or
   content slides visibly along the sides underneath. */
header::before{
  content:"";
  position:absolute;
  /* Reach the viewport edges, and no further. `inset:0 -50vw` put half a
     viewport of background past the header on each side; the left half fell
     off the start of the page where nothing can scroll to it, and the right
     half became 50vw of horizontal scroll on every page that has this header.
     The header is centred, so the distance from its edge to the viewport edge
     is half the viewport minus half the header — which is what this says, and
     which collapses to 0 by itself once the header spans the full width on a
     phone. */
  inset:0 calc(50% - 50vw);
  z-index:-1;
  background:var(--bg);
  border-bottom:1px solid var(--line);
}

/* the wave fills whatever room is left between brand and pills, and doubles as
   the way back to /hello/ */
.waveline{
  opacity:.5;
  align-self:center;
  /* a canvas has an intrinsic 300px width; without basis 0 it counts toward the
     flex line and shoves the voice pills onto a second row */
  flex:1 1 0;
  min-width:40px;
  /* fade both ends so it never butts up against the logo or the pills */
  mask-image:linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
  transition:opacity .15s;
}
.waveline:hover,.waveline:focus-visible{opacity:.85}
#wave-bar{display:block}
@media (prefers-reduced-motion:reduce){ .waveline{opacity:.3} }
/* No narrow-screen rule any more. The wave used to be hidden below 720px on the
   grounds that the topbar had no gap left to fill, but the pills wrap to a row
   of their own on a phone, which leaves exactly the space beside the logo — so
   it keeps that instead of disappearing. Its geometry there is the inline
   fallback on the element, which an ordinary rule here could not override. */

/* ---------- hero lattice background ----------
   Full-bleed canvas behind the header and hero. It breaks out of the body's
   reading column, so body needs a positioning context. */

#lattice-hero{
  position:absolute;
  top:0;
  left:50%;
  transform:translateX(-50%);
  width:100vw;
  height:100vh;
  z-index:0;
  pointer-events:none;
}
#lattice-hero .lattice-hero-canvas{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  display:block;
  /* fade the field out before it reaches the running content */
  mask-image:linear-gradient(180deg,#000 0%,#000 52%,rgba(0,0,0,.35) 78%,transparent 100%);
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 52%,rgba(0,0,0,.35) 78%,transparent 100%);
}
/* keeps the left column readable — the field sits to the right of the text */
#lattice-hero .lattice-hero-scrim{
  position:absolute;
  inset:0;
  background:linear-gradient(100deg,
    var(--bg) 0%,
    color-mix(in srgb, var(--bg) 92%, transparent) 26%,
    color-mix(in srgb, var(--bg) 45%, transparent) 55%,
    color-mix(in srgb, var(--bg) 10%, transparent) 80%);
}
/* the caveman voice has its own ascii-art lattice illustration
   (.painting-ascii-wrap, in index.html) right under the h1 — showing
   this full-bleed animated one too doubled up the visual, so it's
   switched off for that voice. The ai/spec voice goes without it too —
   its plain, structured-documentation layout doesn't want the hero
   illustration either. */
html[data-voice="caveman"] #lattice-hero,
html[data-voice="ai"] #lattice-hero{display:none}

/* ---------- give the hero the whole first screen ----------
   index.html has no hero wrapper: h1 is a direct child of main, but .btns and
   .client-logos sit one level deeper, inside the per-voice `div.v` block — so
   these can't be direct-child (>) selectors, or the margin never lands and the
   hero collapses back to content height. Home page only. */
body.home main > h1{margin-top:13vh}
html[data-voice="honest"] body.home main > h1{
  max-width:25ch;
  font-family:"Archivo", var(--font-display);
  font-size:44px;
  font-weight:400;
  line-height:1.08;
  letter-spacing:-.008em;
  text-transform:uppercase;
}
html[data-voice="honest"] body.home main > h1 .sub{
  font-family:var(--font);
}
@media (min-width:900px){
  html[data-voice="honest"] body.home main > h1{
    line-height:1.05;
  }
}
body.home main .btns:first-of-type{margin-bottom:0}
/* The small labelled section headers sit between the 12px step labels and the
   larger numbered step titles. Keep this homepage hierarchy in the shared
   stylesheet so it cannot depend on a page-local rule loading last. */
html[data-voice="honest"] body.home h2:not(.h2-step){
  font-weight:700;
  font-size:calc(var(--fsize) + 2.5px);
}
/* This one list introduces the card sequence below it. Its bright opening
   phrase leads; the rest uses the same quiet colour as card copy. */
html[data-voice="honest"] body.home .after-hero + ul li{color:var(--dim)}
html[data-voice="honest"] body.home .after-hero + ul li strong{
  font-weight:400;
  color:var(--fg);
}
/* the only element responsible for reaching past the hero — everything above it
   (h1, lead, btns) just flows at its natural height, so the page never has to
   compound several independent pushes to land in the right place. It used to be
   .client-logos; that block is parked (docs/parked-content.md), so the job now
   sits on an explicit .after-hero class. Whatever ends up first below the hero
   carries that class — move it, don't add a second one, or the gap doubles. */
body.home main .after-hero{margin-top:30vh}
@media (max-width:899px){
  body.home main > h1{margin-top:7vh}
  body.home main .after-hero{margin-top:25vh}
}

/* the caption the original painting carried */
#lattice-hero .lattice-hero-caption{
  /* sits just under the nav, right-aligned on the content column, opposite the h1 */
  position:absolute;
  top:172px;
  left:50%;
  transform:translateX(-50%);
  width:min(1120px, 100%);
  padding:0 56px;
  box-sizing:border-box;
  font-family:var(--mono, ui-monospace, monospace);
  font-size:11.5px;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--dim);
  text-align:right;
  z-index:2;
}
@media (max-width:899px){
  #lattice-hero .lattice-hero-caption{top:auto; bottom:24px; padding:0 20px; text-align:left}
}
@media (max-width:520px){
  /* The caption lands in the same vertical band as the hero actions on a
     phone. The diagram is already decorative there, so keep the field and
     remove its museum label rather than putting text behind a button. */
  #lattice-hero .lattice-hero-caption{display:none}
}

/* the page's own content has to sit above it. header::before (the sticky bar's
   full-width backdrop) stays opaque, so the top ~100px of the field sits behind
   the header rather than showing through it. header keeps its own z-index:20
   (set above) so its dropdown menu still stacks above this content. */
main, footer{position:relative; z-index:1}

@media (max-width:899px){
  /* on a phone the text runs the full width, so pull the field back */
  #lattice-hero{height:min(760px, 90vh)}
  #lattice-hero .lattice-hero-scrim{
    background:linear-gradient(180deg,
      color-mix(in srgb, var(--bg) 55%, transparent) 0%,
      color-mix(in srgb, var(--bg) 80%, transparent) 60%,
      var(--bg) 100%);
  }
}

/* ---------- diptych: reveal on hover ----------
   Scoped to .dip-* + .step-with-art, so it touches nothing else.

   At rest the artwork is not drawn at all. Pointing at the CMB or CBA card reveals
   that half and starts its motion; the other half stays hidden. The <svg> keeps its
   box either way, so the row never reflows.

   CMB is a questionnaire being filled in: cell outlines stay put, fills come and go.
   CBA is a scan from outside: the sweep goes round and each contact blips at the
   moment the line reaches its angle (the delays are phased to those angles). */

.dip-half{opacity:0; transition:opacity .5s ease}
.dip-divider{opacity:0; transition:opacity .5s ease}
.step-with-art:has(a.card[href="/cmb.html"]:hover) .dip-half[data-dip="cmb"],
.step-with-art:has(a.card[href="/cba.html"]:hover) .dip-half[data-dip="cba"]{opacity:1}
.step-with-art:has(a.card:hover) .dip-divider{opacity:.16}

/* Outside the homepage row (cba.html, where the diptych hangs in a frame with its
   own plaque) there are no cards to point at, so it is simply always on. */
:not(.step-with-art) > .painting-bare .dip-half,
:not(.step-with-art) > .painting-bare .dip-divider{opacity:1}

@keyframes dipAnswer{
  0%,72%{fill-opacity:0}
  78%{fill-opacity:.8}
  100%{fill-opacity:0}
}
@keyframes dipBreathe{
  0%,100%{fill-opacity:.68}
  50%{fill-opacity:.95}
}
.dip-open{animation:dipAnswer 9s ease-in-out infinite}
.dip-filled{animation:dipBreathe 6s ease-in-out infinite}

@keyframes dipSweep{to{transform:rotate(360deg)}}
@keyframes dipBlip{
  0%,88%{opacity:.3; r:2.6}
  92%{opacity:1; r:4.6}
  100%{opacity:.3; r:2.6}
}
@keyframes dipRing{
  0%,100%{opacity:.42}
  50%{opacity:.72}
}
.dip-sweep{animation:dipSweep 11s linear infinite; transform-origin:510px 150px}
.dip-blip{animation:dipBlip 11s linear infinite}
.dip-ring{animation:dipRing 11s ease-in-out infinite}

/* Play/pause LAST. The `animation` shorthands above reset play-state to running and
   carry the same specificity, so these rules only win by coming after them. */
.dip-half *{animation-play-state:paused}
:not(.step-with-art) > .painting-bare .dip-half *{animation-play-state:running}
.step-with-art:has(a.card[href="/cmb.html"]:hover) .dip-half[data-dip="cmb"] *,
.step-with-art:has(a.card[href="/cba.html"]:hover) .dip-half[data-dip="cba"] *{animation-play-state:running}

/* A plaque for the homepage row. Quieter than .plaque on cba.html — no top rule,
   no frame — because the plain voice draws its art bare. It names something that is
   not on screen at rest, so it fades in and out with the drawing. */
.dip-plaque{
  display:flex; flex-direction:column; gap:2px;
  margin:12px auto 0; width:max-content; max-width:100%;
  text-align:center;
  opacity:0; transition:opacity .5s ease;
}
.dip-plaque .dip-plaque-title{
  font-style:italic; letter-spacing:.02em; color:var(--fg);
  font-size:calc(var(--fsize) - 1px);
}
.dip-plaque .dip-plaque-meta{
  font-size:11.5px; letter-spacing:.16em; text-transform:uppercase; color:var(--dim);
}
.step-with-art:has(a.card:hover) .dip-plaque{opacity:1}
/* outside the homepage row it is always on, like the artwork */
:not(.step-with-art) > .painting-bare .dip-plaque{opacity:1}

/* The bare-voice cap keeps the diptych at 440px; it has a whole column here. */
.step-with-art .painting-bare.step-art:has(.dip-half) .art-svg{max-width:none}

@media (prefers-reduced-motion:reduce){
  .dip-open,.dip-filled,.dip-sweep,.dip-blip,.dip-ring{animation:none}
  .dip-filled{fill-opacity:.8}
  .dip-blip{opacity:.9}
  .dip-ring{opacity:.5}
}

/* ---------- discovery field: reveal on hover ----------
   Scoped to .fd-* + .step-with-art, so it touches nothing else.

   At rest nothing is drawn. Pointing at the Eon Insights card reveals the field and
   starts one cycle of 9s: a scan travels out from the sensor and every asset answers
   the moment the wave reaches it. The ones that are crypto keep a ring and link up
   to their neighbours; the rest fall back to a faint dot. Then the next pass.

   The per-element animation-delay in the partial is that element's distance from the
   sensor. Move a node and its delay has to move with it. */

.fd-ink{opacity:0; transition:opacity .5s ease}
.step-with-art:has(a.card:hover) .fd-ink{opacity:1}

/* Outside the homepage row (eon-insights.html, where the field hangs in a frame with
   its own caption) there is no card to point at, so it is simply always on. */
:not(.step-with-art) > .painting-bare .fd-ink{opacity:1}

@keyframes fdWave{
  0%{r:0; opacity:0}
  3%{opacity:.55}
  46%{opacity:.12}
  55%{r:620; opacity:0}
  100%{r:620; opacity:0}
}
@keyframes fdSeen{
  0%,2%{opacity:0}
  5%{opacity:.9}
  13%{opacity:.3}
  42%{opacity:.28}
  50%,100%{opacity:0}
}
@keyframes fdNode{
  0%,2%{opacity:0}
  6%{opacity:1}
  14%{opacity:.9}
  40%{opacity:.9}
  48%,100%{opacity:0}
}
@keyframes fdRing{
  0%{opacity:0; r:3}
  7%{opacity:.9; r:12}
  13%{opacity:.5; r:9}
  40%{opacity:.5; r:9}
  48%{opacity:0; r:9}
  100%{opacity:0; r:9}
}
@keyframes fdLink{
  0%{stroke-dashoffset:1000; opacity:0}
  5%{opacity:.5}
  22%{stroke-dashoffset:0}
  40%{opacity:.45}
  50%,100%{stroke-dashoffset:0; opacity:0}
}
.fd-wave{animation:fdWave 9s ease-out infinite}
.fd-seen{animation:fdSeen 9s ease-out infinite}
.fd-node{animation:fdNode 9s ease-out infinite}
.fd-ring{animation:fdRing 9s ease-out infinite}
.fd-link{animation:fdLink 9s ease-out infinite}

/* Play/pause LAST. The `animation` shorthands above reset play-state to running and
   carry the same specificity, so these rules only win by coming after them. */
.fd-ink *{animation-play-state:paused}
:not(.step-with-art) > .painting-bare .fd-ink *{animation-play-state:running}
.step-with-art:has(a.card:hover) .fd-ink *{animation-play-state:running}

@media (prefers-reduced-motion:reduce){
  .fd-wave{display:none}
  .fd-seen,.fd-node,.fd-ring,.fd-link{animation:none; opacity:.5}
  .fd-node{opacity:.9}
}

/* ---------- braid: reveal on hover ----------
   Scoped to .br-* + .step-with-art, so it touches nothing else.

   At rest nothing is drawn. Pointing at the Eon Path card reveals the braid and
   starts one cycle of 5.4s: a key packet leaves each strand end, the crossings light
   up as the packets pass, and where the strands meet a single brighter pulse carries
   on down the one channel and lands on the endpoint. */

.br-ink{opacity:0; transition:opacity .5s ease}
.step-with-art:has(a.card:hover) .br-ink{opacity:1}

/* Outside the homepage row (eon-path.html, where the braid hangs in a frame with its
   own plaque) there is no card to point at, so it is simply always on. */
:not(.step-with-art) > .painting-bare .br-ink{opacity:1}

@keyframes brRun{
  0%{stroke-dashoffset:0; opacity:0}
  3%{opacity:.95}
  46%{opacity:.95}
  52%{stroke-dashoffset:-1000; opacity:0}
  100%{stroke-dashoffset:-1000; opacity:0}
}
@keyframes brJoin{
  0%,48%{stroke-dashoffset:0; opacity:0}
  53%{opacity:1}
  78%{stroke-dashoffset:-1000; opacity:1}
  86%{opacity:0}
  100%{stroke-dashoffset:-1000; opacity:0}
}
@keyframes brArrive{
  0%,72%{opacity:.4; r:6}
  80%{opacity:1; r:10}
  92%,100%{opacity:.4; r:6}
}
@keyframes brBreathe{
  0%,100%{stroke-opacity:.42}
  50%{stroke-opacity:.62}
}
.br-comet{animation:brRun 5.4s linear infinite}
.br-join{animation:brJoin 5.4s linear infinite}
.br-arrive{animation:brArrive 5.4s ease-out infinite}
.br-strand{animation:brBreathe 5.4s ease-in-out infinite}

/* Play/pause LAST. The `animation` shorthands above reset play-state to running and
   carry the same specificity, so these rules only win by coming after them. */
.br-ink *{animation-play-state:paused}
:not(.step-with-art) > .painting-bare .br-ink *{animation-play-state:running}
.step-with-art:has(a.card:hover) .br-ink *{animation-play-state:running}

@media (prefers-reduced-motion:reduce){
  .br-comet,.br-join,.br-arrive,.br-strand{animation:none}
  .br-strand{stroke-opacity:.6}
}

/* ---------- artwork: the topology (eon-aethis.html) ----------
   The same machinery as the braid and the field, on a piece that only ever
   appears on its own page: no card to point at, so no reveal — it runs while
   it is on screen and stops for anyone who asked for less motion.

   One cycle of 9s. Five heads run their own flow on their own delay, the two
   rings around the watched host breathe, and the probe's arcs pulse outward in
   the order they are drawn. Nothing is hidden while a head passes: the flows,
   the hosts and the clusters stay exactly as drawn. */

@keyframes tpRun{
  0%{stroke-dashoffset:1000; opacity:0}
  4%{opacity:.9}
  40%{opacity:.9}
  46%{stroke-dashoffset:0; opacity:0}
  100%{stroke-dashoffset:0; opacity:0}
}
@keyframes tpHalo{
  0%,100%{opacity:.18}
  50%{opacity:.5}
}
@keyframes tpScan{
  0%,100%{opacity:.08}
  40%{opacity:.34}
}
.tp-comet{animation:tpRun 9s linear infinite}
.tp-halo{animation:tpHalo 9s ease-in-out infinite}
.tp-scan{animation:tpScan 9s ease-out infinite}

/* Play/pause LAST, for the reason given above the braid's copy of this. */
.tp-ink *{animation-play-state:paused}
:not(.step-with-art) > .painting-bare .tp-ink *{animation-play-state:running}

@media (prefers-reduced-motion:reduce){
  .tp-comet,.tp-halo,.tp-scan{animation:none}
}

/* ---------- artwork: the attack surface (eon-kartis.html) ----------
   What the picture says, in the order it says it: the seed pulses, a head runs
   out along each ray, and the finding it reaches answers with its halo. Six
   rays, 0.55s apart, in a cycle of 9s. The findings themselves never blink —
   they are the content, so only the halo moves. */

@keyframes sfRun{
  0%{stroke-dashoffset:1000; opacity:0}
  3%{opacity:.95}
  26%{opacity:.95}
  32%{stroke-dashoffset:0; opacity:0}
  100%{stroke-dashoffset:0; opacity:0}
}
@keyframes sfHalo{
  0%,100%{opacity:.3}
  8%{opacity:.95}
  30%{opacity:.45}
}
@keyframes sfScan{
  0%,100%{opacity:.1}
  40%{opacity:.38}
}
.sf-comet{animation:sfRun 9s linear infinite}
.sf-halo{animation:sfHalo 9s ease-out infinite}
.sf-scan{animation:sfScan 9s ease-out infinite}

.sf-ink *{animation-play-state:paused}
:not(.step-with-art) > .painting-bare .sf-ink *{animation-play-state:running}

@media (prefers-reduced-motion:reduce){
  .sf-comet,.sf-halo,.sf-scan{animation:none}
}

/* ---------- artwork: pointing at it, and devices without a pointer ----------
   The three drawings in the home page rows each have their own reveal
   machinery: the diptych (.dip-half), the discovery field (.fd-ink) and the
   hybrid braid (.br-ink). All three were revealed by hovering a *card* only,
   which had two consequences.

   Moving the pointer off the card and onto the drawing — to look at the thing
   you just revealed — made it disappear, because the card was no longer
   hovered. Hovering the figure itself now reveals it and runs it too. Over the
   drawing every part shows, since no single card is being pointed at.

   And with no pointer at all there was nothing to hover with, so all three
   were switched off with display:none — a phone showed no artwork whatever.
   They are drawn and running there instead. The original reasoning was that a
   permanently running animation on a phone is worse than no artwork; if that
   turns out to be true in practice, pause them here rather than hide them.

   This sits at the end because the per-artwork blocks above carry the same
   specificity, so it only wins by coming later. */

.step-with-art:has(.step-art:hover) .dip-half,
.step-with-art:has(.step-art:hover) .fd-ink,
.step-with-art:has(.step-art:hover) .br-ink,
.step-with-art:has(.step-art:hover) .dip-plaque{opacity:1}
.step-with-art:has(.step-art:hover) .dip-divider{opacity:.16}
.step-with-art:has(.step-art:hover) .dip-half *,
.step-with-art:has(.step-art:hover) .fd-ink *,
.step-with-art:has(.step-art:hover) .br-ink *{animation-play-state:running}

@media (hover:none){
  .step-with-art .painting-bare.step-art{display:block}
  .step-with-art .dip-half,
  .step-with-art .fd-ink,
  .step-with-art .br-ink,
  .step-with-art .dip-plaque{opacity:1}
  .step-with-art .dip-divider{opacity:.16}
  .step-with-art .dip-half *,
  .step-with-art .fd-ink *,
  .step-with-art .br-ink *{animation-play-state:running}
}

@media (min-width:900px){
  html[data-voice="honest"] .sub{font-size:20px}
}

/* ================== cryptography maturity benchmark ====================== */
/* The questionnaire at /benchmark.html. Everything is scoped under .cmb: the
   rest of the site is an editorial column with 168px between sections, and an
   application screen cannot breathe like that. Inside .cmb the vertical rhythm
   is reset to something a form can live in. Colours and type still come from
   the same tokens, so the voice pills keep working here. */

.cmb h1{margin:0 0 4px}
.cmb h2{margin:0 0 8px}
.cmb .sub{margin-bottom:20px}
.cmb p.lead{margin:0 0 22px}
.cmb .kv{margin:22px 0}
.cmb .btns{margin:26px 0 12px}

/* A button that reads as a link. The intro offers "resume" and "start over"
   mid-sentence, where a real button would shout. */
.cmb-link{
  background:none;border:0;padding:0;cursor:pointer;
  font:inherit;color:var(--accent);text-decoration:underline;
}
.cmb-link:hover{color:var(--accent-2)}

/* ---------- progress ---------- */
/* Sticky, because the answer that tells you how far along you are is the one
   you want while scrolling a round of eight questions. */
.cmb-progress{
  position:sticky;top:0;z-index:20;
  background:var(--bg);
  padding:12px 0 10px;
  border-bottom:1px solid var(--line);
  margin:0 0 28px;
}
.cmb-progress-meta{
  display:flex;justify-content:space-between;gap:16px;
  font-size:12.5px;color:var(--dim);margin:0 0 8px;
}
.cmb-progress-meta strong{color:var(--fg);font-weight:700}
.cmb-progress-track{height:3px;background:var(--line);border-radius:2px;overflow:hidden}
.cmb-progress-fill{
  height:100%;background:var(--accent);width:0;
  transition:width .25s ease;
}

/* ---------- one round ---------- */
.cmb-round-label{
  display:block;font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--dim);margin:0 0 4px;
}
.cmb-round h2{
  text-transform:none;letter-spacing:normal;border-bottom:none;
  font-size:calc(var(--fsize) + 6px);color:var(--fg);line-height:1.25;
  margin:0 0 24px;padding:0;
}

/* ---------- a question ---------- */
.cmb-q{
  border-top:1px solid var(--line);
  padding:22px 0 6px;
}
.cmb-q:first-of-type{border-top:0;padding-top:0}
.cmb-q-text{color:var(--fg);margin:0 0 14px;line-height:1.5}
.cmb-q-num{color:var(--dim);margin-right:8px}

/* The scale. Five radios styled as a segmented control, with the endpoint
   labels underneath rather than inside: the labels are sentences ("not present
   / low / no"), and they do not fit in a 44px box. */
.cmb-scale{margin:0 0 4px}
.cmb-scale-row{display:flex;gap:8px}
.cmb-scale-row label{
  flex:1 1 0;margin:0;position:relative;
  color:var(--fg);
}
.cmb-scale-row label::before{content:""}
.cmb-scale-row input{
  position:absolute;opacity:0;width:1px;height:1px;
}
.cmb-scale-row span{
  display:flex;align-items:center;justify-content:center;
  height:44px;border:1px solid var(--line);border-radius:8px;
  background:var(--card);color:var(--dim);
  font-weight:700;cursor:pointer;
  transition:border-color .12s, color .12s, background .12s;
}
.cmb-scale-row label:hover span{border-color:var(--accent);color:var(--fg)}
.cmb-scale-row input:checked + span{
  background:var(--accent);border-color:var(--accent);color:var(--bg);
}
/* Keyboard focus has to stay visible even though the input is off-screen. */
.cmb-scale-row input:focus-visible + span{outline:2px solid var(--teal);outline-offset:2px}
.cmb-scale-ends{
  display:flex;justify-content:space-between;gap:16px;
  font-size:12px;color:var(--dim);margin:7px 0 0;
}
.cmb-scale-ends span:last-child{text-align:right}

/* ---------- round navigation ---------- */
.cmb-nav{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  margin:34px 0 0;padding-top:22px;border-top:1px solid var(--line);
}
.cmb-nav .cmb-nav-note{color:var(--dim);font-size:12.5px}
.cmb-btn{
  font-family:inherit;font-size:var(--fsize);font-weight:700;
  padding:11px 18px;border-radius:8px;cursor:pointer;
  border:1px solid var(--teal);background:var(--teal);color:var(--bg);
  transition:background .12s, border-color .12s;
}
.cmb-btn:hover:not(:disabled){background:var(--teal-2);border-color:var(--teal-2)}
.cmb-btn:disabled{opacity:.4;cursor:not-allowed}
.cmb-btn.ghost{background:transparent;color:var(--teal);border-color:var(--line)}
.cmb-btn.ghost:hover:not(:disabled){border-color:var(--teal)}

/* ---------- results ---------- */
.cmb-overall{
  border:1px solid var(--line);border-radius:8px;background:var(--card);
  padding:22px 24px;margin:0 0 30px;text-align:center;
}
.cmb-overall-score{
  display:block;font-family:var(--font-display);
  font-size:44px;line-height:1;font-weight:700;color:var(--accent);
}
.cmb-overall-of{display:block;color:var(--dim);font-size:13px;margin-top:8px}

.cmb-radar{margin:0 0 34px;text-align:center}
.cmb-radar svg{max-width:460px;width:100%;height:auto}
.cmb-radar figcaption{color:var(--dim);font-size:12.5px;margin-top:6px}

.cmb-result{
  border-top:1px solid var(--line);
  padding:20px 0 4px;
}
.cmb-result-head{
  display:flex;align-items:baseline;justify-content:space-between;gap:14px;
  margin:0 0 8px;
}
.cmb-result-head h3{margin:0;font-size:calc(var(--fsize) + 1px)}
.cmb-result-score{
  color:var(--accent);font-weight:700;white-space:nowrap;
  font-family:var(--font-display);
}
.cmb-bar{height:6px;background:var(--line);border-radius:3px;overflow:hidden;margin:0 0 12px}
.cmb-bar-fill{height:100%;background:var(--accent);width:0;transition:width .6s ease}
.cmb-result p{color:var(--dim);margin:0 0 14px;font-size:14px;line-height:1.6}

/* People / Process / Technology. Scored from the same answers, so they are a
   second reading of the same data rather than eight more findings — set apart
   to say so. */
.cmb-axes{margin:34px 0 0;padding:22px 0 0;border-top:1px solid var(--line)}
.cmb-axes .cmb-result:first-of-type{border-top:0;padding-top:0}

.cmb-session{
  border:1px solid var(--line);border-radius:8px;
  padding:14px 16px;margin:30px 0 0;font-size:13px;color:var(--dim);
}
.cmb-session code{color:var(--accent-2);overflow-wrap:anywhere}

.cmb-report{
  border:1px solid var(--line);border-radius:8px;background:var(--card);
  padding:20px 22px;margin:22px 0 0;
}
.cmb-report h3{margin:0 0 6px}
.cmb-report p{color:var(--dim);font-size:13.5px;margin:0 0 14px}
.cmb-report label{margin-top:0}
.cmb-status{margin:12px 0 0;font-size:13.5px}
.cmb-status.ok{color:var(--teal-2)}
.cmb-status.err{color:#ff6b6b}

/* ---------- narrow screens ---------- */
@media (max-width:560px){
  .cmb-scale-row{gap:6px}
  .cmb-scale-row span{height:40px}
  .cmb-overall-score{font-size:36px}
  .cmb-nav{flex-wrap:wrap}
  .cmb-nav .cmb-nav-note{order:3;width:100%;text-align:center}
}

/* ================== phoenix beta downloads =============================== */
/* The page at /phoenix.html, rendered by phoenix.js. Grouped per version, the
   same way the plugin's own page does it: four files called
   "eon-phoenix-911bc3a-..." look interchangeable until each one says what it
   is and which version it belongs to. */

.phx{margin-top:28px}
/* No painting in the hero on these pages, so the 144px under the tagline is a
   hole rather than breathing room. */
.phx-page .sub,
.form-page .sub{margin-bottom:40px}
/* On the wide layout, main is a two-column hero: the h1 takes column 1 and a
   painting takes column 2. This page has no painting, and without a rule the
   download list is auto-placed into that slot and ends up beside the title,
   above the text that explains it. It belongs under the copy, full width. */
@media (min-width:900px){
  main > .phx{grid-column:1 / -1; grid-row:3}
}
.phx-release{margin:0 0 34px}
.phx-version{
  display:flex;align-items:center;gap:10px;
  margin:0 0 10px;padding-bottom:8px;
  border-bottom:1px solid var(--line);
}
.phx-badge{
  font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  padding:2px 8px;border-radius:999px;
  background:var(--teal);color:var(--bg);
}
.phx-notes{margin:0 0 14px;color:var(--dim);font-size:13.5px}
.phx-notes li{margin:2px 0}

.phx-build{
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:11px 0;border-bottom:1px solid var(--line);
}
.phx-build:last-child{border-bottom:0}
.phx-build-main{display:flex;flex-direction:column;gap:2px;flex:1 1 240px;min-width:0}
.phx-build-name{overflow-wrap:anywhere}
.phx-build-meta{color:var(--dim);font-size:13px}
.phx-get{padding:7px 16px;font-size:13.5px}
/* Not a disabled button: there is nothing to press yet, and a greyed-out
   button invites clicking to find out why. It does take the same space and
   weight as the Download button it stands in for, though -- as grey italic
   text beside a coloured manual link it disappeared, and the page read as a
   list of manuals with the builds as captions. */
.phx-locked{
  display:inline-block;padding:7px 16px;border-radius:8px;
  border:1px dashed var(--line);color:var(--dim);
  font-size:13px;white-space:nowrap;
}
/* Secondary on purpose: a manual is worth having, but it is not what someone
   came for, and it must not out-shout the download. */
.phx-manuals{flex-basis:100%;font-size:12.5px;color:var(--dim)}
.phx-manuals-label{color:var(--dim);opacity:.75}
.phx-manuals a{color:var(--dim);text-decoration:underline}
.phx-manuals a:hover{color:var(--accent-2)}

.phx-form{
  margin:0 0 34px;padding:18px 20px;
  border:1px solid var(--line);border-radius:10px;background:var(--card);
  max-width:520px;
}
/* Below the list it is a way to refresh expired links, not the point of the
   page, so it gets the spacing of a footnote instead of a heading. */
.phx-release + .phx-form{margin:32px 0 0}
.phx-form label{margin-top:0}
.phx-form .btn{margin-top:14px;display:inline-block}
.phx-form button[disabled]{opacity:.6;cursor:default}

.phx-status{margin:12px 0 0;font-size:13.5px}
.phx-status.ok{color:var(--teal-2)}
.phx-status.err{color:#ff6b6b}

/* Signup and confirmation: the two pages that carry a form of their own.
   They borrow the page's form styling and only add what a result needs —
   a message per field, and the box that replaces the form once it is sent. */
.consent{margin:18px 0 0;font-size:13.5px;color:var(--dim);max-width:520px}
.consent label{display:flex;gap:9px;align-items:flex-start;margin:0;cursor:pointer}
.consent input{width:auto;margin:3px 0 0;flex:0 0 auto}

.field-error{display:block;margin:6px 0 0;font-size:13px;color:#ff6b6b}

.phx-done,.phx-note,.phx-error{
  margin:0 0 20px;padding:18px 20px;
  border:1px solid var(--line);border-radius:10px;background:var(--card);
  max-width:520px;
}
.phx-done{border-color:var(--teal-2)}
.phx-error{border-color:#ff6b6b}
.phx-done h3,.phx-note h3,.phx-error h3{margin:0 0 6px;font-size:16px}
.phx-done p,.phx-note p,.phx-error p{margin:0;color:var(--dim);font-size:14px}

@media (max-width:560px){
  .phx-build{align-items:flex-start}
  .phx-get{width:100%;text-align:center}
}
