/* ─────────────────────────────────────────────────────────────────────
   NOSHASHI — shared page modules
   ─────────────────────────────────────────────────────────────────────
   Components used by both the landing page and the server-rendered
   pages (/news/, /status/, /progress/, /contact/). Everything here
   obeys DESIGN.md:

   - status colour is spent on status only, never on decoration
   - --telemetry is spent on a value that is genuinely updating now
   - orbital geometry is static, bleeds off an edge, never above 8%
   - every number is IBM Plex Mono with tabular numerals
   - a panel never contains another bordered panel

   It assumes the token block (--ground, --surface, --rule, --brand,
   --tele, --go/--hold/--nogo, --ink/--muted/--faint, --r, --shell) is
   already defined by the host page: inline on the landing page, by
   core.css everywhere else.
   ───────────────────────────────────────────────────────────────── */

/* ── Instrument header strip ────────────────────────────────────────
   A console has a title bar carrying its own state. One rule, one
   label, one reading — the same shape as .relay-console-head on the
   landing page, generalised so every module can wear it. */
.console{background:var(--surface);border:1px solid var(--rule);
  border-radius:var(--r);overflow:hidden}
.console-head{display:flex;align-items:center;justify-content:space-between;
  gap:16px;padding:12px 16px;border-bottom:1px solid var(--rule);flex-wrap:wrap}
.console-head strong{font:10px "IBM Plex Mono",monospace;letter-spacing:.16em;
  color:var(--faint);font-weight:500}
.console-head .reading{font:10px "IBM Plex Mono",monospace;letter-spacing:.12em;
  color:var(--faint);font-variant-numeric:tabular-nums}
.console-head .reading.live{color:var(--tele)}
.console-head .reading.stale{color:var(--hold)}
.console-body{padding:16px}
.console-foot{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;
  padding:10px 16px;border-top:1px solid var(--rule);
  color:var(--faint);font:9px "IBM Plex Mono",monospace;letter-spacing:.12em}

/* ── Mission clock ──────────────────────────────────────────────────
   Two readings an operator actually uses: the wall clock the ledger is
   timestamped in, and elapsed time since the project's own epoch. Both
   tabular, because a clock that changes width every second reads as a
   broken instrument. Cyan: these tick. */
.clockbar{display:flex;flex-wrap:wrap;gap:1px;background:var(--rule);
  border:1px solid var(--rule);border-radius:var(--r);overflow:hidden}
.clockbar div{background:var(--surface);padding:12px 16px;flex:1 1 150px;min-width:0}
.clockbar .label{display:block;font:9px "IBM Plex Mono",monospace;
  letter-spacing:.18em;color:var(--faint);margin-bottom:6px}
.clockbar .value{font:15px "IBM Plex Mono",monospace;font-variant-numeric:tabular-nums;
  color:var(--ink);letter-spacing:-.01em;display:block}
.clockbar .value.live{color:var(--tele)}

/* ── Mission log ────────────────────────────────────────────────────
   Project updates and maintenance, newest first, on a single spine.
   The spine is a hairline rather than a border per row: a log is one
   object, not a stack of cards, and the card-prison rule forbids the
   alternative anyway. Each entry's kind is carried by a word, never by
   colour alone. */
.log{position:relative;padding-left:22px}
.log::before{content:"";position:absolute;left:3px;top:6px;bottom:6px;width:1px;
  background:var(--rule)}
.log-entry{position:relative;padding:0 0 22px}
.log-entry:last-child{padding-bottom:0}
.log-entry::before{content:"";position:absolute;left:-22px;top:6px;width:7px;height:7px;
  border-radius:50%;background:var(--ground);border:1px solid var(--rule)}
.log-entry[data-kind="release"]::before{border-color:var(--brand);background:var(--brand)}
.log-entry[data-kind="maintenance"]::before{border-color:var(--hold)}
.log-entry[data-kind="incident"]::before{border-color:var(--nogo)}
.log-meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:10px;margin-bottom:5px}
.log-meta time{font:10px "IBM Plex Mono",monospace;letter-spacing:.12em;color:var(--faint);
  font-variant-numeric:tabular-nums}
.log-kind{font:9px "IBM Plex Mono",monospace;letter-spacing:.16em;
  border:1px solid var(--rule);border-radius:999px;padding:2px 8px;color:var(--muted)}
.log-entry[data-kind="release"] .log-kind{color:var(--brand);
  border-color:color-mix(in srgb,var(--brand) 40%,var(--rule))}
.log-entry[data-kind="maintenance"] .log-kind{color:var(--hold);
  border-color:color-mix(in srgb,var(--hold) 40%,var(--rule))}
.log-entry[data-kind="incident"] .log-kind{color:var(--nogo);
  border-color:color-mix(in srgb,var(--nogo) 40%,var(--rule))}
.log-entry h3{font-size:15px;line-height:1.35;margin:0 0 6px}
.log-entry h3 a{color:inherit;text-decoration:none}
.log-entry h3 a:hover{color:var(--brand)}
.log-entry p{font-size:13px;color:var(--muted);line-height:1.62;margin:0}
.log-entry .log-link{display:inline-block;margin-top:7px;font:10px "IBM Plex Mono",monospace;
  letter-spacing:.14em;color:var(--brand);text-decoration:none}
.log-entry .log-link:hover{text-decoration:underline;text-underline-offset:3px}

/* ── News feed ──────────────────────────────────────────────────────
   Third-party headlines. They are quoted material, not our reading, so
   they are set quieter than our own copy and every row states its
   publisher and its age. No status colour appears anywhere in this
   module: a headline is not a verdict. */
.feed{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);
  border-radius:var(--r);overflow:hidden}
.feed-item{background:var(--surface);padding:16px 18px;display:grid;gap:7px;
  transition:background .16s ease}
.feed-item:hover{background:color-mix(in srgb,var(--surface) 88%,var(--brand))}
.feed-item .feed-meta{display:flex;flex-wrap:wrap;gap:10px;align-items:baseline}
.feed-item .src{font:9.5px "IBM Plex Mono",monospace;letter-spacing:.16em;
  color:var(--faint);text-transform:uppercase}
.feed-item time{font:9.5px "IBM Plex Mono",monospace;letter-spacing:.12em;
  color:var(--faint);font-variant-numeric:tabular-nums}
.feed-item a.headline{font-size:14.5px;line-height:1.45;color:var(--ink);
  text-decoration:none;font-weight:500;text-wrap:pretty}
.feed-item a.headline:hover{color:var(--brand);text-decoration:underline;
  text-underline-offset:3px}
.feed-empty{background:var(--surface);padding:22px 18px;color:var(--faint);font-size:13px;
  line-height:1.6}
.feed-note{margin-top:12px;font-size:11px;color:var(--faint);line-height:1.6}

/* Two columns once there is room for two columns of headline. */
.feed.cols{grid-template-columns:repeat(2,1fr)}
@media (max-width:820px){ .feed.cols{grid-template-columns:1fr} }

/* ── Status board ───────────────────────────────────────────────────
   Flight-readiness rows. This is the one module allowed status colour,
   because every row genuinely reports status — and each one is still
   paired with a word, never colour alone. */
.board{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);
  border-radius:var(--r);overflow:hidden}
.board-row{background:var(--surface);padding:14px 18px;display:grid;
  grid-template-columns:minmax(0,1fr) auto;gap:14px;align-items:center}
.board-row .what{min-width:0}
.board-row .what b{display:block;font-size:13.5px;font-weight:500;color:var(--ink)}
.board-row .what span{display:block;font-size:12px;color:var(--muted);margin-top:3px;
  line-height:1.5}
.board-state{display:inline-flex;align-items:center;gap:7px;white-space:nowrap;
  font:10px "IBM Plex Mono",monospace;letter-spacing:.14em;color:var(--faint)}
.board-state i{width:7px;height:7px;border-radius:50%;background:var(--faint);flex:none}
.board-state[data-state="go"]{color:var(--go)} .board-state[data-state="go"] i{background:var(--go)}
.board-state[data-state="hold"]{color:var(--hold)} .board-state[data-state="hold"] i{background:var(--hold)}
.board-state[data-state="nogo"]{color:var(--nogo)} .board-state[data-state="nogo"] i{background:var(--nogo)}

/* ── Progress ledger ────────────────────────────────────────────────
   Shipped versus planned, with the evidence for "shipped" being a
   release tag rather than an assertion. The bar encodes one ratio and
   is labelled with the same ratio in numbers, because a bar alone is
   not a reading. */
.prog{display:grid;gap:14px}
.prog-line{display:grid;gap:8px}
.prog-line .prog-head{display:flex;justify-content:space-between;gap:12px;align-items:baseline}
.prog-line .prog-head b{font-size:13.5px;font-weight:500}
.prog-line .prog-head span{font:11px "IBM Plex Mono",monospace;color:var(--faint);
  font-variant-numeric:tabular-nums}
.prog-track{height:4px;background:var(--elevated);border-radius:2px;overflow:hidden}
.prog-fill{height:100%;background:var(--brand);border-radius:2px}
.prog-line[data-state="done"] .prog-fill{background:var(--go)}
.prog-line .prog-note{font-size:12px;color:var(--muted);line-height:1.55}

/* Milestone rail — four fixed columns of shipped fact. */
.rail{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--rule);
  border:1px solid var(--rule);border-radius:var(--r);overflow:hidden}
.rail div{background:var(--surface);padding:16px 18px;min-width:0}
.rail .label{display:block;font:9px "IBM Plex Mono",monospace;letter-spacing:.18em;
  color:var(--faint);margin-bottom:7px}
.rail strong{display:block;font:17px "IBM Plex Mono",monospace;color:var(--ink);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;font-weight:500}
.rail small{display:block;margin-top:5px;font-size:11px;color:var(--faint);line-height:1.5}
@media (max-width:900px){ .rail{grid-template-columns:repeat(2,1fr)} }
@media (max-width:520px){ .rail{grid-template-columns:1fr} }

/* ── Support console ────────────────────────────────────────────────
   A console the visitor opens, not a thing that opens itself. It is
   docked bottom-right, it starts closed, and it never auto-opens or
   plays a notification — a marketing page that interrupts you is the
   register this product is arguing against.

   Styled as an instrument panel: one title bar carrying its own state,
   a transcript, one command field. No avatars, no bubbles with tails,
   no typing dots pretending to be a person breathing. */
.support-launch{position:fixed;right:20px;bottom:20px;z-index:60;
  display:inline-flex;align-items:center;gap:9px;
  background:var(--surface);color:var(--ink);border:1px solid var(--rule);
  border-radius:999px;padding:11px 17px;cursor:pointer;
  font:11px "IBM Plex Mono",monospace;letter-spacing:.14em;
  box-shadow:0 8px 26px rgba(0,0,0,.34);
  transition:border-color .16s,transform .16s,color .16s}
.support-launch:hover,.support-launch:focus-visible{border-color:var(--brand);transform:translateY(-1px)}
.support-launch:focus-visible{outline:2px solid var(--tele);outline-offset:3px}
.support-launch i{width:7px;height:7px;border-radius:50%;background:var(--tele);flex:none}
.support-launch[hidden]{display:none}

.support-panel{position:fixed;right:20px;bottom:20px;z-index:61;
  width:min(390px,calc(100vw - 32px));max-height:min(620px,calc(100vh - 40px));
  display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--r);
  box-shadow:0 18px 50px rgba(0,0,0,.45);overflow:hidden}
.support-panel[hidden]{display:none}
.support-top{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:13px 15px;border-bottom:1px solid var(--rule);flex:none}
.support-top .who{display:grid;gap:3px;min-width:0}
.support-top strong{font:10px "IBM Plex Mono",monospace;letter-spacing:.18em;
  color:var(--faint);font-weight:500}
.support-top span{font-size:13px;color:var(--ink);font-weight:500}
.support-close{background:transparent;border:1px solid var(--rule);border-radius:6px;
  color:var(--faint);width:28px;height:28px;flex:none;cursor:pointer;
  font:13px "IBM Plex Mono",monospace;line-height:1;
  transition:color .14s,border-color .14s}
.support-close:hover,.support-close:focus-visible{color:var(--ink);border-color:var(--brand)}

.support-log{flex:1;overflow-y:auto;padding:15px;display:grid;gap:14px;align-content:start}
.support-msg{display:grid;gap:5px;max-width:100%}
.support-msg .from{font:9px "IBM Plex Mono",monospace;letter-spacing:.18em;color:var(--faint)}
.support-msg p{font-size:13.5px;line-height:1.6;color:var(--ink);overflow-wrap:anywhere}
.support-msg.visitor{justify-items:end;text-align:right}
.support-msg.visitor p{color:var(--muted)}
/* A hairline, not a second bordered box inside a bordered panel. */
.support-msg.console{border-left:2px solid var(--rule);padding-left:12px}
.support-msg.console[data-grounded="false"]{border-left-color:var(--hold)}
.support-msg .links{display:flex;flex-wrap:wrap;gap:8px;margin-top:3px}
.support-msg .links a{font:10px "IBM Plex Mono",monospace;letter-spacing:.12em;
  color:var(--brand);text-decoration:none;border:1px solid var(--rule);
  border-radius:999px;padding:4px 10px}
.support-msg .links a:hover{border-color:var(--brand)}
.support-suggest{display:flex;flex-wrap:wrap;gap:7px;padding:0 15px 13px}
.support-suggest button{background:transparent;border:1px solid var(--rule);border-radius:999px;
  color:var(--muted);font:10.5px "IBM Plex Mono",monospace;padding:6px 11px;cursor:pointer;
  transition:color .14s,border-color .14s}
.support-suggest button:hover,.support-suggest button:focus-visible{color:var(--ink);border-color:var(--brand)}
.support-form{display:flex;gap:8px;padding:12px 14px;border-top:1px solid var(--rule);flex:none}
.support-form input{flex:1;min-width:0;background:var(--ground);color:var(--ink);
  border:1px solid var(--rule);border-radius:var(--r);padding:11px 13px;font-size:13.5px;
  font-family:inherit}
.support-form input:focus{outline:none;border-color:var(--brand)}
.support-form button{flex:none;background:var(--brand);color:#fff;border:1px solid var(--brand);
  border-radius:var(--r);padding:0 15px;font:11px "IBM Plex Mono",monospace;letter-spacing:.1em;
  cursor:pointer}
.support-form button[disabled]{opacity:.55;cursor:progress}
.support-foot{padding:0 15px 12px;font-size:10.5px;color:var(--faint);line-height:1.5}
.support-foot a{color:var(--brand)}
@media (max-width:560px){
  .support-panel{right:8px;left:8px;bottom:8px;width:auto;max-height:calc(100vh - 16px)}
  .support-launch{right:12px;bottom:12px}
}
@media (prefers-reduced-motion:reduce){
  .support-launch{transition:none}
}

/* ── Contact form ───────────────────────────────────────────────────
   Field, label above, hairline between rows. Same input treatment as
   the address checker on the landing page so the site has one way of
   asking for text. */
.form-grid{display:grid;gap:16px}
.form-row{display:grid;gap:7px}
.form-row.split{grid-template-columns:repeat(2,1fr);gap:14px}
.form-row label,.form-field label{font:10px "IBM Plex Mono",monospace;letter-spacing:.16em;
  color:var(--faint);text-transform:uppercase}
.form-field{display:grid;gap:7px;min-width:0}
.form-field input,.form-field select,.form-field textarea{
  background:var(--ground);color:var(--ink);border:1px solid var(--rule);
  border-radius:var(--r);padding:12px 14px;font-size:14px;font-family:inherit;width:100%;
  transition:border-color .14s}
.form-field textarea{min-height:150px;resize:vertical;line-height:1.6}
.form-field input:focus,.form-field select:focus,.form-field textarea:focus{
  outline:none;border-color:var(--brand)}
.form-field .hint{font-size:11.5px;color:var(--faint);line-height:1.5}
/* The honeypot. Off-screen rather than display:none — some bots skip
   anything explicitly hidden, and none of them tab to it. */
.form-trap{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}
.form-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:4px}
.form-status{font-size:13px;line-height:1.6;min-height:1.6em}
.form-status[data-tone="good"]{color:var(--go)}
.form-status[data-tone="bad"]{color:var(--nogo)}
.form-status[data-tone="busy"]{color:var(--muted)}
@media (max-width:560px){ .form-row.split{grid-template-columns:1fr} }

/* ── Frame suppression ──────────────────────────────────────────────
   Inside a .console the module supplies content, not a frame. The
   card-prison rule in DESIGN.md forbids a bordered panel inside a
   bordered panel, and .feed / .board both carry their own border for
   standalone use. `.bare` drops it for exactly that case — the 1px
   grid background stays, because that is the row separator, not a
   frame. */
.feed.bare,.board.bare{border:0;border-radius:0}
.console-body.flush{padding:0}

/* A second badge tone, for a channel that is not GO. Amber because the
   builds are pre-1.0 — not red, because a beta is not a fault. */
.badge .dot.hold{background:var(--hold)}

/* ── Questions ──────────────────────────────────────────────────────
   Plain disclosure rows. Native <details> so the answers are in the
   markup whether or not they are open — a crawler and a reader with
   JavaScript off both get the whole answer. */
.qa{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);
  border-radius:var(--r);overflow:hidden}
.qa details{background:var(--surface)}
.qa summary{cursor:pointer;padding:16px 18px;font-size:14.5px;font-weight:500;
  color:var(--ink);list-style:none;display:flex;justify-content:space-between;gap:16px;
  align-items:center;transition:color .14s}
.qa summary::-webkit-details-marker{display:none}
.qa summary::after{content:"+";font:14px "IBM Plex Mono",monospace;color:var(--faint);flex:none}
.qa details[open] summary::after{content:"−"}
.qa summary:hover,.qa summary:focus-visible{color:var(--brand)}
.qa summary:focus-visible{outline:2px solid var(--tele);outline-offset:-2px}
.qa .answer{padding:0 18px 18px;font-size:13.5px;line-height:1.65;color:var(--muted);
  max-width:74ch}
.qa .answer a{color:var(--brand)}

/* ── Charts ─────────────────────────────────────────────────────────
   One accent, one encoding, labelled axes, tabular numerals. The
   gradient under the trace is the brand board's own treatment and is
   the only gradient in this stylesheet; it encodes nothing and is there
   so the trace reads as lit rather than printed on top.

   Nothing here takes a status colour. A price moving is not a verdict,
   and the moment a chart is allowed green and red, the one red thing
   on a NO-GO screen stops being the verdict. */
.chart{margin:0;position:relative}
.chart svg{display:block;width:100%;height:auto;overflow:visible}
.chart .grid{stroke:var(--rule);stroke-width:1;shape-rendering:crispEdges}
.chart .axis{fill:var(--faint);font-family:"IBM Plex Mono",monospace;font-size:11px;
  font-variant-numeric:tabular-nums;letter-spacing:.02em}
.chart .trace{fill:none;stroke:var(--brand);stroke-width:2.2;
  stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke}
.chart .bars rect{fill:var(--brand);opacity:.42}
/* Telemetry cyan on the crosshair: it tracks a value right now, which
   is the one thing that token is reserved for. */
.chart .cross-x{stroke:var(--tele);stroke-width:1;stroke-dasharray:3 4;
  vector-effect:non-scaling-stroke}
.chart .cross-dot{fill:var(--tele);stroke:var(--surface);stroke-width:2}
.chart svg:focus-visible{outline:2px solid var(--tele);outline-offset:2px}

.chart-tip{position:absolute;pointer-events:none;z-index:3;
  background:var(--elevated);border:1px solid var(--rule);border-radius:6px;
  padding:8px 11px;min-width:128px;
  font-family:"IBM Plex Mono",monospace;font-size:11.5px;
  font-variant-numeric:tabular-nums;color:var(--ink);
  box-shadow:0 6px 20px rgba(0,0,0,.34)}
.chart-tip b{display:block;font-size:14px;font-weight:500;margin-bottom:3px}
.chart-tip span{color:var(--faint);font-size:10px;letter-spacing:.1em}

/* Every chart states its question. If it cannot be written in six
   words the chart should not exist — DESIGN.md's test, kept visible. */
.chart-q{font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--faint);margin:0 0 10px}
.chart-q.vol{margin-top:22px;padding-top:18px;border-top:1px solid var(--rule)}
.chart-empty{color:var(--faint);font-size:13px;line-height:1.6;padding:18px 0}

/* ── XRP quote ──────────────────────────────────────────────────────
   The number at size, the change beside it in a glyph and a sign. */
.xrp-top{display:flex;justify-content:space-between;align-items:flex-end;gap:20px;
  flex-wrap:wrap;margin-bottom:14px}
.xrp-quote .label{display:block;font-family:"IBM Plex Mono",monospace;font-size:9px;
  letter-spacing:.18em;color:var(--faint);margin-bottom:7px}
.xrp-quote strong{font-family:"IBM Plex Mono",monospace;font-size:clamp(26px,3.4vw,36px);
  font-weight:500;letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:var(--ink);
  display:inline-block;vertical-align:baseline}
.xrp-change{display:inline-flex;align-items:baseline;gap:6px;margin-left:12px;
  font-family:"IBM Plex Mono",monospace;font-size:12px;color:var(--muted);
  font-variant-numeric:tabular-nums}
.xrp-change[data-dir="down"] span[aria-hidden]{color:var(--muted)}
.rail.bare{border:0;border-radius:0;border-top:1px solid var(--rule)}
@media (max-width:560px){
  .xrp-top{align-items:flex-start}
  .chart-q{font-size:9px}
}

/* ═══════════════════════════════════════════════════════════════════
   THE HERO SKY
   ═══════════════════════════════════════════════════════════════════
   Everything in this block is confined to the landing page's opening
   section. It is the one place on the site with depth and movement, and
   it exists because the first screen has to say what this is before it
   says anything measurable.

   Below the hero, the instrument rules resume without exception: static
   geometry, one accent, status colour reserved for verdicts. Nothing
   here may be reused behind a panel, a table, a chart or a number.
   ═══════════════════════════════════════════════════════════════════ */

/* The starfield canvas. Behind everything, including the existing
   orbital plates, and inert to the pointer. */
canvas.cosmos{position:absolute;inset:0 0 auto 0;width:100%;height:min(100%,920px);
  pointer-events:none;z-index:-3;opacity:1;
  transition:opacity .3s ease;
  /* Fades out before the copy begins, so no star is ever behind a word
     it has to compete with. */
  -webkit-mask-image:radial-gradient(120% 92% at 50% 8%,#000 38%,rgba(0,0,0,.55) 66%,transparent 100%);
  mask-image:radial-gradient(120% 92% at 50% 8%,#000 38%,rgba(0,0,0,.55) 66%,transparent 100%)}

/* The blue and teal washes that used to sit here are removed. The hero
   keeps the navy lift already defined on .hero::before in the page's own
   stylesheet — one radial of the ground colour's lighter neighbour,
   introducing no hue. Depth now comes from the starfield and the
   existing orbital plates, which is enough; two tinted radials on top
   of them was the "blue AI gradient" DESIGN.md bans, arrived at one
   reasonable-looking step at a time. */

/* ── Brand lockup ───────────────────────────────────────────────────
   The name, at the size a name should be, inside a live orbital
   system. The rings turn; the mark does not. */
.lockup{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;
  gap:30px;margin-bottom:28px;position:relative}
.lockup-brand{display:flex;align-items:center;gap:22px;min-width:0}
@media (max-width:900px){
  .lockup{grid-template-columns:1fr;gap:20px}
}
.lockup-mark{position:relative;width:92px;height:92px;flex:none;
  display:grid;place-items:center}
.lockup-mark svg.mark{width:46px;height:46px;color:var(--ink);position:relative;z-index:2}

/* Three rings at three inclinations and three speeds. Rotation only —
   no scale, no opacity pulse, nothing that reads as a loading state. */
.lockup-ring{position:absolute;inset:0;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--brand) 36%,transparent);
  animation:orbit 26s linear infinite}
.lockup-ring:nth-child(2){inset:11px;border-color:color-mix(in srgb,var(--tele) 26%,transparent);
  transform:rotate(58deg);animation-duration:19s;animation-direction:reverse}
.lockup-ring:nth-child(3){inset:22px;border-style:dashed;
  border-color:color-mix(in srgb,var(--faint) 40%,transparent);animation-duration:34s}
/* One body on the outer path, so the rotation is legible as an orbit
   rather than as a spinning circle. */
.lockup-ring:first-child::after{content:"";position:absolute;top:-3px;left:50%;
  width:6px;height:6px;margin-left:-3px;border-radius:50%;background:var(--tele);
  box-shadow:0 0 0 4px color-mix(in srgb,var(--tele) 16%,transparent)}
@keyframes orbit{to{transform:rotate(360deg)}}
.lockup-ring:nth-child(2){animation-name:orbit-tilted}
@keyframes orbit-tilted{from{transform:rotate(58deg)}to{transform:rotate(418deg)}}

.lockup-word{display:grid;gap:9px;min-width:0}
/* Solid ink. A gradient here clipped the letters to transparent and let
   the ground show through them, which reads as faded on the one word
   that should be the most confident thing on the page. */
.lockup-word h2{font-size:clamp(32px,4.4vw,52px);letter-spacing:.14em;line-height:1;
  font-weight:600;margin:0;color:var(--ink)}
.lockup-word .tagline{font-family:"IBM Plex Mono",monospace;font-size:11px;
  letter-spacing:.34em;text-transform:uppercase;color:var(--tele)}

/* ── Mission ────────────────────────────────────────────────────────
   Said once, at the top, before any claim is made. */
.mission{max-width:56ch;margin:0;padding-left:22px;
  border-left:1px solid var(--rule);
  font-size:clamp(14px,1.35vw,16.5px);line-height:1.62;color:var(--muted)}
@media (max-width:900px){
  .mission{padding-left:16px;max-width:66ch}
}
.mission strong{color:var(--ink);font-weight:600}
.mission .mission-pair{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;
  margin-top:16px;padding-top:14px;border-top:1px solid var(--rule)}
.mission .mission-pair div{min-width:0}
.mission .mission-pair b{display:block;font-family:"IBM Plex Mono",monospace;font-size:9.5px;
  letter-spacing:.18em;color:var(--faint);margin-bottom:5px;font-weight:500}
.mission .mission-pair span{font-size:13.5px;color:var(--ink);line-height:1.45}

/* The tagline is three words at .34em tracking; below this it wraps to
   a second line and the lockup stops reading as one object. */
@media (max-width:430px){
  .lockup-word .tagline{letter-spacing:.2em;font-size:10px}
}
@media (max-width:640px){
  .lockup{gap:18px;margin-bottom:24px}
  .lockup-mark{width:78px;height:78px}
  .lockup-mark svg.mark{width:38px;height:38px}
  .mission .mission-pair{grid-template-columns:1fr}
}

/* Reduced motion stops the orbits. The rings stay — the picture is not
   the animation, and removing the state change would be the wrong fix. */
@media (prefers-reduced-motion:reduce){
  .lockup-ring{animation:none}
}

/* The canvas is hidden on a phone along with the other bleed graphics:
   at that width it is a battery cost for something a thumb covers. */
@media (max-width:900px){
  canvas.cosmos{display:none}
}

/* ── Mobile navigation ──────────────────────────────────────────────
   Below 820px the nav links are lifted into a panel of their own by
   site/assets/nav.js. The bar itself is untouched, which is what lets
   one stylesheet serve three pages that each hand-rolled their own
   header markup.

   The button is hidden above the breakpoint and the panel cannot be
   opened there, so desktop behaviour is exactly what it was. */
.nav-menu-btn{display:none;align-items:center;gap:8px;
  background:transparent;color:var(--muted);
  border:1px solid var(--rule);border-radius:var(--r);
  padding:8px 12px;min-height:38px;cursor:pointer;
  font:11px "IBM Plex Mono",monospace;letter-spacing:.14em;
  transition:color .14s,border-color .14s}
.nav-menu-btn:hover,.nav-menu-btn:focus-visible{color:var(--ink);border-color:var(--brand)}
.nav-menu-btn:focus-visible{outline:2px solid var(--tele);outline-offset:3px}

/* Three rules that become a cross. Drawn rather than an icon font, and
   it is the only place on the site with a shape that changes state. */
.nav-menu-bars{position:relative;width:14px;height:10px;flex:none}
.nav-menu-bars::before,.nav-menu-bars::after{content:"";position:absolute;left:0;right:0;
  height:1.5px;background:currentColor;transition:transform .18s cubic-bezier(.2,0,0,1),top .18s}
.nav-menu-bars::before{top:0}
.nav-menu-bars::after{top:8.5px}
.nav-menu-btn.on .nav-menu-bars::before{top:4px;transform:rotate(45deg)}
.nav-menu-btn.on .nav-menu-bars::after{top:4px;transform:rotate(-45deg)}

.nav-panel{position:absolute;left:0;right:0;top:100%;z-index:30;
  background:var(--ground);border-bottom:1px solid var(--rule);
  box-shadow:0 18px 40px rgba(0,0,0,.42);
  max-height:calc(100vh - 62px);overflow-y:auto}
.nav-panel[hidden]{display:none}
.nav-panel nav{display:grid}
.nav-panel a{display:flex;align-items:center;min-height:52px;
  padding:0 max(4vw,18px);
  color:var(--muted);text-decoration:none;font-size:15px;
  border-bottom:1px solid color-mix(in srgb,var(--rule) 65%,transparent)}
.nav-panel a:hover,.nav-panel a:focus-visible{color:var(--ink);background:var(--surface)}
.nav-panel a[aria-current="page"]{color:var(--ink)}
.nav-panel a[aria-current="page"]::before{content:"";width:3px;height:16px;flex:none;
  background:var(--brand);margin-right:12px;margin-left:-12px;border-radius:2px}
.nav-panel-foot{display:grid;grid-template-columns:1fr 1fr;gap:10px;
  padding:14px max(4vw,18px) 18px}
.nav-panel-foot a{justify-content:center;min-height:46px;border:1px solid var(--rule);
  border-radius:var(--r);padding:0 14px;font-size:13px;font-weight:600;color:var(--ink)}
.nav-panel-foot a:last-child{background:var(--brand);border-color:var(--brand);color:#fff}

@media (max-width:820px){
  .nav-menu-btn{display:inline-flex}
  /* Only once the menu is actually present — see nav.js. */
  .nav-links.has-menu .btn{display:none}
  /* The header has to establish a containing block for the panel, and
     stop clipping it. */
  header{position:sticky}
  .nav{position:relative}
}
@media (min-width:821px){
  .nav-panel{display:none}
}
@media (prefers-reduced-motion:reduce){
  .nav-menu-bars::before,.nav-menu-bars::after{transition:none}
}

/* ── Touch targets ──────────────────────────────────────────────────
   On a phone the header keeps two controls, and both were sized for a
   cursor: 38px and 32px tall against the 44px a thumb actually wants.
   Raised here rather than globally, because on a desktop the smaller
   control is the correct one and a 44px button in a 62px bar is
   clumsy. */
@media (max-width:820px){
  .nav-menu-btn,header .theme-toggle{min-height:44px;padding-inline:14px}
  header .brand{min-height:44px}
  /* A little more room either side of the bar on a small screen, so
     the wordmark and the menu are not against the bezel. */
  .nav{gap:12px}
}
