/* ===========================================================================
   xemulator design system -- "flight-test protocol"
   ---------------------------------------------------------------------------
   Shared by xemulator.com (landing) and app.xemulator.com (studio).
   This file is self-contained: tokens, reset, base elements, primitives,
   components. Load it FIRST, then a surface-specific stylesheet on top.

     <link rel="stylesheet" href="/static/design-system.css">
     <link rel="stylesheet" href="/static/style.css">

   Contract and usage rules: see ../DESIGN.md (moved out of the web root).
   ASCII only. No emojis, ever -- including decorative glyphs.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. TOKENS
   --------------------------------------------------------------------------- */

:root {
  /* --- surfaces: paper (the document) --- */
  --paper:          #f6f5f0;   /* page ground */
  --paper-raised:   #fbfaf7;   /* cards, wells, form containers */
  --paper-sunken:   #eeede6;   /* recessed strips, table zebra */

  /* --- surfaces: panel (the instrument, always dark) --- */
  --panel:          #0e0f12;   /* panel ground */
  --panel-raised:   #16171c;   /* stimulus block, hovered rows */
  --panel-sunken:   #0a0b0e;   /* caption rail, panel head */

  /* --- ink on paper --- */
  --ink:            #0e0f12;   /* body + headings, 18:1 on paper */
  --ink-muted:      #3c3d38;   /* secondary prose, 10:1 */
  --ink-faint:      #62615a;   /* labels, captions, 5.2:1 -- the floor */

  /* --- ink on panel --- */
  --on-panel:       #f1f0e9;   /* body, 16.8:1 on panel */
  --on-panel-muted: #b9b9b1;   /* secondary, 9.6:1 */
  --on-panel-faint: #8b8c95;   /* labels, 5.7:1 -- the floor */

  /* --- lines --- */
  --line:           #d9d7cd;   /* hairline on paper */
  --line-strong:    #0e0f12;   /* structural border on paper */
  --line-panel:     #2c2d34;   /* hairline on panel */
  --grid:           rgba(14, 15, 18, 0.055); /* drafting grid */

  /* --- signal blue: system voice. Never decorative. --- */
  --blue:           #2233ee;   /* on paper, 6.8:1 */
  --blue-on-panel:  #8390ff;   /* on panel, 6.5:1 */
  --blue-wash:      #eceefd;   /* selected / active fill on paper */

  /* --- heat orange: INTENSITY ONLY. Never a brand accent. --- */
  --heat:           #e8451c;   /* fills, meters, rails -- not text on paper */
  --heat-ink:       #bf3410;   /* heat as TEXT on paper, 5.1:1 */
  --heat-on-panel:  #ff7047;   /* heat as text on panel, 6.6:1 */
  --heat-wash:      #fdf0ec;

  /* --- outcome --- */
  --ok:             #157a45;
  --ok-wash:        #edf5f0;
  --caution:        #d9b23a;   /* provenance flags (DEMO ENGINE) only */
  --caution-wash:   #faf5e4;

  /* --- focus: one ring, everywhere --- */
  --focus:          #2233ee;
  --focus-ring:     0 0 0 2px var(--paper), 0 0 0 4px var(--focus);
  --focus-ring-panel: 0 0 0 2px var(--panel), 0 0 0 4px var(--blue-on-panel);

  /* --- type families --- */
  --font-display: "Archivo", ui-sans-serif, "Helvetica Neue", Arial, sans-serif;
  --font-body:    "Archivo", ui-sans-serif, "Helvetica Neue", Arial, sans-serif;
  --font-mono:    "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Archivo is variable on wdth 62..125. Width is part of the voice:
     display type is EXPANDED, data type is normal. */
  --wdth-display: 116%;
  --wdth-normal:  100%;

  /* --- type scale (1.25 minor third, fluid at the top) --- */
  --size-hero:   clamp(2.375rem, 1.35rem + 4.4vw, 4rem);      /* 38 -> 64 */
  --size-h2:     clamp(1.75rem, 1.15rem + 2.6vw, 2.75rem);    /* 28 -> 44 */
  --size-h3:     clamp(1.375rem, 1.1rem + 1.1vw, 1.75rem);    /* 22 -> 28 */
  --size-h4:     1.0625rem;                                    /* 17 */
  --size-lead:   1.0625rem;                                    /* 17 */
  --size-body:   0.9375rem;                                    /* 15 */
  --size-small:  0.8125rem;                                    /* 13 */
  --size-micro:  0.6875rem;                                    /* 11 -- mono only */

  --lh-display: 1.02;
  --lh-tight:   1.2;
  --lh-body:    1.55;
  --lh-relaxed: 1.7;

  --track-display: -0.028em;
  --track-tight:   -0.012em;
  --track-normal:  0;
  --track-wide:    0.1em;
  --track-wider:   0.2em;

  /* --- space (4px base) --- */
  --s1: 4px;   --s2: 8px;   --s3: 12px;  --s4: 16px;  --s5: 24px;
  --s6: 32px;  --s7: 48px;  --s8: 64px;  --s9: 96px;  --s10: 128px;

  /* --- layout --- */
  --page-max:      1120px;
  --gutter:        clamp(18px, 3.6vw, 32px);
  --measure:       66ch;   /* prose */
  --measure-tight: 52ch;   /* lead paragraphs, post bodies */
  --radius:        0;      /* the system has no radii except .avatar */
  /* The drafting grid's pitch. Any surface that redraws the grid on top of the
     page -- a full-screen overlay, a print sheet -- reads it from here, so the
     ruling cannot drift out of register with the page behind it. */
  --grid-step:     96px;

  /* --- depth: the whole stack, named ---
     Six layers and no more. A raw z-index in a surface stylesheet is a value
     nobody can rank against the others, which is how 9999 gets written. */
  --z-page:    1;    /* every top-level child, above the drafting grid */
  --z-focus:   2;    /* a focus ring, over its own neighbours */
  --z-sticky:  3;    /* a rail or foot riding with a panel */
  --z-head:    4;    /* the panel head, over its own rail */
  --z-overlay: 100;  /* a surface that takes the whole window */
  --z-skip:    200;  /* the skip link, over everything, always reachable */

  /* --- motion --- */
  --dur-1: 120ms;  /* state echo: hover, active */
  --dur-2: 200ms;  /* control transitions */
  --dur-3: 360ms;  /* element entrance */
  --dur-4: 640ms;  /* orchestrated entrance */
  --ease-out:    cubic-bezier(0.2, 0.7, 0.2, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
}

/* ---------------------------------------------------------------------------
   2. RESET + BASE
   --------------------------------------------------------------------------- */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: var(--s7);
}

body {
  font-family: var(--font-body);
  font-size: var(--size-body);
  line-height: var(--lh-body);
  font-variant-numeric: tabular-nums;
  background: var(--paper);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* The drafting grid. Fixed, so the page reads as ink on a ruled sheet.
   Sits behind everything; every top-level child is lifted above it. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    linear-gradient(var(--grid) 1px, transparent 1px) 0 0 / 100% var(--grid-step),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px) 0 0 / var(--grid-step) 100%;
}
body > * { position: relative; z-index: var(--z-page); }

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

a { color: inherit; text-underline-offset: 3px; }
button { font: inherit; color: inherit; cursor: pointer; background: none; border: 0; }
img, svg { display: block; max-width: 100%; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: var(--wdth-display);
  letter-spacing: var(--track-display);
  line-height: var(--lh-display);
  text-wrap: balance;
}
h1 { font-size: var(--size-hero); }
h2 { font-size: var(--size-h2); }
h3 { font-size: var(--size-h3); letter-spacing: var(--track-tight); line-height: var(--lh-tight); }
h4 {
  font-size: var(--size-h4);
  font-weight: 700;
  font-stretch: var(--wdth-normal);
  letter-spacing: var(--track-tight);
  line-height: var(--lh-tight);
}

/* Orphan control on everything that runs as a paragraph, not only on <p>.
   A one-word last line is the cheapest tell that nobody set the type, and it
   turns up in list items, captions and injected blocks just as readily. */
p, li, figcaption, blockquote, dd { text-wrap: pretty; }

/* One focus ring, everywhere. Never remove it; re-point it. */
:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  position: relative;
  z-index: var(--z-focus);
}
.panel :focus-visible,
.on-panel :focus-visible { box-shadow: var(--focus-ring-panel); }

[hidden], .hidden { display: none !important; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------------
   3. TYPE PRIMITIVES
   --------------------------------------------------------------------------- */

/* Uppercase mono label. The system's voice: instrument readouts, section
   eyebrows, field labels, provenance. Always mono, always uppercase. */
.microlabel {
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  font-weight: 600;
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--ink-faint);
}
.panel .microlabel, .on-panel .microlabel { color: var(--on-panel-faint); }

/* Index number. Only for content that is genuinely ordered or addressable:
   protocol sections (02), sub-items (2.1). Never as decoration. */
.index-no {
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  color: var(--blue);
}
.panel .index-no, .on-panel .index-no { color: var(--blue-on-panel); }

/* Mono fine print: counters, telemetry, timestamps, disclaimers. */
.fine {
  font-family: var(--font-mono);
  font-size: var(--size-small);
  line-height: var(--lh-relaxed);
  color: var(--ink-faint);
  text-wrap: pretty;
}
.fine a { color: var(--blue); }
.panel .fine, .on-panel .fine { color: var(--on-panel-faint); }

.lead {
  font-size: var(--size-lead);
  color: var(--ink-muted);
  max-width: var(--measure-tight);
  text-wrap: pretty;
}
.lead b, .lead strong { font-weight: 700; color: var(--ink); }

.prose { max-width: var(--measure); color: var(--ink-muted); text-wrap: pretty; }

/* Hairline that fills remaining space in a baseline-aligned row. */
.rule { flex: 1 1 auto; height: 1px; min-width: var(--s5); background: var(--line); }
.panel .rule, .on-panel .rule { background: var(--line-panel); }

/* ---------------------------------------------------------------------------
   4. STAMPS + PILLS
   ---------------------------------------------------------------------------
   .stamp is load-bearing legal furniture. Every machine-generated persona
   line carries one. Do not restyle it into invisibility; do not remove it. */

.stamp {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 1px 6px;
  border: 1px solid currentColor;
  color: var(--ink-faint);
  white-space: nowrap;
  flex: none;
}
.panel .stamp, .on-panel .stamp { color: var(--on-panel-faint); }
/* Inverted stamp for use on top of a raised/filled block. */
.stamp--solid {
  color: var(--panel);
  background: var(--paper);
  border-color: var(--paper);
}

/* Provenance pill: states a fact about how the output was produced. */
.pill {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  padding: 3px 8px;
  border: 1px solid var(--line);
  color: var(--ink-faint);
  white-space: nowrap;
}
.pill--caution { background: var(--caution); border-color: var(--caution); color: var(--panel); }
.pill--live { background: var(--blue); border-color: var(--blue); color: #fff; }

/* ---------------------------------------------------------------------------
   5. BUTTONS
   ---------------------------------------------------------------------------
   Four ranks. One primary action per view. Labels are verbs and keep the
   same word through the whole flow ("Join the waitlist" -> "Joined"). */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  border: 1px solid var(--line-strong);
  padding: 12px 24px;
  min-height: 44px;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  text-align: center;
  background: transparent;
  color: var(--ink);
  transition: background var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out),
              border-color var(--dur-1) var(--ease-out),
              transform var(--dur-1) var(--ease-out);
}
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn:disabled { opacity: 0.5; cursor: default; transform: none; }

.btn--primary { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.btn--primary:hover:not(:disabled) { background: var(--blue); border-color: var(--blue); color: #fff; }

.btn--ghost:hover:not(:disabled) { border-color: var(--blue); color: var(--blue); }

/* Text-rank action. No border; underline on hover so it still reads as a link. */
.btn--quiet {
  border-color: transparent;
  padding: 12px 4px;
  color: var(--ink-muted);
}
.btn--quiet:hover:not(:disabled) { color: var(--blue); text-decoration: underline; }

/* Controls that live on the dark panel. */
.btn--panel {
  border-color: var(--line-panel);
  color: var(--on-panel-muted);
  background: transparent;
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  padding: 8px 14px;
  min-height: 36px;
}
.btn--panel:hover:not(:disabled) { border-color: var(--blue-on-panel); color: var(--blue-on-panel); }

.btn--sm { padding: 7px 14px; min-height: 36px; font-size: 12.5px; }
.btn--block { width: 100%; }

/* ---------------------------------------------------------------------------
   6. FORM CONTROLS
   --------------------------------------------------------------------------- */

.label,
label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--s2);
}

.field,
select, textarea,
input[type="text"], input[type="email"], input[type="password"], input[type="url"] {
  width: 100%;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.4;
  padding: 12px 12px;
  min-height: 46px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  transition: border-color var(--dur-1) var(--ease-out);
}
.field:hover, select:hover, textarea:hover, input:hover { border-color: #bdbbb0; }
.field:focus, select:focus, textarea:focus, input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: inset 0 0 0 1px var(--blue);
}
.field:focus-visible, select:focus-visible, textarea:focus-visible, input:focus-visible {
  box-shadow: inset 0 0 0 1px var(--blue), var(--focus-ring);
}
textarea { resize: vertical; min-height: 92px; }
::placeholder { color: #9c9a90; opacity: 1; }

/* Segmented control (tabs). The selected segment inverts. */
.segmented { display: inline-flex; border: 1px solid var(--line); }
.segmented > button {
  padding: 10px 18px;
  min-height: 42px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-faint);
  border-right: 1px solid var(--line);
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.segmented > button:last-child { border-right: 0; }
.segmented > button:hover { color: var(--ink); background: var(--paper-raised); }
/* Pressed. Every control in this system answers a press the same way, with the
   same 1px, so a chip, a tab and a button all feel like the same hardware. */
.segmented > button:active { transform: translateY(1px); }
.segmented > button[aria-selected="true"],
.segmented > button.is-active { background: var(--ink); color: var(--paper); }

/* Chip: a one-tap value, not a navigation control. */
.chip {
  border: 1px solid var(--line);
  padding: 7px 12px;
  min-height: 34px;
  font-size: 12.5px;
  color: var(--ink-muted);
  text-align: left;
  transition: border-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out),
              transform var(--dur-1) var(--ease-out);
}
.chip:hover { border-color: var(--blue); color: var(--blue); }
.chip:active { transform: translateY(1px); }

/* ---------------------------------------------------------------------------
   7. MESSAGES
   ---------------------------------------------------------------------------
   Errors say what happened and what to do. They do not apologize. */

.msg {
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink);
  padding: 11px 14px;
  font-size: 14px;
  background: var(--paper-raised);
}
.msg--error   { border-color: var(--heat);  border-left-color: var(--heat);  background: var(--heat-wash); color: var(--ink); }
.msg--ok      { border-color: var(--ok);    border-left-color: var(--ok);    background: var(--ok-wash); }
.msg--caution { border-color: var(--caution); border-left-color: var(--caution); background: var(--caution-wash); }

/* ---------------------------------------------------------------------------
   8. CARDS + HAIRLINE GRID
   ---------------------------------------------------------------------------
   Cards do not float. They are cells in a ruled sheet: the 1px gap in the
   grid IS the border. No shadows anywhere in this system. */

/* A row of cards is a list of peers, so a surface may write it as one. The
   grid is the border either way; the marker is never wanted. */
.card-grid {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  list-style: none;
}
.card-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.card-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.card { background: var(--paper); padding: var(--s5) var(--s5) var(--s6); }
.card h4 { margin: var(--s3) 0 var(--s2); }
.card p { font-size: var(--size-small); color: var(--ink-muted); }

/* A bordered well on paper: forms, verdicts, callouts. */
.well {
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  padding: var(--s6);
}

/* ---------------------------------------------------------------------------
   9. THE PANEL -- the instrument surface
   ---------------------------------------------------------------------------
   Everything the engine emits appears on the panel. Paper is what WE say;
   panel is what the SIMULATION says. Never mix the two. */

.panel {
  background: var(--panel);
  color: var(--on-panel);
  border: 1px solid var(--panel);
}

.panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 11px var(--s5);
  border-bottom: 1px solid var(--line-panel);
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--on-panel-faint);
  min-height: 42px;
}
.panel__status { display: flex; align-items: center; gap: var(--s2); color: var(--on-panel); }

/* Status lamp. Blue pulsing = running. Green solid = complete.
   Faint solid = standby. Never green while work is in flight. */
.lamp {
  width: 7px; height: 7px;
  flex: none;
  background: var(--on-panel-faint);
  display: inline-block;
}
.lamp--live { background: var(--blue-on-panel); animation: lamp-pulse 1.4s var(--ease-in-out) infinite; }
.lamp--done { background: var(--ok); animation: none; }
@keyframes lamp-pulse { 50% { opacity: 0.22; } }

/* Caption rail: the engine narrating its own decisions, in its own voice.
   It keeps a fixed height while a run is playing, because a rail that
   collapses when it has nothing to say moves every row beneath it. */
.panel__caption {
  display: flex;
  align-items: center;
  min-height: 38px;
  padding: 9px var(--s5);
  border-bottom: 1px solid var(--line-panel);
  background: var(--panel-sunken);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.45;
  color: var(--blue-on-panel);
}
.panel__caption:empty { display: none; }

.panel__body { padding: var(--s2) var(--s5) var(--s4); }
.panel__foot { padding: 0 var(--s5) var(--s4); display: flex; justify-content: flex-end; gap: var(--s2); }

/* Utility: a dark region that is not a full panel (an inset, a strip).
   Adopts every panel-aware component style. */
.on-panel { background: var(--panel); color: var(--on-panel); }

/* Readout: a single instrument value with its label and, optionally, a meter.
   Used for the verdict strip and any other headline number on the panel. */
.readout {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s3) var(--s5);
  border-top: 1px solid var(--line-panel);
  background: var(--panel-sunken);
}
.readout__value {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: var(--wdth-display);
  letter-spacing: var(--track-tight);
  font-size: 20px;
  line-height: 20px;
  color: var(--on-panel);
  /* Two-word verdicts break across the strip on a narrow panel; balanced, they
     break in the middle rather than leaving one word stranded. */
  text-wrap: balance;
}

/* ---------------------------------------------------------------------------
   10. THE POST -- the atomic feed unit
   ---------------------------------------------------------------------------
   Layout: avatar rail on the left, content on the right. The rail continues
   downward as a thread line whenever the post has replies beneath it -- this
   is both the X thread convention and the protocol's signal trace.

   Structure:
     .post[.post--stimulus][.post--hot][data-depth="0|1|2"]
       .post__rail   > .avatar  (+ .post__thread when the post has children)
       .post__main
         .post__wire  > .post__clock .post__form .stamp
         .post__ident > .post__name .post__handle
         .post__body
         .post__quote            (optional)
         .divergence             (optional -- see 11)
*/

/* Every post carries the intensity rail's gutter, whether or not it is hot.
   If only hot posts reserved it, the avatar column would step 14px sideways
   on every high-intensity row and the feed would read as ragged. */
.post {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  gap: var(--s3);
  padding: var(--s4) 0 var(--s3) var(--s3);
  border-left: 2px solid transparent;
  border-bottom: 1px solid var(--line-panel);
}
.post:last-child { border-bottom: 0; }

.post__main { min-width: 0; }
.post__rail { display: flex; flex-direction: column; align-items: center; gap: var(--s2); }
.post__thread { flex: 1 1 auto; width: 1px; background: var(--line-panel); min-height: var(--s3); }

.post__wire {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--on-panel-faint);
  margin-bottom: var(--s2);
}
.post__clock { color: var(--blue-on-panel); }
.post__form { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The stamp sits with the clock and the form, not flung to the far edge:
   at panel width the row would otherwise break into two unrelated halves
   with a void between them. Same size, same prominence, one unit. */
.post__wire .stamp { margin-right: auto; }

.post__ident { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s1); }
.post__name { font-weight: 700; font-size: 14.5px; letter-spacing: var(--track-tight); }
.post__handle { font-family: var(--font-mono); font-size: 12px; color: var(--on-panel-faint); }

.post__body {
  font-size: 15px;
  line-height: 1.5;
  color: var(--on-panel);
  max-width: var(--measure-tight);
  text-wrap: pretty;
}

.post__quote {
  margin-top: var(--s3);
  border: 1px solid var(--line-panel);
  padding: var(--s2) var(--s3);
  font-size: 13px;
  color: var(--on-panel-muted);
  max-width: var(--measure-tight);
}

/* Depth: replies indent under their parent. Two levels, then it flattens --
   deeper nesting is unreadable on a phone and adds no information. */
.post[data-depth="1"] { margin-left: var(--s6); }
.post[data-depth="2"] { margin-left: calc(var(--s6) * 2); }

/* Intensity marker. Heat is reserved for exactly this, and it only colours
   the rail every post already reserves -- it never moves the content. */
.post--hot { border-left-color: var(--heat); }

/* The stimulus: what the operator asked, not what the engine produced. */
.post--stimulus {
  background: var(--panel-raised);
  border: 1px solid var(--line-panel);
  border-left: 2px solid var(--blue-on-panel);
  padding: var(--s4) var(--s4) var(--s4) var(--s3);
  margin-bottom: var(--s2);
}
.post--stimulus .post__body { font-size: 16.5px; font-weight: 500; }
/* An event has no author. Rendering it with an avatar and a handle makes the
   wire look like a person, which is the one thing it is not. */
.post--wire { grid-template-columns: minmax(0, 1fr); }
.post--wire .post__rail { display: none; }

/* Entrance. JS adds .is-landed one frame after insertion. */
.post { opacity: 0; transform: translateY(8px); }
.post.is-landed {
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out);
}

/* Focus. While a run is playing exactly one post carries the beat and the
   rest recede, so the eye never has to search for the new information.
   Declared after .is-landed on purpose: same specificity, later wins. */
.post.is-recede { opacity: 0.45; }
/* Except the scenario. Every reaction is a reaction to it, so it is the one
   row that has to stay readable for the whole run. */
.post--stimulus.is-recede { opacity: 1; }

/* Deferred action. On the run's one reasoning beat the post arrives as an
   identity and its reasoning; the public action is appended after, so the
   reader sees the causal order rather than a wall. */
.post__reveal { opacity: 0; transform: translateY(6px); }
.post__reveal.is-shown {
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out);
}

/* The cast, stated once, before the first reaction. Where these voices came
   from is the answer a stranger needs first, so it is a row of the feed
   rather than a caption about it. No names: names appear only on a
   stamped post. */
.assemble {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s4) 0 var(--s4) var(--s3);
  border-bottom: 1px solid var(--line-panel);
  opacity: 0;
}
.assemble.is-landed { opacity: 1; transition: opacity var(--dur-3) var(--ease-out); }
.assemble__what {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.45;
  color: var(--on-panel-muted);
}
.assemble__more {
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  font-weight: 600;
  letter-spacing: var(--track-wide);
  color: var(--on-panel-faint);
  margin-left: calc(var(--s2) * -1);
}

/* Typing indicator: occupies the slot the next post will fill, so the feed
   does not jump when it lands. */
.typing {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4) 0;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--on-panel-faint);
  min-height: 46px;
}
.typing__dots { display: inline-flex; gap: 4px; }
.typing__dots i { width: 5px; height: 5px; background: var(--blue-on-panel); }
.typing__dots i:nth-child(2) { animation-delay: 0.18s; }
.typing__dots i:nth-child(3) { animation-delay: 0.36s; }
.typing__dots i { animation: lamp-pulse 1s var(--ease-in-out) infinite; }

/* ---------------------------------------------------------------------------
   11. THE DIVERGENCE BLOCK -- modeled reasoning, and the seam
   ---------------------------------------------------------------------------
   The product's signature artifact. Renders ONLY when the payload carries a
   reasoning field; never fabricate one in the view layer.

   FIVE RULES. All five are load-bearing; none is stylistic.

   1. ORDER. Reasoning renders BEFORE .post__body. That is the causal order in
      the engine (an agent reasons, then acts) and it is what produces the beat:
      he thought this ... and posted that.
   2. COLOUR. Reasoning is BLUE, never heat. Heat means intensity and only
      intensity, and .post--hot already spends it on the same post. Magnitude is
      carried by gauge length, never by hue. Gold is reserved for silence.
   3. REGISTER. Reasoning is mono at 12.5px; public posts are Archivo at 15px.
      A reader tells the two apart peripherally, before reading a label.
   4. GATING. data-tier decides what stays open once the action has landed.
      The CLIENT sets the attribute it is given; the STYLESHEET decides
      openness. Thresholds are tuned server-side without a frontend deploy.
   5. NO QUOTATION MARKS around reasoning text. Anywhere. Ever. A fabricated
      private belief attributed to a named real person is heavier exposure than
      a fabricated post, and quote marks read as verbatim.

   Structure:
     .divergence[data-tier][.is-settled][.is-open]
       .divergence__head  > .microlabel .stamp
       .divergence__body  > the reasoning, mono, no quote marks
       .divergence__seam  > .divergence__toggle  .gauge

   Lifecycle: rendered open while the node reasons; the client adds
   .is-settled once the public action lands. From there the tier decides:
   aligned and softened fold into the seam, holding_back and withheld stay
   open. The seam is clickable both ways, permanently; collapse never destroys.
   An ancestor with [data-xray="on"] opens every block at once. */

.divergence {
  margin: var(--s2) 0 var(--s3);
  border: 1px solid var(--line-panel);
  border-left: 2px solid var(--blue-on-panel);
  max-width: var(--measure-tight);
}
.divergence__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s2);
  padding: 7px var(--s3);
  border-bottom: 1px solid var(--line-panel);
  background: var(--panel-sunken);
}
.divergence__head .microlabel { color: var(--blue-on-panel); }

.divergence__body {
  padding: var(--s3);
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--on-panel-muted);
  /* A rambling reasoning block must not be able to dominate the feed. */
  max-height: 12em;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* While the node is still reasoning it is the only thing on screen, and it is
   there to be read: clamping it there would clip the one beat that needs to
   land whole. The clamp applies from the moment the action settles. */
.divergence:not(.is-settled) .divergence__body { max-height: none; }

/* The seam: a labeled hairline that names what the figure did with the
   reasoning above it. Present whether the block is open or folded, so a
   folded block is still a statement rather than an absence. */
.divergence__seam {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: 6px var(--s3);
  border-top: 1px solid var(--line-panel);
  background: var(--panel-sunken);
}
/* A folded block is still a statement, but it is a footnote, not a component.
   Most figures in most runs post as they thought; if that outcome renders as
   a bordered, filled bar it lands on every row with the same weight as the
   posts, and then the rows where something WAS held back stop reading as
   remarkable. Folded: a hairline caption. Open: a component. That contrast
   is the whole point of gating. */
.divergence.is-settled[data-tier="aligned"],
.divergence.is-settled[data-tier="softened"] {
  border: 0;
  margin: 0 0 var(--s2);
}
.divergence.is-settled[data-tier="aligned"] > .divergence__seam,
.divergence.is-settled[data-tier="softened"] > .divergence__seam {
  border-top: 0;
  background: none;
  padding: 0;
  gap: var(--s2);
}
.divergence.is-settled[data-tier="aligned"] > .divergence__seam > .gauge,
.divergence.is-settled[data-tier="softened"] > .divergence__seam > .gauge {
  margin-left: var(--s1);
}
.divergence.is-settled[data-tier="aligned"] > .divergence__seam > .gauge i,
.divergence.is-settled[data-tier="softened"] > .divergence__seam > .gauge i {
  width: 10px;
  height: 4px;
}

/* Re-opened, by the reader or by X-RAY: it is a component again. */
.divergence.is-settled.is-open,
[data-xray="on"] .divergence.is-settled {
  border: 1px solid var(--line-panel);
  border-left: 2px solid var(--blue-on-panel);
  margin: var(--s2) 0 var(--s3);
}
.divergence.is-settled.is-open > .divergence__seam,
[data-xray="on"] .divergence.is-settled > .divergence__seam {
  border-top: 1px solid var(--line-panel);
  background: var(--panel-sunken);
  padding: 6px var(--s3);
  gap: var(--s3);
}
.divergence.is-settled.is-open > .divergence__seam > .gauge i,
[data-xray="on"] .divergence.is-settled > .divergence__seam > .gauge i {
  width: 14px;
  height: 6px;
}

.divergence__toggle {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--on-panel-faint);
  text-align: left;
  padding: 2px 0;
  transition: color var(--dur-1) var(--ease-out),
              transform var(--dur-1) var(--ease-out);
}
.divergence__toggle:hover { color: var(--blue-on-panel); }
.divergence__toggle:active { transform: translateY(1px); }
.divergence[data-tier="holding_back"] .divergence__toggle { color: var(--blue-on-panel); }
.divergence[data-tier="withheld"] .divergence__toggle { color: var(--caution); }
.divergence[data-tier="withheld"] { border-left-color: var(--caution); }

/* Folded state: tier decides, the stylesheet enforces.
   Selector order below IS the order of authority -- tier, then the reader's
   own click, then X-RAY. Each layer is written at a specificity that beats
   the one above it; do not reorder or simplify these selectors. */
.divergence.is-settled[data-tier="aligned"] > .divergence__head,
.divergence.is-settled[data-tier="aligned"] > .divergence__body,
.divergence.is-settled[data-tier="softened"] > .divergence__head,
.divergence.is-settled[data-tier="softened"] > .divergence__body { display: none; }

/* The reader closed it. */
.divergence.is-closed > .divergence__head,
.divergence.is-closed > .divergence__body,
.divergence.is-settled.is-closed > .divergence__head,
.divergence.is-settled.is-closed > .divergence__body { display: none; }

/* The reader opened it. */
.divergence.is-open > .divergence__body,
.divergence.is-settled.is-open > .divergence__body { display: block; }
.divergence.is-open > .divergence__head,
.divergence.is-settled.is-open > .divergence__head { display: flex; }

/* X-RAY opens everything, whatever the tier or the reader chose. */
[data-xray="on"] .divergence > .divergence__body,
[data-xray="on"] .divergence.is-settled > .divergence__body,
[data-xray="on"] .divergence.is-closed > .divergence__body,
[data-xray="on"] .divergence.is-settled.is-closed > .divergence__body { display: block; }
[data-xray="on"] .divergence > .divergence__head,
[data-xray="on"] .divergence.is-settled > .divergence__head,
[data-xray="on"] .divergence.is-closed > .divergence__head,
[data-xray="on"] .divergence.is-settled.is-closed > .divergence__head { display: flex; }

/* Silence is an outcome, not an absence: it gets a block of its own. */
.post--silent .post__body { display: none; }
.post__silent {
  font-family: var(--font-mono);
  font-size: 12.5px;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--caution);
  border: 1px dashed var(--line-panel);
  padding: var(--s3);
  max-width: var(--measure-tight);
}

/* Paired mode: reasoning and post side by side, both labeled. For deliberate
   reading only -- the report, or a marketing page. Never the live feed, where
   it doubles the reading load on every row and buries the rows that matter. */
.divergence--paired { border-left-width: 2px; }
.divergence--paired .divergence__row {
  padding: var(--s3);
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--s3);
}
.divergence--paired .divergence__row + .divergence__row { border-top: 1px solid var(--line-panel); }
.divergence__key {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--on-panel-faint);
  padding-top: 2px;
  white-space: nowrap;
}
.divergence__row--private .divergence__key { color: var(--blue-on-panel); }
.divergence__row--private .divergence__val {
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--on-panel-muted);
}
.divergence__row--public .divergence__val { font-size: 14.5px; line-height: 1.5; color: var(--on-panel); }

/* Gauge: how far the public action sits from the reasoning. Blue, and
   magnitude is segment count -- never a second hue. */
.gauge { display: inline-flex; gap: 3px; margin-left: auto; flex: none; }
.gauge i { width: 14px; height: 6px; background: var(--line-panel); }
.gauge i.is-on { background: var(--blue-on-panel); }
.divergence[data-tier="withheld"] .gauge i.is-on { background: var(--caution); }

/* X-RAY: one control opens every block in a feed. The honest answer to
   "are you only showing me the dramatic ones?" */
.xray-toggle[aria-pressed="true"] { border-color: var(--blue-on-panel); color: var(--blue-on-panel); }

/* ---------------------------------------------------------------------------
   12. METER -- discrete intensity, 3 segments
   ---------------------------------------------------------------------------
   Level 1 neutral, level 2 amber, level 3 heat. A meter always shows all
   segments so the maximum is legible. */

.meter { display: inline-flex; gap: 4px; vertical-align: middle; }
.meter i { width: 26px; height: 9px; background: var(--line); }
.meter i.is-on { background: var(--heat); }
.meter[data-level="1"] i.is-on { background: var(--ink-faint); }
.meter[data-level="2"] i.is-on { background: #d98a1c; }
.meter--sm i { width: 16px; height: 6px; }
.panel .meter i, .on-panel .meter i { background: var(--line-panel); }
.panel .meter i.is-on, .on-panel .meter i.is-on { background: var(--heat); }

/* Lean tag: the direction of sentiment, as a bordered mono tag. */
.lean {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 5px 10px;
  border: 1px solid var(--ink);
}
.lean--hostile    { border-color: var(--heat-ink); color: var(--heat-ink); }
.lean--supportive { border-color: var(--ok); color: var(--ok); }

/* ---------------------------------------------------------------------------
   13. AVATAR
   ---------------------------------------------------------------------------
   The only round object in the system. Fixed box, so a failed image can
   never move the layout: initials sit underneath and show through. */

.avatar {
  width: 40px; height: 40px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  background: #4a4a55;
  user-select: none;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 30px; height: 30px; font-size: 10px; }
.avatar--xs { width: 26px; height: 26px; font-size: 9px; }

/* Overlapping cluster: "the panel", as a group. */
.avatar-stack { display: flex; align-items: center; }
.avatar-stack .avatar { margin-left: -8px; box-shadow: 0 0 0 2px var(--paper-raised); }
.avatar-stack .avatar:first-child { margin-left: 0; }
/* The ring is a knockout in whatever surface the stack sits on, not a shadow. */
.panel .avatar-stack .avatar,
.on-panel .avatar-stack .avatar { box-shadow: 0 0 0 2px var(--panel); }

/* ---------------------------------------------------------------------------
   14. SKELETON -- loading placeholder
   --------------------------------------------------------------------------- */

.skeleton { background: var(--line-panel); animation: skeleton 1.4s var(--ease-in-out) infinite; }
.skeleton--line { height: 10px; margin-bottom: var(--s2); }
@keyframes skeleton { 50% { opacity: 0.35; } }

/* ---------------------------------------------------------------------------
   15. ORCHESTRATED ENTRANCE
   ---------------------------------------------------------------------------
   Used once, at the top of a surface. Stagger with --rise-delay. */

.rise {
  opacity: 0;
  transform: translateY(12px);
  animation: rise var(--dur-4) var(--ease-out) var(--rise-delay, 0s) forwards;
}
@keyframes rise { to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------------------
   16. THE SHEET -- a modal that takes exactly one action
   ---------------------------------------------------------------------------
   For an action small enough to fit in one field. The reader stays where they
   are, does the thing, and the outcome resolves inside the sheet: no scroll,
   no second page, no hunting for the form they were promised. A control that
   sends someone down the page to find its own field is a page journey, and a
   page journey loses the person who was already ready to act.

   Native <dialog>, opened with showModal(): the top layer, the focus trap,
   Escape, the inert background and returning focus to the trigger are then the
   browser's job rather than ours, and they are right in every browser at once.

   No shadow and no radius, like everything else here. Separation from the page
   is the backdrop and the structural border -- the same hairline vocabulary as
   .well, because a sheet IS a well that arrived on top. */

.sheet {
  width: min(520px, calc(100vw - 2 * var(--gutter)));
  max-height: min(88vh, 720px);
  /* The reset in sec 2 zeroes every margin, including the `margin: auto` a
     browser uses to centre a modal dialog. Put it back here, or the sheet
     opens in the top-left corner. */
  margin: auto;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--paper-raised);
  color: var(--ink);
  overflow: auto;
  overscroll-behavior: contain;
}
.sheet::backdrop { background: rgba(14, 15, 18, 0.62); }
/* The resting state is the legible one: the animation runs only on the way in,
   and the reduced-motion block below collapses it to nothing. */
.sheet[open] { animation: sheet-in var(--dur-3) var(--ease-out); }
@keyframes sheet-in { from { opacity: 0; transform: translateY(10px); } }

.sheet__wire {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s5);
  border-bottom: 1px solid var(--line);
  background: var(--paper-sunken);
}
.sheet__body { padding: var(--s5); }
.sheet__body > h2 { font-size: var(--size-h3); }
.sheet__body > p {
  margin-top: var(--s3);
  font-size: var(--size-small);
  line-height: var(--lh-body);
  color: var(--ink-muted);
  max-width: var(--measure-tight);
}

/* One field, one button. They sit on one row where there is room and stack
   where there is not; the button never narrows below its own label. */
.sheet__form { margin-top: var(--s5); }
.sheet__row { display: flex; gap: var(--s2); }
.sheet__row input { flex: 1 1 auto; min-width: 0; }
.sheet__row .btn { flex: none; }

@media (max-width: 460px) {
  .sheet__row { flex-direction: column; }
  .sheet__row .btn { width: 100%; }
}

/* ---------------------------------------------------------------------------
   17. REDUCED MOTION
   ---------------------------------------------------------------------------
   Every animated element must have a resting state that is fully legible
   with animation removed. Entrance animations resolve to their end state. */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
  .rise, .post, .verdict, .assemble, .post__reveal {
    opacity: 1 !important;
    transform: none !important;
  }
  .lamp--live { opacity: 1; }
}
