/* web/v2.css — the V2 skin.
 *
 * A committed dark instrument panel. Every colour is painted explicitly rather
 * than inherited, and there is no light variant: this is a control surface for
 * one person, used at a desk and in a car, and a theme switch would be a
 * feature nobody asked for.
 *
 * MOBILE FIRST, and not as a slogan — the base rules below ARE the phone
 * layout. The orb is the primary control there: thumb-reachable, tap to wake,
 * and the only thing above the fold. Desktop is one breakpoint that puts the
 * conversation beside it instead of beneath it.
 *
 * Nothing here changes what any screen DOES. Every tab from V1 still exists and
 * still calls the same renderer; the generators moved from tabs to panels on
 * the home view because a status you glance at is a panel, and a status you
 * interrogate is a page — they are now both.
 */

:root {
  /* ---------------------------------------------------------------- ground */
  /* Warm near-black. A red-biased dark reads as a lit room rather than as a
     screen, and it is what makes a pooled highlight look like a lamp. */
  --void: #0B0908;
  --deep: #14100D;
  --raise: #1C1713;
  --leather: #241C16;

  /* Surfaces are lit leather, not glass: a warm top-light, a brass hairline,
     and an inner highlight so the edge has thickness. */
  --glass: rgba(214, 192, 155, 0.045);
  --glass-lit: rgba(214, 192, 155, 0.075);
  --edge: rgba(188, 161, 115, 0.14);
  --edge-lit: rgba(214, 192, 155, 0.30);

  --text: #EDE2D2;
  --dim: #A2907A;
  --faint: #6B5C4B;

  /* -------------------------------------------------------------- ambient */
  /* THE CHROMA CEILING IS 0.072 (oklch), and it is load-bearing rather than
     stylistic. Recording sits at 0.188 — 2.6x the most saturated thing it can
     ever appear beside — and that ratio is the whole reason an amber room can
     still have an unmistakable amber recording light. Nothing ambient may
     cross the ceiling. Measure before adding a colour here. */
  --brass: #BCA173;    /* L .72  C .070  H  81 */
  --copper: #8C6142;   /* L .53  C .072  H  56 */
  --tan: #D6C09B;      /* L .82  C .055  H  81 */
  --lamp: #FFEFD6;     /* L .96  C .037  H  79 — the bulb at full */

  /* -------------------------------------------------------------- signals */
  --rec: #FF7A0F;      /* L .72  C .188  H  50 — RECORDING. the only saturated warm */
  --alarm: #FF3355;    /* L .65  C .235  H  19 — NOT RECORDING. loudest thing there is */
  --halt: #6FA8FF;     /* L .73  C .141  H 259 — deliberately held: kill switch, preamble */
  --ok: #9BC471;       /* L .77  C .119  H 130 */
  --dead: #B33A4E;     /* L .50  C .150  H  16 — a failure, deliberately quieter than the alarm */
  --ash: #8A7F73;      /* L .60  C .022  H  70 — degraded: the fire gone out */

  /* Triplets, so a translucent wash never hardcodes a hex. One place to change
     a colour, which the 32 rgba() literals this replaces did not have. */
  --rec-rgb: 255 122 15;
  --alarm-rgb: 255 51 85;
  --halt-rgb: 111 168 255;
  --ok-rgb: 155 196 113;
  --dead-rgb: 179 58 78;
  --brass-rgb: 188 161 115;
  --copper-rgb: 140 97 66;
  --tan-rgb: 214 192 155;
  --lamp-rgb: 255 239 214;

  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --ui: "Barlow", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;

  /* ------------------------------------------------------------ the scales */
  /* One spacing scale, used everywhere. Ad-hoc margins are what made the old
     screens read as serviceable: nothing lined up because nothing shared a
     unit. */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1.15rem;
  --s5: 1.75rem;
  --s6: 2.75rem;
  --s7: 4rem;

  /* And one type scale. Hierarchy is the point — a page where everything is
     0.85rem has no hierarchy, only text. */
  --t-eyebrow: 0.68rem;
  --t-micro: 0.72rem;
  --t-small: 0.82rem;
  --t-body: 0.94rem;
  --t-lead: 1.05rem;
  --t-title: 1.25rem;
  --t-display: 1.6rem;

  --r-lg: 20px;
  --r-md: 14px;
  --r-sm: 9px;

  /* The phone's home bar and notch. */
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--void);
  color: var(--text);
  font-family: var(--ui);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overflow-x: hidden;
}

/* Two soft washes so the ground has depth without a gradient that reads as a
   hero. They sit behind everything and never intercept a tap. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(52rem 36rem at 12% -10%, rgb(var(--rec-rgb) / 0.05), transparent 62%),
    radial-gradient(44rem 32rem at 96% 6%, rgb(var(--brass-rgb) / 0.045), transparent 60%),
    radial-gradient(64rem 46rem at 50% 106%, rgb(var(--copper-rgb) / 0.05), transparent 66%);
}

/* Grain and a vignette, so the dark between the pools is genuinely dark rather
   than a flat fill. One inline SVG turbulence tiled at 120px and a corner
   falloff — no asset, no request, and it composites on the GPU. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.032;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
}
.shell::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(120% 100% at 50% 45%, transparent 42%, rgb(0 0 0 / 0.5) 100%);
}

/* ============================================================== primitives */

.glass {
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--glass), rgb(var(--tan-rgb) / 0.012));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
}

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.dim { color: var(--dim); }
.hidden { display: none !important; }

h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.02em; margin: 0; }
h2 {
  font-size: 0.92rem; font-weight: 600; letter-spacing: 0.01em;
  margin: 0 0 0.85rem; color: var(--text);
}
p { margin: 0 0 0.7rem; }

a { color: var(--brass); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Focus is visible everywhere. A control surface used one-handed needs it. */
:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; border-radius: 4px; }

/* ================================================================== forms */

label {
  display: block;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0.85rem 0 0.3rem;
}

input[type=text], input[type=search], input[type=number],
input[type=month], input[type=password], select, textarea {
  width: 100%;
  padding: 0.62rem 0.75rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--edge);
  background: rgba(0, 0, 0, 0.28);
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
}
input:focus, select:focus, textarea:focus { border-color: var(--edge-lit); }
input.code {
  font-family: var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
textarea { resize: vertical; min-height: 2.8rem; }

button {
  font-family: var(--ui);
  font-size: 0.88rem;
  font-weight: 500;
  padding: 0.55rem 1.05rem;
  margin-top: 0.75rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--edge);
  background: var(--glass-lit);
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, color 0.15s;
  /* Comfortable for a thumb without looking oversized on a laptop. */
  min-height: 2.6rem;
}
button:hover { border-color: var(--edge-lit); background: rgb(var(--tan-rgb) / 0.07); }
button:active { transform: translateY(1px); }
button.primary {
  background: var(--brass);
  border-color: var(--brass);
  color: #241C16;
  font-weight: 600;
}
button.primary:hover { background: var(--tan); border-color: var(--tan); }
button.danger { color: var(--dead); border-color: rgb(var(--alarm-rgb) / 0.32); background: rgb(var(--alarm-rgb) / 0.06); }
button.ghost { background: transparent; }

.status { margin-top: 0.6rem; font-size: 0.85rem; color: var(--dim); min-height: 1.3em; }
.status.ok { color: var(--ok); }
.status.bad { color: var(--dead); }

/* ================================================================ badges */

.badge {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border-radius: 5px;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  background: rgb(var(--tan-rgb) / 0.06);
  color: var(--dim);
  border: 1px solid transparent;
  vertical-align: baseline;
}
.badge.ok { color: var(--ok); border-color: rgb(var(--ok-rgb) / 0.25); background: rgb(var(--ok-rgb) / 0.08); }
.badge.bad { color: var(--dead); border-color: rgb(var(--alarm-rgb) / 0.28); background: rgb(var(--alarm-rgb) / 0.08); }
.badge.warn { color: var(--brass); border-color: rgb(var(--rec-rgb) / 0.26); background: rgb(var(--rec-rgb) / 0.08); }

.banner {
  border-radius: var(--r-md);
  padding: 0.75rem 0.95rem;
  margin-bottom: 0.85rem;
  font-size: 0.87rem;
  border: 1px solid rgb(var(--rec-rgb) / 0.3);
  background: rgb(var(--rec-rgb) / 0.07);
  color: var(--brass);
}
/* The §12.2.1 case. A meeting the room believes is recording and is not has to
   be the loudest thing on the screen. */
.banner.alarm {
  border-color: rgb(var(--alarm-rgb) / 0.55);
  background: rgb(var(--alarm-rgb) / 0.12);
  color: var(--alarm);
  font-weight: 600;
}

/* ================================================================== shell */

.shell {
  position: relative;
  z-index: 1;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  /* Room for the nav bar that is fixed to the bottom on a phone. */
  padding: calc(var(--safe-t) + 0.9rem) 0.9rem calc(5.5rem + var(--safe-b));
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 0.9rem;
}
.topbar .who { font-family: var(--mono); font-size: 0.72rem; color: var(--dim); }

/* ==================================================================== orb */

.orb-stage {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  /* Sized so the orb owns the phone's first screen without pushing the
     conversation entirely below the fold. */
  width: min(78vw, 20rem);
  aspect-ratio: 1;
  margin: 0.5rem auto 0;
}

/* A real button, so it is reachable by keyboard and announced by a screen
   reader. The canvas is decoration painted inside it. */
.orb-button {
  all: unset;
  /* `all: unset` does NOT remove the native control appearance on iOS Safari.
     That is why the orb showed as a flat grey disc on the phone and as nothing
     at all on desktop: the canvas was transparent, and iOS was drawing its own
     button underneath it. These four have to be explicit. */
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;

  position: relative;
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  cursor: pointer;
  border-radius: 50%;
  /* Above the page wash, below the nav. Without its own stacking context the
     fixed nav composited over the canvas on iOS. */
  z-index: 2;
  /* A long press must not raise the iOS selection or callout menus. */
  -webkit-touch-callout: none;
  user-select: none;
  -webkit-user-select: none;
}
.orb-button canvas { width: 100%; height: 100%; display: block; }
.orb-button:focus-visible { outline: 2px solid var(--brass); outline-offset: 6px; }

.orb-caption {
  position: absolute;
  left: 50%;
  bottom: -0.2rem;
  transform: translateX(-50%);
  text-align: center;
  pointer-events: none;
  white-space: nowrap;
}
.orb-state {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}
.orb-hint { font-size: 0.78rem; color: var(--faint); }

/* ========================================================== conversation */

.transcript, .stream {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  max-height: 26rem;
  overflow-y: auto;
  padding-right: 0.2rem;
  /* Momentum scrolling on iOS without a scrollbar that fights the glass. */
  scrollbar-width: thin;
  scrollbar-color: var(--edge) transparent;
}
.stream::-webkit-scrollbar, .transcript::-webkit-scrollbar { width: 6px; }
.stream::-webkit-scrollbar-thumb,
.transcript::-webkit-scrollbar-thumb { background: var(--edge); border-radius: 3px; }

.msg {
  border-radius: var(--r-md);
  padding: 0.6rem 0.75rem;
  font-size: 0.92rem;
  border: 1px solid transparent;
}
/* Josh's turns lean cyan and sit right; hers are the neutral surface. The
   asymmetry does the work a name label would otherwise have to. */
.msg.josh {
  background: rgb(var(--brass-rgb) / 0.07);
  border-color: rgb(var(--brass-rgb) / 0.16);
  margin-left: 1.5rem;
}
.msg.friday { background: rgb(var(--tan-rgb) / 0.028); border-color: var(--edge); margin-right: 1.5rem; }
.msg.pending { opacity: 0.6; font-style: italic; }

.msg-meta {
  display: flex;
  gap: 0.45rem;
  align-items: baseline;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.25rem;
}
.msg.josh .msg-meta > span:first-child { color: var(--brass); }
.msg-body p { margin: 0.3rem 0; }
.msg-body code {
  background: rgba(0, 0, 0, 0.35);
  padding: 0.05rem 0.3rem;
  border-radius: 4px;
  font-family: var(--mono);
  font-size: 0.84rem;
}
.msg-list { margin: 0.3rem 0; padding-left: 1.1rem; }

.composer { display: flex; gap: 0.5rem; align-items: flex-end; margin-top: 0.8rem; }
.composer textarea { flex: 1; margin: 0; }
.composer button { margin: 0; flex-shrink: 0; }

/* ========================================================= live meeting */
/*
 * While a meeting is open this is the most important thing on the screen, and
 * it earns that by being EVIDENCE rather than a status light: the lines the
 * system actually captured. A recording meeting and a dead one used to look
 * identical, which is a design failure rather than a missing feature.
 */

.meeting-panel {
  border-color: rgb(var(--rec-rgb) / 0.34);
  background: linear-gradient(180deg, rgb(var(--rec-rgb) / 0.06), rgb(var(--tan-rgb) / 0.012));
  margin-bottom: 0.9rem;
}

.meeting-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.7rem; }
.meeting-head h2 { color: var(--rec); letter-spacing: 0.04em; text-transform: uppercase; font-size: 0.78rem; }

/* The one piece of decoration that is load-bearing: a recording light that is
   only on while lines are actually arriving. */
.rec-dot {
  width: 0.5rem; height: 0.5rem; border-radius: 50%;
  background: var(--rec); flex: 0 0 auto;
  box-shadow: 0 0 10px rgb(var(--rec-rgb) / 0.7);
  animation: rec-pulse 2s ease-in-out infinite;
}
/* Calm. Recording is the normal state of a meeting and should not look like an
   emergency — the tempo IS the signal, and a slow one says "working". */
@keyframes rec-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.42; transform: scale(0.88); } }
/* Three times faster, and a hard step rather than an ease. Tempo is what reads
   first across a room, before hue and before shape. */
@keyframes rec-strobe { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0.15; } }

.meeting-tally {
  margin-left: auto;
  font-family: var(--mono); font-size: 0.72rem; color: var(--dim);
}
.meeting-tally b { color: var(--rec); font-size: 0.95rem; font-variant-numeric: tabular-nums; }

.meeting-people { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.7rem; }

.meeting-lines {
  max-height: 13rem;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 0.35rem;
  font-size: 0.86rem;
  scrollbar-width: thin;
  scrollbar-color: var(--edge) transparent;
}
.meeting-lines::-webkit-scrollbar { width: 5px; }
.meeting-lines::-webkit-scrollbar-thumb { background: var(--edge); border-radius: 3px; }

.meeting-line { display: flex; gap: 0.5rem; align-items: baseline; }
.meeting-line.friday { color: var(--copper); }
.meeting-who {
  font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.06em;
  color: var(--faint); flex: 0 0 auto; min-width: 4.2rem;
  text-transform: uppercase;
}
.meeting-line.friday .meeting-who { color: var(--copper); opacity: 0.75; }

/* Nothing is arriving. The panel says it as loudly as the banner does — a
   stalled record is exactly as informative as an alarm, and it is right where
   he is already looking. */
/* THE LIGHT GOES OUT. In a room lit warm, the alarm is not another warm colour
   — it is the warmth being extinguished. This is the only cold surface in the
   interface, and the hazard hatch means it still reads with no colour at all. */
.meeting-panel.stalled {
  position: relative;
  overflow: hidden;
  border-color: rgb(var(--alarm-rgb) / 0.55);
  background: linear-gradient(180deg, rgb(var(--alarm-rgb) / 0.1), rgb(10 14 22 / 0.55));
  box-shadow: inset 0 0 44px -12px rgb(var(--alarm-rgb) / 0.35);
}
.meeting-panel.stalled::after {
  content: "";
  position: absolute; inset: 0; pointer-events: none; opacity: 0.5;
  background: repeating-linear-gradient(
    45deg, transparent 0 9px, rgb(var(--alarm-rgb) / 0.1) 9px 18px);
}
.meeting-panel.stalled > * { position: relative; z-index: 1; }
.meeting-panel.stalled .meeting-head h2 { color: var(--alarm); }
.meeting-panel.stalled .meeting-head h2::after { content: " — stopped"; }
.meeting-panel.stalled .rec-dot {
  background: transparent; box-shadow: none;
  border: 2px solid var(--alarm);
  animation: rec-strobe 0.7s steps(1, end) infinite;
}
.meeting-panel.stalled .meeting-tally b { color: var(--alarm); }

/* Mid-ritual: open, but not yet keeping anything. Amber says "recording", red
   says "stopped", and neither of those is true during the consent window — so
   the preamble gets its own, quieter, state rather than borrowing one. */
.meeting-panel.preamble {
  border-color: rgb(var(--halt-rgb) / 0.34);
  background: linear-gradient(180deg, rgb(var(--halt-rgb) / 0.06), rgb(var(--tan-rgb) / 0.012));
}
.meeting-panel.preamble .meeting-head h2 { color: var(--halt); }
.meeting-panel.preamble .meeting-head h2::after { content: " — starting up"; }
.meeting-panel.preamble .rec-dot { background: var(--halt); animation: none; }
.meeting-panel.preamble .meeting-tally b { color: var(--halt); }

/* An item past its date says so in words as well as colour — the count in the
   briefing is spoken, and this is the same fact for the eye. */
.due { color: var(--dim); }
.due.late { color: var(--dead); font-weight: 600; }
li.overdue { border-left: 2px solid var(--dead); padding-left: 0.5rem; }

/* The per-company view: what she loads, in the order she loads it. */
/* ============================================================ briefs (V3)
 *
 * A LIBRARY, not a form. It was a form: every document carried a date input, a
 * title input, Save and Retire inline, repeated down the page — so the resting
 * state of the screen was "editing everything", and the titles were impossible
 * to read down.
 *
 * Two sections also showed the same documents twice, once in load order inside
 * a <details> and once flat with the forms. They are one shelf now, in load
 * order, with the conflict rule visible in the primary list rather than hidden.
 *
 * The rhythm is deliberate: DENSE where he is scanning (the rows), AIRY where
 * he is reading or changing something (the open editor, the notes).
 */

.briefs { display: flex; flex-direction: column; gap: var(--s6); }

.page-head { display: flex; flex-direction: column; gap: var(--s2); }
.page-head h1 { font-size: var(--t-display); line-height: 1.1; }
.page-stat { font-size: var(--t-small); color: var(--dim); }
.page-stat .sep, .shelf-stat .sep { color: var(--faint); margin: 0 0.15rem; }

.attention { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s1); }
.att {
  font-family: var(--mono); font-size: var(--t-micro);
  padding: 0.15rem 0.5rem; border-radius: 5px;
  border: 1px solid currentColor;
}
.att.stale { color: var(--rec); }
.att.undated { color: var(--brass); }
.att.gap { color: var(--halt); }

/* ---- the shelves ---- */

.shelves { display: flex; flex-direction: column; gap: var(--s5); }

.shelf-head {
  display: flex; flex-direction: column; gap: 0.1rem;
  padding-bottom: var(--s2); margin-bottom: var(--s2);
  border-bottom: 1px solid var(--edge);
}
.shelf-head h3 {
  font-size: var(--t-lead); font-weight: 600; margin: 0;
  color: var(--tan); letter-spacing: -0.01em;
}
.shelf-stat { font-size: var(--t-micro); color: var(--faint); }

.doc-list { display: flex; flex-direction: column; }

.doc { border-bottom: 1px solid rgb(var(--brass-rgb) / 0.08); }
.doc:last-child { border-bottom: 0; }

/* The row is one grid, so titles and dates line up down the whole page. That
   alignment is most of what separates a list from a pile. */
.doc-row {
  display: grid;
  grid-template-columns: 1.5rem 1fr auto;
  grid-template-areas: "i title chev" ". flags flags" ". meta meta";
  align-items: center;
  gap: 0.1rem var(--s2);
  width: 100%; margin: 0; padding: var(--s3) var(--s1);
  background: none; border: 0; border-radius: var(--r-sm);
  text-align: left; min-height: 0; cursor: pointer;
  transition: background 0.12s;
}
.doc-row:hover { background: rgb(var(--tan-rgb) / 0.035); }
.doc-i {
  grid-area: i;
  font-family: var(--mono); font-size: var(--t-micro);
  color: var(--faint); font-variant-numeric: tabular-nums;
}
.doc-title {
  grid-area: title;
  font-size: var(--t-body); font-weight: 500; color: var(--text);
  overflow-wrap: anywhere;
}
.doc-flags { grid-area: flags; display: flex; gap: var(--s1); }
.doc-flags:empty { display: none; }
/* One cell holding both, so the date and the version can never land on top of
   each other the way two rules claiming the same grid area did. */
.doc-meta {
  grid-area: meta;
  display: flex; align-items: baseline; gap: var(--s3);
  font-family: var(--mono); font-size: var(--t-micro); color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.doc-v { color: var(--faint); opacity: 0.75; }
.doc-chev {
  grid-area: chev; width: 14px; height: 14px; color: var(--faint);
  transition: transform 0.18s;
}
.doc.open .doc-chev { transform: rotate(90deg); color: var(--brass); }
.doc.open .doc-title { color: var(--tan); }

.flag {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.06em;
  padding: 0.05rem 0.35rem; border-radius: 4px; text-transform: uppercase;
}
.flag.stale { color: var(--rec); background: rgb(var(--rec-rgb) / 0.1); }
.flag.undated { color: var(--brass); background: rgb(var(--brass-rgb) / 0.1); }
.flag.fired { color: var(--ok); background: rgb(var(--ok-rgb) / 0.1); }

/* ---- the editor: air, because this is the deliberate part ---- */

.doc-ed {
  padding: var(--s4) var(--s1) var(--s4) 1.6rem;
  border-left: 2px solid rgb(var(--brass-rgb) / 0.3);
  margin: 0 0 var(--s3) 0.5rem;
  display: flex; flex-direction: column; gap: var(--s3);
}
.doc-facts {
  display: grid; grid-template-columns: auto 1fr; gap: 0.2rem var(--s3);
  margin: 0; font-size: var(--t-micro);
}
.doc-facts dt {
  font-family: var(--mono); color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.08em;
}
.doc-facts dd { margin: 0; color: var(--dim); }

.doc-fields { display: flex; flex-wrap: wrap; gap: var(--s3); }
.field { flex: 1 1 14rem; display: flex; flex-direction: column; }
.field-date { flex: 0 1 10rem; }
.field label { margin: 0 0 var(--s1); }

.hint { font-size: var(--t-micro); color: var(--faint); margin: 0; max-width: 54ch; }
.doc-actions { display: flex; gap: var(--s2); flex-wrap: wrap; }
.doc-actions button { margin-top: 0; }

.shelf-note {
  font-size: var(--t-micro); color: var(--faint);
  margin: var(--s3) 0 0; padding-left: var(--s1);
}

.empty { color: var(--faint); font-size: var(--t-small); margin: var(--s3) 0; }
.empty.big { font-size: var(--t-body); padding: var(--s5) 0; }

/* ---- folds: history and the add form, both demoted ---- */

.fold {
  border: 1px solid var(--edge); border-radius: var(--r-md);
  background: linear-gradient(180deg, var(--glass), transparent);
}
.fold > summary {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3) var(--s4); cursor: pointer; list-style: none;
  font-size: var(--t-small);
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before {
  content: "＋"; font-family: var(--mono); color: var(--faint); font-size: 0.8rem;
}
.fold[open] > summary::before { content: "－"; }
.fold[open] > summary { border-bottom: 1px solid var(--edge); }
.fold-t { font-weight: 600; color: var(--text); }
.fold-n {
  font-size: var(--t-micro); color: var(--faint);
  padding: 0.05rem 0.4rem; border-radius: 4px; background: rgb(var(--tan-rgb) / 0.07);
}
.fold-why { color: var(--faint); font-size: var(--t-micro); margin-left: auto; text-align: right; }
.retired-list { padding: 0 var(--s4) var(--s2); opacity: 0.65; }
.add-form { padding: var(--s4); display: flex; flex-direction: column; gap: var(--s3); }
.add-form button { margin-top: var(--s1); align-self: flex-start; }

/* ---- open threads: a different kind of thing, at a different rhythm ---- */

.threads { display: flex; flex-direction: column; gap: var(--s4); }
.threads-head { display: flex; flex-direction: column; gap: var(--s1); }
.threads-head h2 { font-size: var(--t-title); margin: 0; color: var(--tan); }
.thread-cols { display: flex; flex-direction: column; gap: var(--s4); }
.thread h3 {
  font-family: var(--mono); font-size: var(--t-eyebrow); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--copper);
  margin: 0 0 var(--s2); display: flex; align-items: baseline; gap: var(--s2);
}
.thread h3 .count { color: var(--faint); letter-spacing: 0; }
.thread-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.thread-list li {
  font-size: var(--t-small); color: var(--dim); line-height: 1.45;
  padding: var(--s2) 0; border-bottom: 1px solid rgb(var(--brass-rgb) / 0.07);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2);
}
.thread-list li:last-child { border-bottom: 0; }
.thread-list li.fired { opacity: 0.5; }
.thread-list .cat {
  font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--faint); flex: 0 0 auto;
}
.thread-list .when { font-size: var(--t-micro); color: var(--faint); margin-left: auto; }

/* =========================================================== settings (V3)
 *
 * Ordered by what the tab is opened FOR. Devices first, because "what is
 * connected and which am I on" was the question, and five revoked devices used
 * to answer it at the same weight as the two live ones.
 *
 * Everything reversible and everyday sits in the middle. The one control that
 * cannot be undone from this screen is at the bottom, marked, and given air —
 * the failure mode is firing it by muscle memory while reaching past it.
 */

.settings { display: flex; flex-direction: column; gap: var(--s6); }

.block { display: flex; flex-direction: column; gap: var(--s3); }
.block-head {
  display: flex; align-items: baseline; gap: var(--s3);
  padding-bottom: var(--s2); border-bottom: 1px solid var(--edge);
}
.block-head h2 {
  font-size: var(--t-title); font-weight: 600; margin: 0;
  color: var(--tan); letter-spacing: -0.01em;
}
.block-status { margin: 0 0 0 auto; font-size: var(--t-micro); min-height: 0; }

.sub {
  font-family: var(--mono); font-size: var(--t-eyebrow); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--copper);
  margin: var(--s4) 0 var(--s2);
}
.plain-list { list-style: none; margin: 0; padding: 0; }
.plain-list li {
  font-size: var(--t-small); color: var(--dim);
  padding: var(--s2) 0; border-bottom: 1px solid rgb(var(--brass-rgb) / 0.07);
}
.plain-list li:last-child { border-bottom: 0; }
.plain-list.audit li { font-size: var(--t-micro); }
.code-block {
  font-family: var(--mono); font-size: var(--t-micro);
  white-space: pre-wrap; word-break: break-word;
  background: rgb(0 0 0 / 0.3); padding: var(--s3);
  border-radius: var(--r-sm); margin-top: var(--s2); overflow-x: auto;
}
.btn-row { display: flex; gap: var(--s2); flex-wrap: wrap; }
.btn-row button { margin-top: 0; }
.fold-body { padding: var(--s4); display: flex; flex-direction: column; gap: var(--s2); }
.fold-body button { align-self: flex-start; }

/* ---- a labelled control: what it does, then the thing that does it ---- */

.control {
  display: flex; flex-direction: column; gap: var(--s3);
  padding: var(--s4) 0; border-bottom: 1px solid rgb(var(--brass-rgb) / 0.07);
}
.control:last-child { border-bottom: 0; }
.control-text h3 {
  font-size: var(--t-body); font-weight: 600; margin: 0 0 var(--s1); color: var(--text);
}
.control-text p { font-size: var(--t-small); color: var(--dim); margin: 0; max-width: 62ch; }
.control-do { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.control-do button { margin-top: 0; }
.control-do .status { margin-top: 0; }

/* ---- devices ---- */

.device-list { display: flex; flex-direction: column; }
.device { border-bottom: 1px solid rgb(var(--brass-rgb) / 0.08); }
.device:last-child { border-bottom: 0; }

.device-row {
  display: grid;
  grid-template-columns: 0.6rem 1fr auto;
  grid-template-areas: "dot name chev" ". tags tags" ". state state";
  align-items: center; gap: 0.15rem var(--s3);
  width: 100%; margin: 0; padding: var(--s3) var(--s1);
  background: none; border: 0; border-radius: var(--r-sm);
  text-align: left; min-height: 0; cursor: pointer;
  transition: background 0.12s;
}
.device-row:hover { background: rgb(var(--tan-rgb) / 0.035); }

/* The dot is the glanceable part: lit for connected, hollow for away, dark for
   revoked. Shape as well as colour, so it survives a squint. */
.device-dot {
  grid-area: dot; width: 0.55rem; height: 0.55rem; border-radius: 50%;
  border: 1.5px solid var(--faint); background: transparent;
}
.device.connected .device-dot {
  background: var(--ok); border-color: var(--ok);
  box-shadow: 0 0 8px rgb(var(--ok-rgb) / 0.55);
}
.device.revoked .device-dot { border-color: rgb(var(--dead-rgb) / 0.5); background: transparent; opacity: 0.6; }

.device-name {
  grid-area: name; font-size: var(--t-lead); font-weight: 600; color: var(--text);
  overflow-wrap: anywhere;
}
.device.revoked .device-name { font-size: var(--t-body); font-weight: 500; color: var(--dim); }
.device-tags { grid-area: tags; display: flex; gap: var(--s1); flex-wrap: wrap; }
.device-tags:empty { display: none; }
.dtag {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 0.05rem 0.35rem; border-radius: 4px;
  color: var(--faint); background: rgb(var(--tan-rgb) / 0.06);
}
/* The one you are holding. It should be findable without reading. */
.dtag.self { color: var(--brass); background: rgb(var(--brass-rgb) / 0.13); }
.device.is-self { background: rgb(var(--brass-rgb) / 0.035); border-radius: var(--r-sm); }

.device-state {
  grid-area: state;
  font-family: var(--mono); font-size: var(--t-micro); color: var(--faint);
}
.device.connected .device-state { color: var(--ok); }
.device.open .doc-chev { transform: rotate(90deg); color: var(--brass); }
.device.revoked { opacity: 0.7; }

.device-more {
  padding: var(--s3) var(--s1) var(--s4) 1.4rem;
  border-left: 2px solid rgb(var(--brass-rgb) / 0.25);
  margin: 0 0 var(--s3) 0.25rem;
  display: flex; flex-direction: column; gap: var(--s3);
}
/* Revoke is destructive, so it says what it costs before it offers itself. */
.device-danger {
  border: 1px solid rgb(var(--dead-rgb) / 0.35);
  background: rgb(var(--dead-rgb) / 0.06);
  border-radius: var(--r-sm); padding: var(--s3);
  display: flex; flex-direction: column; gap: var(--s2);
}
.device-danger p { font-size: var(--t-micro); color: var(--dim); margin: 0; max-width: 56ch; }
.device-danger button { margin-top: 0; align-self: flex-start; }

/* ---- the danger zone ---- */

.danger-zone {
  border: 1px solid rgb(var(--dead-rgb) / 0.45);
  border-radius: var(--r-md);
  background:
    linear-gradient(180deg, rgb(var(--dead-rgb) / 0.09), rgb(0 0 0 / 0.25));
  padding: var(--s5);
  display: flex; flex-direction: column; gap: var(--s3);
  /* Air, deliberately. This is the one control on the screen that cannot be
     undone from the screen. */
  margin-top: var(--s2);
}
.danger-head { display: flex; align-items: center; gap: var(--s2); }
.danger-head h2 {
  font-size: var(--t-title); margin: 0; color: var(--dead);
  letter-spacing: 0.02em;
}
.danger-ico { width: 20px; height: 20px; color: var(--dead); flex: 0 0 auto; }
.danger-say { font-size: var(--t-small); color: var(--dim); margin: 0; max-width: 62ch; }
.danger-say strong { color: var(--text); font-weight: 600; }
.danger-zone button.danger { align-self: flex-start; margin-top: var(--s1); }
.danger-zone button.danger:disabled {
  opacity: 0.55; cursor: not-allowed;
  border-style: dashed;
}

/* How many ways back remain. Escalates by tone rather than only by number, so
   "one left" cannot be read as calmly as "nine left". */
.recovery-left {
  font-family: var(--mono); font-size: var(--t-micro);
  color: var(--dim); margin: 0; padding: var(--s2) var(--s3);
  border-left: 2px solid var(--faint); border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: rgb(0 0 0 / 0.22);
}
.recovery-left:empty { display: none; }
.recovery-left.low { color: var(--rec); border-left-color: var(--rec); }
.recovery-left.critical,
.recovery-left.gone {
  color: var(--alarm); border-left-color: var(--alarm);
  background: rgb(var(--alarm-rgb) / 0.1);
  font-size: var(--t-small); font-weight: 600;
}

.signout-row {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding-top: var(--s2);
}
.signout-row button { margin-top: 0; }

/* ============================================================ history (V3)
 *
 * Two questions, two answers: what is outstanding, and what happened. It used
 * to be four flat lists stacked with nothing between them — open items,
 * meetings, by day, recent sessions — so answering either meant scrolling past
 * three you did not ask for.
 *
 * "By day" and "recent sessions" were the same events at two grains, and
 * meetings had already happened inside those same sessions. They are one
 * chronological spine now, with meetings among the entries rather than beside
 * them.
 */

.hist { display: flex; flex-direction: column; gap: var(--s6); }
.page-head .page-stat { max-width: 62ch; color: var(--dim); font-size: var(--t-small); }

/* ---- outstanding: the only part of this screen that is about now ---- */

.out-group { margin-top: var(--s3); }
.out-h {
  font-family: var(--mono); font-size: var(--t-eyebrow); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--faint);
  margin: 0 0 var(--s2); display: flex; align-items: baseline; gap: var(--s2);
}
.out-h.late { color: var(--dead); }
.out-h.now { color: var(--rec); }
.out-h .count { color: var(--faint); letter-spacing: 0; }

.out-list { list-style: none; margin: 0; padding: 0; }
.out-item {
  display: flex; gap: var(--s3); align-items: flex-start;
  padding: var(--s3) var(--s1);
  border-bottom: 1px solid rgb(var(--brass-rgb) / 0.07);
}
.out-item:last-child { border-bottom: 0; }
.out-item input[type=checkbox] {
  width: auto; margin: 0.2rem 0 0; flex: 0 0 auto;
  accent-color: var(--brass); min-height: 0;
}
.out-body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.out-text { font-size: var(--t-body); color: var(--text); }
.out-meta { font-size: var(--t-micro); color: var(--faint); }
.out-meta .sep { margin: 0 0.3rem; opacity: 0.6; }
/* An overdue item says so in words as well as colour; the stripe is the glance. */
.out-item.late { border-left: 2px solid var(--dead); padding-left: var(--s3); }
.out-item.now { border-left: 2px solid var(--rec); padding-left: var(--s3); }

/* ---- controls ---- */

.hist-controls {
  display: flex; flex-wrap: wrap; gap: var(--s3); align-items: flex-end;
  padding-bottom: var(--s3); border-bottom: 1px solid var(--edge);
}
.hist-search { flex: 1 1 18rem; }
.hist-controls button { margin-top: 0; }
.hist-controls .status { flex: 1 0 100%; margin: 0; }

/* ---- the spine ---- */

.days { display: flex; flex-direction: column; gap: var(--s5); }
.day-head {
  display: flex; align-items: baseline; gap: var(--s3); flex-wrap: wrap;
  padding-bottom: var(--s2); margin-bottom: var(--s2);
  border-bottom: 1px solid var(--edge);
}
.day-name {
  font-size: var(--t-lead); font-weight: 600; color: var(--tan);
  text-decoration: none; letter-spacing: -0.01em;
}
.day-name:hover { color: var(--brass); text-decoration: underline; }
.day-stat { font-size: var(--t-micro); color: var(--faint); }
.day-stat .sep { margin: 0 0.3rem; opacity: 0.6; }
.day-cats { display: flex; gap: var(--s1); margin-left: auto; }

.entries { display: flex; flex-direction: column; }
.entry-wrap { border-bottom: 1px solid rgb(var(--brass-rgb) / 0.07); }
.entry-wrap:last-child { border-bottom: 0; }

/* One row shape for both kinds, so the day reads as a single column of events
   rather than two interleaved lists. */
.entry {
  display: grid;
  grid-template-columns: 4.9rem minmax(0, 1fr);
  grid-template-areas: "time what" ". meta";
  align-items: baseline; gap: 0.1rem var(--s3);
  width: 100%; margin: 0; padding: var(--s3) var(--s1);
  background: none; border: 0; border-bottom: 1px solid rgb(var(--brass-rgb) / 0.07);
  border-radius: var(--r-sm); text-align: left; min-height: 0;
  color: inherit; text-decoration: none; cursor: pointer;
  transition: background 0.12s;
}
.entry-wrap .entry { border-bottom: 0; }
.entries > .entry:last-child { border-bottom: 0; }
.entry:hover { background: rgb(var(--tan-rgb) / 0.035); }
.entry-time { grid-area: time; white-space: nowrap; font-size: var(--t-micro); color: var(--faint); }
.entry-what { grid-area: what; display: flex; flex-wrap: wrap; gap: var(--s2); align-items: baseline; }
.entry-quote { font-size: var(--t-small); color: var(--dim); overflow-wrap: anywhere; }
.entry-meta { grid-area: meta; font-size: var(--t-micro); color: var(--faint); }
.entry-meta .sep { margin: 0 0.3rem; opacity: 0.6; }

/* A meeting is the one entry that is a record rather than a conversation. */
.entry-kind {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--rec);
  padding: 0.05rem 0.35rem; border-radius: 4px; background: rgb(var(--rec-rgb) / 0.12);
  flex: 0 0 auto;
}
.entry.meeting .entry-quote { color: var(--text); }
.entry-wrap.open .entry.meeting { background: rgb(var(--rec-rgb) / 0.05); }
.entry-hits { font-size: var(--t-micro); color: var(--faint); padding: 0 var(--s1) var(--s2) 4.9rem; }

.meeting-record {
  margin: 0 0 var(--s4) var(--s1);
  padding: var(--s3) var(--s4);
  border-left: 2px solid rgb(var(--rec-rgb) / 0.4);
  display: flex; flex-direction: column; gap: var(--s2);
}
.mr-summary { font-size: var(--t-small); color: var(--text); margin: 0; max-width: 68ch; }
.mr-h {
  font-family: var(--mono); font-size: var(--t-eyebrow); font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--copper);
  margin: var(--s2) 0 0; display: flex; gap: var(--s2); align-items: baseline;
}
.mr-h .count { color: var(--faint); letter-spacing: 0; text-transform: none; }
.mr-list { margin: 0; padding-left: 1.1rem; font-size: var(--t-small); color: var(--dim); }
.mr-list li { padding: 0.15rem 0; }
.mr-lines {
  max-height: 22rem; overflow-y: auto; font-size: var(--t-small);
  scrollbar-width: thin; scrollbar-color: var(--edge) transparent;
}
.mr-line { padding: 0.15rem 0; color: var(--dim); }
.mr-who {
  font-family: var(--mono); font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--faint); margin-right: var(--s2);
}

/* ---- search hits and one day ---- */

.hits { display: flex; flex-direction: column; gap: var(--s4); }
.hit { border-left: 2px solid var(--edge); padding-left: var(--s3); }
.hit-meta { font-size: var(--t-micro); color: var(--faint); margin-bottom: var(--s2); }
.hit-meta .sep { margin: 0 0.3rem; opacity: 0.6; }
.hit-day { margin-left: var(--s2); }
.hit-turn { font-size: var(--t-small); color: var(--faint); padding: 0.15rem 0; }
.hit-turn.match { color: var(--text); }
.hit-who {
  font-family: var(--mono); font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--copper); margin-right: var(--s2);
}
.day-session { margin-bottom: var(--s5); }
.day-session-head {
  font-size: var(--t-micro); color: var(--faint);
  padding-bottom: var(--s2); margin-bottom: var(--s2);
  border-bottom: 1px solid rgb(var(--brass-rgb) / 0.07);
}
.day-session-head .sep { margin: 0 0.3rem; opacity: 0.6; }

/* ============================================================== panels */

.panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9.5rem), 1fr));
  gap: 0.65rem;
}

/* A generator at a glance. Tapping it opens the full page — the same renderer
   the V1 tab used, so nothing was lost in the move from tab to panel. */
.gen-panel {
  all: unset;
  display: block;
  cursor: pointer;
  padding: 0.8rem 0.85rem;
  border: 1px solid var(--edge);
  border-radius: var(--r-md);
  background: linear-gradient(180deg, var(--glass), rgb(var(--tan-rgb) / 0.01));
  transition: border-color 0.15s, background 0.15s;
  /* The state stripe. Colour is redundant with the badge text on purpose. */
  border-left: 2px solid var(--faint);
}
.gen-panel:hover { border-color: var(--edge-lit); background: var(--glass-lit); }
.gen-panel:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
.gen-panel.state-working, .gen-panel.state-green { border-left-color: var(--ok); }
.gen-panel.state-idle { border-left-color: var(--brass); }
.gen-panel.state-warn, .gen-panel.state-amber { border-left-color: var(--brass); }
.gen-panel.state-error, .gen-panel.state-dark { border-left-color: var(--dead); }
/* A kill switch is a deliberate halt, not a fault. It read as a fault on this
   stripe and as a warning in the badge — the two disagreed. */
.gen-panel.state-frozen { border-left-color: var(--halt); }

.gen-name { font-size: 0.88rem; font-weight: 600; }
.gen-state {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
  margin-top: 0.15rem;
}
.gen-metric {
  font-family: var(--mono);
  font-size: 1.15rem;
  margin-top: 0.45rem;
  font-variant-numeric: tabular-nums;
}
.gen-metric-label { font-size: 0.68rem; color: var(--faint); font-family: var(--mono); letter-spacing: 0.08em; }

/* Data tiles, used by the generator detail pages and the Friday tab. */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr)); gap: 0.6rem; }
.tile { border: 1px solid var(--edge); border-radius: var(--r-md); padding: 0.65rem 0.75rem; background: rgba(0, 0, 0, 0.2); }
.tile-label { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.08em; color: var(--faint); text-transform: uppercase; }
.tile-value { font-family: var(--mono); font-size: 1.2rem; margin-top: 0.15rem; font-variant-numeric: tabular-nums; }
.tile-note { font-size: 0.7rem; color: var(--faint); margin-top: 0.15rem; }

/* ================================================================ tables */

.lat-grid, .cost-grid {
  display: grid;
  gap: 0.3rem 0.85rem;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}
.lat-grid { grid-template-columns: 1fr auto auto; }
.cost-grid { grid-template-columns: 1fr auto auto auto; }
.lat-grid .h, .cost-grid .h { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.08em; color: var(--faint); text-transform: uppercase; }
.lat-grid .n, .cost-grid .n { font-family: var(--mono); text-align: right; }
.lat-grid .n.bad, .cost-grid .n.bad { color: var(--dead); }

.bar { height: 0.35rem; border-radius: 999px; background: rgba(0, 0, 0, 0.35); overflow: hidden; margin-top: 0.35rem; }
.bar > span { display: block; height: 100%; background: var(--brass); }
.bar.warn > span { background: var(--brass); }
.bar.bad > span { background: var(--dead); }

ul { list-style: none; padding: 0; margin: 0; }
.rows > li {
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--edge);
  font-size: 0.87rem;
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  align-items: baseline;
}
.rows > li:last-child { border-bottom: 0; }
.scroll-list { max-height: 20rem; overflow-y: auto; }

.feed li { display: flex; gap: 0.5rem; align-items: baseline; padding: 0.45rem 0; border-bottom: 1px solid var(--edge); font-size: 0.82rem; }
.feed li.muted { opacity: 0.45; }
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }

/* ================================================================== nav */

/* Phone: a fixed bar within thumb reach. Desktop: a rail beside the content.
   Same markup, same handlers. */
.nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  justify-content: space-around;
  gap: 0.15rem;
  padding: 0.4rem 0.5rem calc(0.4rem + var(--safe-b));
  background: rgb(11 9 8 / 0.86);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--edge);
}
.nav button {
  all: unset;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  padding: 0.4rem 0.2rem;
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--faint);
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  min-height: 2.9rem;
  transition: color 0.15s;
}
.nav button .ico { width: 1.15rem; height: 1.15rem; display: block; }
.nav button[aria-current="page"] { color: var(--brass); }
.nav button:hover { color: var(--text); }
.nav button:focus-visible { outline: 2px solid var(--brass); outline-offset: -2px; }

/* ================================================================ views */

.view { display: flex; flex-direction: column; gap: 0.9rem; }
.view > section { padding: 1rem 1.05rem; }

/* Home: orb on top, conversation beneath. This is the phone, and it is the
   base rule rather than a fallback. */
.home { display: flex; flex-direction: column; gap: 1rem; }

/* The status band. A column on a phone — orb first, at full size, ventures
   beneath — and one row on a desktop. Mobile is deliberately untouched: there
   the orb IS the primary control and earns the first screen. */
.home-status { display: flex; flex-direction: column; gap: var(--s4); }
.ventures { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.ventures > .eyebrow { margin: 0; }

/* ============================================================ desktop */

@media (min-width: 60rem) {
  /* ---- briefs ---- */
  /* Titles get a column of their own, so the eye runs down one edge. */
  /* Titles get a column of their own, so the eye runs down one edge and the
     dates line up as a second. Alignment is most of what separates a list from
     a pile. */
  .doc-row {
    grid-template-columns: 1.75rem minmax(0, 1fr) auto 9rem 1rem;
    grid-template-areas: "i title flags meta chev";
    gap: var(--s3);
    padding: 0.55rem var(--s2);
  }
  .doc-meta { justify-content: flex-end; }
  .doc-flags { justify-self: end; }

  /* ---- settings ---- */
  .device-row {
    grid-template-columns: 0.6rem minmax(0, 1fr) auto 11rem 1rem;
    grid-template-areas: "dot name tags state chev";
    gap: var(--s3);
    padding: var(--s3) var(--s2);
  }
  .device-state { text-align: right; }
  .device-tags { justify-self: end; }
  .device-more { padding-left: 2.2rem; }
  .control { flex-direction: row; align-items: flex-start; gap: var(--s5); }
  .control-text { flex: 1 1 auto; }
  .control-do { flex: 0 0 auto; justify-content: flex-end; }

  /* ---- history ---- */
  .entry {
    grid-template-columns: 5rem minmax(0, 1fr) 11rem;
    grid-template-areas: "time what meta";
    gap: var(--s3);
    padding: 0.5rem var(--s2);
  }
  .entry-meta { text-align: right; }
  .out-item { align-items: center; }
  .doc-ed { padding-left: 2.4rem; }
  .thread-cols { flex-direction: row; align-items: start; gap: var(--s5); }
  .thread { flex: 1 1 0; min-width: 0; }
  .shelf-head { flex-direction: row; align-items: baseline; gap: var(--s3); }
  .shelf-stat { margin-left: auto; }

  .shell {
    max-width: 74rem;
    margin: 0 auto;
    width: 100%;
    /* The rail takes the bottom bar's place, so the padding goes back. */
    padding: 1.4rem 1.5rem 2rem calc(6.5rem + 1.5rem);
  }

  .nav {
    top: 0;
    right: auto;
    bottom: 0;
    width: 6.5rem;
    flex-direction: column;
    justify-content: flex-start;
    gap: 0.3rem;
    padding: 1.4rem 0.6rem;
    border-top: 0;
    border-right: 1px solid var(--edge);
  }
  .nav button { flex: 0 0 auto; font-size: 0.62rem; padding: 0.6rem 0.2rem; }

  /* THREE BANDS, not two columns.
   *
   * The orb was a 24rem object in a 26rem column, beside the thing actually
   * being read. It is a status light now: still the wake control, still
   * readable across a room, no longer the largest thing on a screen where
   * nothing is happening to it. The conversation gets the width.
   */
  .home {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas: "status" "meeting" "talk";
    gap: var(--s4);
    align-items: start;
  }
  .home-status { grid-area: status; flex-direction: row; align-items: center; gap: var(--s5); }
  #meeting-panel { grid-area: meeting; margin-bottom: 0; }
  #pane-talk { grid-area: talk; }

  .orb-stage { width: 11rem; max-width: 11rem; margin: 0; flex: 0 0 auto; }
  .orb-caption { text-align: left; }

  /* Four across, aligned, quiet. The state stripe carries the signal, so a
     dark generator shows without competing with the conversation. */
  .ventures { flex: 1 1 auto; }
  .panels { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3); }
  .gen-panel { padding: var(--s3); }

  /* Capped at a readable measure rather than run edge to edge. */
  #pane-talk > .glass { max-width: 62rem; margin-inline: auto; }
  .stream { max-height: min(56vh, 40rem); }
  .view > section { padding: 1.15rem 1.25rem; }

  /* THE DENSE CASE: a live meeting wants the panel, the conversation and the
     ventures on screen at once, in front of other people. The status band gives
     up its height rather than pushing the record off the bottom. */
  .home.meeting-live .orb-stage { width: 6.5rem; max-width: 6.5rem; }
  .home.meeting-live .orb-caption { font-size: 0.7rem; }
  .home.meeting-live .ventures > .eyebrow { display: none; }
  .home.meeting-live .gen-panel { padding: var(--s2) var(--s3); }
  .home.meeting-live .gen-panel .gen-metric,
  .home.meeting-live .gen-panel .gen-metric-label { display: none; }
  .home.meeting-live .stream { max-height: min(30vh, 21rem); }
  .home.meeting-live .meeting-lines { max-height: 14rem; }
}

/* ======================================================= reduced motion */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  button:active { transform: none; }
}
