/* Wizard chrome on top of tokens.css (UI_GUIDELINES §3–§6). No
   frameworks; AA contrast; opaque surfaces so contrast holds in
   daylight on site.

   D206 "RazeForce command centre": DemSim is a RazeForce product, so
   the console IS the product identity — near-black page, #111111
   panels, bone text, ONE brand red (hot-red on dark), Archivo Black
   for short headlines only, figures in tabular mono. The simulation
   canvas no longer sits as a dark lens in a light page; the whole app
   is the instrument. Colour changes are instant (0ms = snappy); motion
   is opacity/transform only, 0.12s fast / 0.2s base, never springy;
   engineering data never animates.

   The PAPER family (D194's drafting-paper neutrals) is not gone — it is
   scoped to `[data-theme='light']` and used by the client-facing
   deliverables, which get printed. See tokens.json's comment. */

* { box-sizing: border-box; }
/* the hidden attribute must win over element display rules (e.g.
   `label { display: flex }`), so toggling `.hidden` reliably shows/hides */
[hidden] { display: none !important; }
body {
  margin: 0; 
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-family); font-size: var(--font-size-md);
  min-height: 100vh; display: flex; flex-direction: column;
}
main { flex: 1; padding: var(--space-5); max-width: 1100px;
       width: 100%; margin: 0 auto; z-index: 1; }
/* Display face on headlines ONLY (D206) — Archivo Black is a poster
   face, unreadable as body copy. It is a short-string face: screen
   titles and step numerals, nothing that wraps. */
/* Slice 6 (D456, audit P2): h1 was `text-transform: uppercase`, which
   made every screen title SHOUT ("START YOUR STUDY", "CONFIRM EVERY
   ASSUMPTION") — and it broke the app's OWN title-block rule (only the
   12px group labels are uppercase, nothing else). Sentence case in the
   display face reads as a considered heading, not a banner. */
h1 { font-size: var(--font-size-xl); margin: 0 0 var(--space-4);
  font-family: var(--font-display); font-weight: 400;
  letter-spacing: -0.01em; line-height: 1.15; }
h2 { font-size: var(--font-size-lg); margin: 0 0 var(--space-2);
  font-weight: 650; letter-spacing: -0.01em; }
.mono { font-family: var(--font-mono); }
.hint { color: var(--muted); font-size: var(--font-size-sm); }
.spacer { flex: 1; }

.topbar {
  display: flex; align-items: center; gap: var(--space-4);
  flex-wrap: wrap; row-gap: var(--space-2);   /* the key field drops
    cleanly on narrow screens instead of floating detached (D142) */
  padding: var(--space-3) var(--space-5);
  background: var(--elevated);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-sm); position: sticky; top: 0; z-index: 30;
}
/* the wordmark as a title-block stamp: spaced caps, drawing-set voice.
   The drawn logo (D200) supersedes the text treatment on the masthead;
   the type rules stay because brand.js falls back to a text company
   name, and the deliverable pages share this voice. */
.brand { font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; font-size: var(--font-size-sm);
  display: inline-flex; align-items: center; }
/* Two contexts, two sizes. On a DOCUMENT (the method-statement cover,
   the report) the wordmark is a masthead and gets a fixed 24px; in an
   app TOPBAR it is chrome and tracks the type scale so it stays in
   proportion to the badge beside it. `currentColor` does the theming. */
.brand-mark { height: 24px; width: auto; display: block; }
.topbar .brand-mark { height: 1.15em; }
/* white-label: the contractor's company + logo take the masthead
   (brand.js); the tier badge, pack chip and disclaimer are untouched */
.brand.branded { display: inline-flex; align-items: center;
  gap: var(--space-2); letter-spacing: normal; text-transform: none;
  font-size: inherit; }
.brand-logo { height: 28px; width: auto; max-width: 200px;
  display: block; }
.brand-name { font-weight: 700; }
#brand-meta { margin: var(--space-2) 0 0; }

/* branding editor dialog */
#brand-dialog { border: 1px solid var(--muted);
  border-radius: var(--radius-md); background: var(--surface);
  color: var(--text); max-width: 460px; width: 92%; padding: 0; }
#brand-dialog form { display: flex; flex-direction: column;
  gap: var(--space-3); padding: var(--space-5); margin: 0; }
#brand-dialog::backdrop { background: rgba(0, 0, 0, 0.55); }
#brand-dialog h2 { margin: 0; }
#brand-logo-preview { display: inline-flex; align-items: center; }
#brand-logo-preview img { max-height: 40px; width: auto; }
#brand-accent { width: 56px; height: 32px; padding: 2px; }

/* Rule 1: the tier badge is prominent on every screen — never styled
   to be ignorable. */
.tier-badge {
  padding: 2px var(--space-3); border-radius: var(--radius-sm);
  font-size: var(--font-size-sm); font-weight: 600; color: #111;
  background: var(--color-tier-L0); cursor: help;
  /* inset ring so L0-grey can never dissolve into the topbar — the one
     badge an engineer's sign-off leans on (rule 1) */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
}
.tier-badge[data-tier="L1"] { background: var(--color-tier-L1); }
.tier-badge[data-tier="L2"] { background: var(--color-tier-L2); }
.tier-badge[data-tier="L3"] { background: var(--color-tier-L3); }
.chip {
  padding: 2px var(--space-2); border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--border);
  font-size: var(--font-size-xs); color: var(--text);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.key-label { font-size: var(--font-size-xs); color: var(--muted);
  display: flex; gap: var(--space-2); align-items: center; }

#steps ol {
  display: flex; gap: var(--space-5); list-style: none;
  margin: 0 auto; padding: var(--space-3) var(--space-5);
  max-width: 1100px; counter-reset: step;
}
#steps li { counter-increment: step; color: var(--muted);
  font-size: var(--font-size-sm); }
#steps li::before { content: counter(step) ". "; }
#steps li.current { color: var(--text); font-weight: 600; }
#steps li.done { color: var(--color-accent); }

.cards { display: flex; gap: var(--space-5); flex-wrap: wrap; }
#jump-card { margin-bottom: var(--space-4);
  border: 1px solid var(--color-accent); }
#jump-input { flex: 1; min-width: 200px; }
#chimney-card { margin-top: var(--space-4); }
#parametric-form { display: flex; flex-direction: column; gap: var(--space-3); }
.chimney-grid { display: grid; gap: var(--space-2);
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
.target-picker { display: flex; flex-direction: column; gap: var(--space-2);
  margin: var(--space-2) 0; }
/* A checkbox sits BESIDE its own words (D293).
   `label` is `flex-direction: column` app-wide, which is right for a
   field over its input and wrong for a tick box: every `.check` in the
   app rendered its box stacked ABOVE the text it labels, floating and
   detached. Only `.target-picker .check` said otherwise, so the rule
   already existed and was simply scoped to one list. Generalised —
   found by looking at the screen, not the markup, which is the only
   place it was visible. */
.check { flex-direction: row; align-items: center;
  gap: var(--space-2); }
.check > input { flex: 0 0 auto; }
.target-picker .check { display: flex; align-items: center; gap: var(--space-2); }
.target-plan-row { background: var(--surface); border-radius: var(--radius-md);
  border: 1px solid var(--border); padding: var(--space-3);
  margin-bottom: var(--space-3); }
.target-plan-row h3 { margin: 0 0 var(--space-2); font-size: var(--font-size-md); }
.target-plan-row input[type="number"] { width: 9.5em; }
#chimney-firing input[type="number"],
#t0-row input[type="number"],
#high-reach-row input[type="number"],
#cable-pull-row input[type="number"],
#cut-study input[type="number"],
#parametric-dimensions input[type="number"],
#duration,
input[name="stiffness_scale"],
#prox-rings,
input[name="target_footprint_radius"],
.seq-table .load-cell input[type="number"],
.seq-table input[data-k="capacity_scale"],
.seq-table input[data-k="time"],
.seq-table input[data-k="story"],
.seq-table select[data-k="action"],
.seq-table .load-cell select,
#blastseq-delay,
#noise-limit,
#vib-k,
#vib-n,
#osm-form input[name="radius"],
#site-aspect { width: 12em; }
#noise-period,
#vib-class-plan,
#vib-class,
#finish-quality,
#mesh-grade,
#solver-backend,
#ensemble-n,
#parametric-type,
#oc-quality,
#render-provider,
#render-view { width: 22em; }
#chimney-list { list-style: none; margin: var(--space-3) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-1); }
#chimney-list li { display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--font-size-sm); }
.chimney-row.selected { border-color: var(--color-danger); }
/* Opaque surfaces on purpose (D65): translucency made real contrast
   unpredictable and washed out in daylight on site. */
.card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: var(--space-4); flex: 1 1 320px;
  display: flex; flex-direction: column; gap: var(--space-3);
  border: 1px solid var(--border); box-shadow: var(--shadow-sm);
}
label { display: flex; flex-direction: column; gap: var(--space-1);
  font-size: var(--font-size-sm); }
input, textarea, select {
  /* a field is a CUT-OUT, darker than the panel it sits in, with a
     bright enough edge to read at arm's length on a site tablet
     (WCAG 1.4.11 >=3:1). Both values are theme-bound so the printed
     deliverables' paper fields still work. */
  background: var(--field); color: var(--text);
  border: 1px solid var(--field-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3); font: inherit;
  min-height: 40px;                     /* touch target on a site tablet */
}
input:hover, textarea:hover, select:hover { border-color: var(--muted); }
input:focus-visible, textarea:focus-visible, select:focus-visible,
button:focus-visible, [data-mode-btn]:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 1px;
  border-color: var(--color-accent); }

/* ONE primary button. Everything else is secondary or quiet — when
   every control glows, nothing is the next step. Primary is a hot-red
   fill with bone text; secondary (`.ghost`) is a raised dark panel with
   the bright border. No blue anywhere (D206). */
button {
  background: var(--color-accent); color: var(--color-rf-offWhite);
  border: 1px solid transparent;
  border-radius: var(--radius-md); padding: var(--space-2) var(--space-4);
  font: inherit; font-weight: 600; cursor: pointer; min-height: 40px;
  transition: background var(--motion-fast) linear;
}
button:hover:not(:disabled) { background: var(--color-accentHover); }
/* the only button motion: a 1px machine press. */
button:active:not(:disabled) { transform: translateY(1px); }
button.ghost { background: var(--elevated); color: var(--text);
  border: 1px solid var(--border-bright); font-weight: 500; }
button.ghost:hover:not(:disabled) {
  border-color: var(--color-accent); color: var(--color-rf-offWhite); }
button:disabled { opacity: 0.45; cursor: not-allowed; }

/* D511: AI-assisted actions are quiet secondaries. The old `.ai-glow`
   neon treatment is gone — it competed with "Run the study" and read
   as a 2024 AI-product kit. */

/* ---------------------------------------------- study modes & modules */
.mode-switch { display: grid; gap: var(--space-3); margin: 0 0 var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.mode-switch [data-mode-btn] {
  display: flex; flex-direction: column; gap: var(--space-1);
  align-items: flex-start; text-align: left;
  background: var(--elevated); color: var(--text);
  border: 2px solid var(--border); border-radius: var(--radius-md);
  padding: var(--space-4); font-weight: 500; }
.mode-switch [data-mode-btn]:hover { border-color: var(--muted); }
.mode-switch [data-mode-btn].active {
  border-color: var(--color-accent); background: var(--color-accentSoft); }
.mode-switch .mode-title { font-weight: 700; font-size: var(--font-size-md); }
.mode-switch [data-mode-btn].active .mode-title { color: var(--color-accent); }
.mode-switch .mode-desc { font-size: var(--font-size-sm); color: var(--muted); }
.mode-switch [data-mode-btn].active .mode-desc { color: var(--text); }

#module-picker { margin: 0 0 var(--space-5); display: flex;
  flex-direction: column; gap: var(--space-4); }
/* the TITLE-BLOCK rule (D194 signature): every group header is a drawing
   title block — 12px caps over a full-width hairline. Applied here, the
   method families, the rail title and table headers; nowhere else caps. */
.module-group h3 { margin: 0 0 var(--space-2); font-size: var(--font-size-xs);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted);
  font-weight: 600; border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-1); }
.module-grid { display: grid; gap: var(--space-2);
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.module-card { flex-direction: row; align-items: flex-start;
  gap: var(--space-3); padding: var(--space-3);
  background: var(--elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); cursor: pointer; font-size: inherit; }
.module-card:hover { border-color: var(--muted); }
.module-card.on { border-color: var(--color-accent);
  background: var(--color-accentSoft);
  /* selection ring: 1px border + 1px inset = a 2px machined ring with
     zero layout shift */
  box-shadow: inset 0 0 0 1px var(--color-accent); }
.module-card input { min-height: 0; margin-top: 3px; flex: 0 0 auto; }
.module-icon { font-size: var(--font-size-lg); line-height: 1;
  color: var(--muted); flex: 0 0 auto; }
.module-card.on .module-icon { color: var(--color-accent); }
.module-body { display: flex; flex-direction: column; gap: 2px; }
.module-label { font-weight: 600; }
.module-desc { font-size: var(--font-size-sm); color: var(--muted); }
.module-cost { font-size: var(--font-size-xs); color: var(--color-warn); }

.module-panel { border-top: 1px solid var(--border);
  padding-top: var(--space-4); margin-top: var(--space-4); }
.module-panel > h2 { font-size: var(--font-size-md); }
.field-grid { display: grid; gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.result-block { margin-top: var(--space-3); padding: var(--space-3);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: var(--font-size-sm); }

/* ------------------------------------------------- drill & charge plan */
.blast-pattern { border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: var(--space-3); margin-bottom: var(--space-3);
  background: var(--elevated); }
.blast-head { display: flex; align-items: center; justify-content: space-between;
  margin-bottom: var(--space-2); }
.blast-head .blast-del { min-height: 28px; padding: 0 var(--space-3);
  line-height: 1; }
.blast-draw { margin-top: var(--space-3); overflow-x: auto; }
.blast-elev { width: 100%; max-width: 560px; height: auto;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--space-2); }
fieldset { border: 1px solid var(--border); border-radius: var(--radius-sm);
  display: flex; flex-direction: column; gap: var(--space-2); }
fieldset label { flex-direction: row; align-items: center;
  gap: var(--space-2); }

.banner {
  background: var(--elevated); border: 1px solid var(--border);
  border-left: 4px solid var(--color-tier-L0);
  padding: var(--space-3); border-radius: var(--radius-sm);
}
.flow-note { margin: 0 0 var(--space-3); }
.switch-on { margin: var(--space-2) 0 0; padding-left: 1.2rem; }
.switch-on li { margin: 0.15rem 0; }
/* the drawing drop zone — the fastest route to a real plan (D52), so it is
   a visible target, not a passive tip line */
.dropzone {
  display: block; width: 100%; text-align: left; cursor: pointer;
  margin: var(--space-3) 0; padding: var(--space-3);
  background: var(--elevated); color: inherit; font: inherit;
  border: 2px dashed var(--border); border-radius: var(--radius-sm);
}
.dropzone.disabled {
  /* D286: a capability that is not switched on says so BEFORE the drop
     (D115), so the zone must LOOK inert as well as answer inert. */
  opacity: 0.55;
  cursor: not-allowed;
  border-style: solid;
}
.dropzone:hover, .dropzone:focus-visible {
  border-color: var(--color-accent, currentColor);
  background: var(--bg);
}
.dropzone-icon { font-size: 1.6rem; display: block; }
.dropzone-main { display: block; font-weight: 600; margin: 0.2rem 0; }
.dropzone-sub { display: block; font-size: 0.9em; opacity: 0.85; }
.copilot-dragging .dropzone {
  border-style: solid; border-color: var(--color-accent, currentColor);
}
#falldir { margin-bottom: var(--space-3); }
#fd-current { margin: 0 0 var(--space-2); }
/* The two safety verdicts must never share an ink (D494). `.fd-ok` wore
   var(--color-accent) — which since D206 IS the danger hot-red, so
   "lands clear" and "would land on X" rendered in the same red, and the
   clear verdict wore the danger colour. The status TEXT tokens are the
   pair made for this: distinct on the dark console (#4ade80 / #ff8585)
   AND still distinct after the light/report rebinding (#12643f /
   crimson) — the raw --color-ok/--color-danger fail AA as type on dark,
   which is exactly why the *Text variants exist. The words already
   differ; colour is never the only carrier (UI_GUIDELINES §6). */
.fd-ok { color: var(--color-okText); font-weight: 600; }
.fd-bad { color: var(--color-dangerText); font-weight: 600; }
.fd-alt { margin-right: var(--space-2); }
.fm-canvas { width: 100%; max-width: 520px; height: auto;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); cursor: crosshair; display: block;
  margin: var(--space-2) 0; }
.fm-table { border-collapse: collapse; font-size: var(--font-size-sm); }
.fm-table th, .fm-table td { padding: 2px var(--space-3); text-align: left;
  border-bottom: 1px solid var(--border); }
.switch-detected {
  margin: var(--space-2) 0; padding: var(--space-2);
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 0.9em;
}
.banner code, .switch-on code {
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 3px; padding: 0.05rem 0.3rem; font-size: 0.92em;
  word-break: break-all;
}
/* the callout pattern on dark (D206): a red-tinted surface, a deep-red
   edge and the READABLE red for text — #C0392B on near-black fails AA
   as type, which is exactly why `--color-dangerText` exists. */
.error {
  background: var(--color-dangerSoft);
  border: 1px solid #5f1010;
  border-left: 4px solid var(--color-danger);
  color: var(--color-dangerText);
  padding: var(--space-3); border-radius: var(--radius-md);
  white-space: pre-wrap;
}

.split { display: flex; gap: var(--space-5); align-items: flex-start;
  flex-wrap: wrap; }
#plan { flex: 1 1 380px; margin: 0; background: var(--surface);
  border-radius: var(--radius-md); padding: var(--space-3);
  border: 1px solid var(--border); box-shadow: var(--shadow-md); }
#plan svg { width: 100%; height: auto; display: block; }
#bldg-list { flex: 1 1 340px; display: flex; flex-direction: column;
  gap: var(--space-3); }
/* Shrinkable at ANY width (D142). The fixed 1fr×4 grid had a min-content
   wider than the step column leaves at laptop widths beside the canvas —
   and the overflow painted UNDER the sticky panel, clipping the very
   inputs (lat/lon, base Ø) the row exists to edit. auto-fit + a real
   minimum wraps instead of overflowing, and min-width:0 lets the tracks
   actually compress. */
.bldg-row { background: var(--elevated); border-radius: var(--radius-md);
  padding: var(--space-3); display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-2);
  border: 1px solid var(--border); box-shadow: var(--shadow-sm); }
.bldg-row label { min-width: 0; }
.bldg-row input, .bldg-row select { width: 100%; min-width: 0;
  box-sizing: border-box; }
.bldg-row:hover { border-color: var(--muted); }
/* Slice 6 (D456, audit M3): the focused card flooded with
   --color-accentSoft, which is byte-identical to --color-dangerSoft (accent
   and danger are the SAME hot-red in this theme) — so the card a contractor
   is actively working on read as an error state. A left accent bar marks
   "this is the one" without the alarm; the border brightens neutrally and
   the fields stay their own colour. */
.bldg-row.selected { border-color: var(--border-bright);
  box-shadow: inset 3px 0 0 var(--color-accent), var(--shadow-sm); }
.bldg-row .name { grid-column: 1 / -1; font-weight: 600;
  display: flex; justify-content: space-between; align-items: center; }
/* Slice 4 (D454): each structure card is a <details>; its summary is the
   header + the disclosure toggle. Native marker off, a chevron on the
   right that flips with the open state; collapsed cards show only this
   line so a multi-structure Confirm stays scannable (audit H3). The card
   is a grid, so the UA "hide non-summary when closed" needs restating
   for grid children. */
summary.name { cursor: pointer; list-style: none; }
summary.name::-webkit-details-marker { display: none; }
summary.name::after { content: "▾"; margin-left: var(--space-2);
  color: var(--muted); font-weight: 400; }
details.bldg-row:not([open]) > summary.name::after { content: "▸"; }
details.bldg-row:not([open]) > :not(summary) { display: none; }
details.bldg-row:not([open]) { background: var(--surface); }
.bldg-row .span-all { grid-column: 1 / -1; }

/* provenance chips: assumed / provided / confirmed — never a silent
   guess (MASTER_PLAN rule P2, docs/PARAMETERS.md) */
/* styled as inked STAMPS — the assumed/confirmed marks are part of the
   identity (rule 6), never muted */
.prov { font-size: var(--font-size-xs); padding: 0 var(--space-1);
  border-radius: 2px; color: #111; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12); }
.prov-assumed { background: var(--color-tier-L1); }
.prov-provided { background: var(--color-kind-slab); }
.prov-confirmed { background: var(--color-kind-beam); }
.prov-documented { background: var(--color-tier-L3); }

/* a value read off a drawing, offered beside the field it belongs to and
   applied one at a time (D83) — never written silently */
.proposal { display: inline-flex; align-items: center; gap: var(--space-2);
  margin-top: var(--space-1); padding: 2px var(--space-2);
  border: 1px dashed var(--color-accent); border-radius: var(--radius-sm);
  background: var(--color-accentSoft); font-size: var(--font-size-xs); }
.proposal-val { font-weight: 600; }
.proposal button { min-height: 24px; padding: 0 var(--space-2);
  font-size: var(--font-size-xs); }

.seq-table { border-collapse: collapse; width: 100%;
  font-size: var(--font-size-sm); }
.seq-table th { text-align: left; color: var(--muted);
  font-weight: 600; padding: var(--space-1) var(--space-2);
  font-size: var(--font-size-xs); text-transform: uppercase;
  letter-spacing: 0.08em; border-bottom: 1px solid var(--border); }
.seq-table td { padding: var(--space-1) var(--space-2);
  vertical-align: middle; }
.seq-table input, .seq-table select { width: auto; min-width: 3.5em; }
.seq-table .row-del { padding: 0 var(--space-2); }
.sched-table .sched-cell { cursor: pointer; border-left: 3px solid
  transparent; }
.sched-cell.conf-ok { border-left-color: var(--color-kind-beam); }
.sched-cell.conf-warn { border-left-color: var(--color-tier-L1); }
.sched-cell.conf-bad { border-left-color: var(--color-danger);
  background: color-mix(in srgb, var(--color-danger) 12%, transparent); }
.sched-cell input { width: 100%; min-width: 5em; }
#drawings-card { margin-top: var(--space-5); }
#vib-card { margin-top: var(--space-4);
  border-top: 1px solid var(--muted); padding-top: var(--space-3); }
#vib-receptors { border-collapse: collapse; width: 100%;
  font-size: var(--font-size-sm); }
#vib-receptors th { text-align: left; color: var(--muted);
  font-weight: 600; padding: var(--space-1) var(--space-2);
  font-size: var(--font-size-xs); text-transform: uppercase;
  letter-spacing: 0.08em; border-bottom: 1px solid var(--border); }
#vib-receptors td { padding: var(--space-1) var(--space-2); }
#vib-receptors tr.vib-over td { background: var(--color-dangerSoft);
  font-weight: 600; }
#reality-check-box { margin-top: var(--space-4); }
#reality-check p { margin: var(--space-1) 0;
  font-size: var(--font-size-sm); }
.rc-assumed { color: var(--color-tier-L1); font-weight: 600; }
.rc-provided { color: var(--color-kind-slab); }
.rc-ok { color: var(--color-kind-beam); }
#technique-form { max-width: none; }
#technique-form > label, #technique-form fieldset,
#technique-form details,
/* Act 5's controls are the SAME controls (D288) and kept their sizing
   by matching `#technique-form > label` — until they moved to their own
   screen, where that selector no longer reaches them and every field
   stretched the full column. A move can carry a control intact and
   still drop the rule that shaped it. */
#screen-review > label, #screen-review > details,
#screen-review > section > label { max-width: 480px; }
#sequence-editor, #preweaken-box { background: var(--surface);
  border-radius: var(--radius-md); padding: var(--space-3); }

#technique-form { display: flex; flex-direction: column;
  gap: var(--space-4); max-width: 480px; }
/* `flex-wrap` is not decoration here (D293). Without it a button row
   OVERFLOWS its column and is clipped: measured at 1280 px, the confirm
   act's row ran 210 px past its container with `overflow-x: visible`,
   so the last button could be neither seen nor clicked. The mobile
   query stacks these below 768 px, which is why the gap between there
   and about 1400 px went unnoticed — squarely the laptop a contractor
   actually plans on. */
.nav-row { display: flex; flex-wrap: wrap; gap: var(--space-3);
  margin-top: var(--space-4); }

.status { display: grid; grid-template-columns: auto 1fr;
  gap: var(--space-2) var(--space-4); background: var(--elevated);
  padding: var(--space-4); border-radius: var(--radius-md);
  border: 1px solid var(--border); box-shadow: var(--shadow-md);
  max-width: 480px; }
.status dt { color: var(--muted); }
/* instrument readouts (D194): the run's figures are gauges, not prose */
.status dd { margin: 0; font-variant-numeric: tabular-nums;
  font-family: var(--font-mono); font-weight: 600; }

/* Run progress (D72). Real measured progress from the worker — the fill
   only ever moves because a step counter moved, never on a timer. */
.run-phase { font-size: var(--font-size-lg); margin: 0 0 var(--space-3);
  max-width: 480px; }
.run-bar { max-width: 480px; height: 28px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  overflow: hidden; margin-bottom: var(--space-4);
  box-shadow: inset 0 1px 2px rgba(24, 20, 16, 0.06); }
.run-bar-fill { height: 100%; width: 0;
  background: var(--color-accent); color: var(--color-rf-offWhite);
  font-size: var(--font-size-sm); line-height: 28px; text-align: right;
  padding-right: var(--space-2); box-sizing: border-box;
  transition: width .4s linear; font-variant-numeric: tabular-nums;
  font-family: var(--font-mono); font-weight: 600; }

/* the queue, with a handle on every job in it (D81) */
.queue-box { max-width: 640px; margin-bottom: var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--space-3); background: var(--surface); }
.queue-box > summary { cursor: pointer; font-weight: 600; }
.queue-rows { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
.queue-row { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) 0; border-top: 1px solid var(--border); }
.queue-row.mine { font-weight: 600; }
.queue-what { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.queue-place { flex: 0 0 auto; color: var(--muted);
  font-size: var(--font-size-sm); }
.queue-stop { flex: 0 0 auto; }

#viewer { flex: 2 1 520px; aspect-ratio: 16 / 10; border: 0;
  border-radius: var(--radius-lg); background: var(--color-console-bg);
  box-shadow: 0 0 0 1px var(--color-console-border),
    0 0 0 2px var(--border-bright), var(--shadow-md); }
#result-panel { flex: 1 1 300px; display: flex; flex-direction: column;
  gap: var(--space-3); }
#result-metrics, #provenance { display: grid;
  grid-template-columns: auto 1fr; gap: var(--space-1) var(--space-3);
  margin: 0; font-size: var(--font-size-sm); }
#result-metrics dt, #provenance dt { color: var(--muted); }
#result-metrics dd, #provenance dd { margin: 0; text-align: right;
  font-variant-numeric: tabular-nums; font-family: var(--font-mono); }

footer { padding: var(--space-4) var(--space-5);
  background: var(--surface); border-top: 1px solid var(--border); }
.disclaimer { margin: 0; font-size: var(--font-size-sm);
  color: var(--text); }
#attribution { margin: var(--space-2) 0 0; }

svg .pit { fill: rgba(40, 32, 24, 0.45); stroke: var(--text);
  stroke-width: 0.4; stroke-dasharray: 0.4 0.25; cursor: pointer; }
svg .pit.selected { fill: rgba(40, 32, 24, 0.7); }
svg .bldg { fill: var(--color-kind-slab); stroke: var(--text);
  stroke-width: 0.4; cursor: pointer; }
svg .bldg.selected { fill: var(--color-danger); }
svg .chimney { fill: var(--color-kind-wall); stroke: var(--text);
  stroke-width: 0.4; cursor: pointer; }
svg .chimney.selected { fill: var(--color-danger); }
svg .fell-dir { stroke: var(--color-danger); stroke-width: 0.6;
  stroke-dasharray: 2 1.5; }
svg .bay-grid { stroke: var(--text); stroke-width: 0.25;
  pointer-events: none; }
svg .opening-rect, svg .opening-ratio { stroke: #16161b; stroke-width: 0.7;
  pointer-events: none; }
svg .cross-wall { stroke: var(--text); stroke-width: 0.35;
  pointer-events: none; }
svg .curtain-face { stroke: var(--muted); stroke-width: 0.55;
  stroke-dasharray: 1.2 0.8; pointer-events: none; }
svg .extra-column { stroke: var(--text); stroke-width: 0.45;
  pointer-events: none; }
svg .extra-column-beam { stroke: var(--text); stroke-width: 0.28;
  pointer-events: none; }
svg text { fill: var(--text); font-family: var(--font-family);
  pointer-events: none; }
svg .scale line { stroke: var(--muted); stroke-width: 0.6; }
svg .scale text { fill: var(--muted); }

/* --- safety plan overlay (D28) ----------------------------------- */
#safety-plan svg { width: 100%; height: auto;
  background: color-mix(in srgb, var(--color-kind-beam) 8%, var(--bg)); }
#safety-plan .excl-zone { fill: color-mix(in srgb,
  var(--color-kind-beam) 14%, transparent);
  stroke: var(--color-kind-beam); stroke-width: 0.5;
  stroke-dasharray: 3 2; }
#safety-plan .debris-max { fill: color-mix(in srgb,
  var(--color-danger) 20%, transparent); stroke: var(--color-danger);
  stroke-width: 0.4; }
#safety-plan .debris-p95 { fill: color-mix(in srgb,
  var(--color-danger) 30%, transparent); stroke: none; }
#safety-plan .bldg { fill: var(--color-kind-slab); stroke: var(--text);
  stroke-width: 0.4; cursor: default; }
#safety-plan .bldg.target { fill: var(--color-danger); }
#safety-verdict.ok { color: var(--color-kind-beam); font-weight: 600; }
#safety-verdict.error { color: var(--color-danger); font-weight: 600; }

/* --- proximity / receptor plan (D38) ----------------------------- */
#proximity-box { margin-top: var(--space-4); }
#proximity-plan { margin: var(--space-3) 0; overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-console-border);
  box-shadow: 0 0 0 1px var(--border-bright), var(--shadow-md); }
#proximity-plan svg { width: 100%; height: auto; display: block; }
#proximity-table { border-collapse: collapse; width: 100%;
  font-size: var(--font-size-sm); }
#proximity-table th { text-align: left; color: var(--muted);
  font-weight: 600; padding: var(--space-1) var(--space-2);
  font-size: var(--font-size-xs); text-transform: uppercase;
  letter-spacing: 0.08em; border-bottom: 1px solid var(--border); }
#proximity-table td { padding: var(--space-1) var(--space-2); }
#proximity-table td.num { text-align: right;
  font-variant-numeric: tabular-nums; }
/* Dark, satellite-style panel matching the Google-Earth field convention:
 * inner ring yellow (25 m), outer ring green (50 m), white measured lines. */
svg .prox-bg { fill: #12181f; }
svg .prox-bldg { fill: rgba(255, 255, 255, 0.16);
  stroke: rgba(255, 255, 255, 0.6); stroke-width: 0.4; }
svg .prox-target { fill: var(--color-danger); stroke: #fff; stroke-width: 0.3; }
svg .prox-chimney { fill: #ff5a4d; stroke: #fff; stroke-width: 0.3; }
svg .prox-recept { fill: var(--color-tier-L1); stroke: #fff;
  stroke-width: 0.3; }
svg .prox-ring { fill: none; stroke: #cfd8dc; stroke-width: 0.5;
  stroke-dasharray: 3 2; }
svg .prox-ring-inner { stroke: #ffd400; }
svg .prox-ring-outer { stroke: #35c759; }
svg .prox-ringlbl { fill: #cfd8dc; font-weight: 600; paint-order: stroke;
  stroke: #12181f; stroke-width: 1.5px; }
svg .prox-ringlbl-inner { fill: #ffd400; }
svg .prox-ringlbl-outer { fill: #35c759; }
svg .prox-line { stroke: #fff; stroke-width: 0.6; }
svg .prox-dist { fill: #fff; paint-order: stroke; stroke: #12181f;
  stroke-width: 2px; font-weight: 700; }
svg .prox-tgtlbl { fill: #fff; font-weight: 700; paint-order: stroke;
  stroke: #12181f; stroke-width: 2.5px; }
svg .prox-scale line { stroke: #fff; stroke-width: 0.6; }
svg .prox-scale text { fill: #fff; paint-order: stroke; stroke: #12181f;
  stroke-width: 1.5px; }
svg .prox-north line, svg .prox-north path { stroke: #fff; fill: #fff;
  stroke-width: 0.5; }
svg .prox-north text { fill: #fff; font-weight: 700; }
svg .prox-legend-bg { fill: rgba(0, 0, 0, 0.5); rx: 1; }
svg .prox-legtext { fill: #fff; }
/* planned fall corridor (D39) + neighbours it reaches + sim debris reach */
svg .prox-fall { fill: rgba(255, 176, 32, 0.16); stroke: #ffb020;
  stroke-width: 0.5; }
svg .prox-fall-arrow { fill: none; stroke: #ffb020; stroke-width: 0.6; }
svg .prox-fall-head { fill: #ffb020; }
svg .prox-falllbl { fill: #ffb020; font-weight: 700; paint-order: stroke;
  stroke: #12181f; stroke-width: 1.5px; }
svg .prox-debris { fill: none; stroke: #ff8c42; stroke-width: 0.5;
  stroke-dasharray: 1 1.4; }
/* recorded suppression rig (D172) — lock-step with PROX_STYLE */
svg .prox-monitor { fill: rgba(80, 170, 255, 0.18); stroke: #63b3ff;
  stroke-width: 0.5; }
svg .prox-curtain { stroke: #63b3ff; stroke-width: 1.1;
  stroke-dasharray: 2 1.6; }
svg .prox-hit { stroke: #ff4d4d; stroke-width: 1.1; }
svg .prox-line-hit { stroke: #ff6b6b; stroke-width: 0.8; }
svg .prox-dist-hit { fill: #ff8080; }
#proximity-table tr.prox-row-hit td { color: var(--color-danger);
  font-weight: 600; }
.warn-note { margin-top: var(--space-2); padding: var(--space-2);
  border-left: 3px solid var(--color-danger);
  background: var(--color-dangerSoft);
  font-size: var(--font-size-sm); border-radius: var(--radius-sm); }

/* --- AI copilot (D41): the plain-language front door ----------------- */
#copilot-card { border: 1px solid var(--color-accent); }
/* #copilot-intro (D213): a <summary> renders at browser-default weight,
   which visibly shrank the heading versus the plain <h2> it replaced —
   this keeps the assistant screen's look identical. The dock itself
   needs no override: aidock.js collapses the <details>, so there is
   nothing here for #ai-dock to restyle. */
#copilot-intro > summary { font-size: var(--font-size-lg);
  font-weight: 650; letter-spacing: -0.01em; margin: 0 0 var(--space-2);
  cursor: pointer; }
#copilot-intro[open] > summary { margin-bottom: var(--space-2); }
#copilot-intro:not([open]) > summary { margin-bottom: 0; }
.copilot-log { display: flex; flex-direction: column; gap: var(--space-2);
  max-height: 340px; overflow-y: auto; padding: var(--space-2) 0;
  margin: var(--space-2) 0; }
.copilot-msg { max-width: 85%; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md); font-size: var(--font-size-sm);
  line-height: 1.45; white-space: pre-wrap; }
.copilot-assistant { align-self: flex-start; background: var(--surface);
  border: 1px solid var(--muted); }
.copilot-user { align-self: flex-end; background: var(--color-accent);
  color: var(--color-rf-offWhite); }
#copilot-input { flex: 1; }
#copilot-apply { margin-top: var(--space-2); font-weight: 600;
  background: var(--color-tier-L3); color: #111; }
#copilot-card.copilot-dragging { outline: 2px dashed var(--color-accent);
  outline-offset: 4px; }
.copilot-attach-list { display: flex; flex-wrap: wrap; gap: var(--space-1);
  margin-bottom: var(--space-1); }
.copilot-chip { display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: var(--font-size-sm); background: var(--surface);
  border: 1px solid var(--muted); border-radius: var(--radius-sm);
  padding: 2px 6px; }
.copilot-chip button { border: 0; background: none; cursor: pointer;
  font-size: 1.1em; line-height: 1; color: var(--muted); padding: 0 2px; }

/* --- footprint tracing map (M2b) --------------------------------- */
#trace-panel { margin-top: var(--space-4); }
/* The map is the workhorse surface — give it real screen (D131): it was
   a fixed 440px window in a 900px viewport, which made tracing feel like
   working through a letterbox. Capped so the trace controls stay on
   screen below it inside the sticky canvas (D133). */
#trace-map { position: relative; height: min(55vh, 600px); overflow: hidden;
  border-radius: var(--radius-md); border: 1px solid var(--muted);
  cursor: crosshair; user-select: none;
  /* bare-grid fallback so tracing works when tiles cannot load */
  background:
    linear-gradient(var(--surface) 1px, transparent 1px) 0 0 / 32px 32px,
    linear-gradient(90deg, var(--surface) 1px, transparent 1px)
      0 0 / 32px 32px,
    var(--bg); }
#trace-map.picking { outline: 3px solid var(--color-accent);
  outline-offset: -3px; }
#trace-tiles { position: absolute; inset: 0; }
.trace-tile { position: absolute; width: 256px; height: 256px;
  pointer-events: none; }
.trace-tile.missing { visibility: hidden; }
#trace-svg { position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; }
#trace-svg .trace-poly { fill: var(--color-kind-slab);
  fill-opacity: 0.45; stroke: var(--text); stroke-width: 1.5; }
#trace-svg .trace-line { fill: none; stroke: var(--color-danger);
  stroke-width: 1.5; stroke-dasharray: 5 3; }
#trace-svg circle { fill: var(--color-danger); }
#trace-svg text { paint-order: stroke; stroke: var(--bg);
  stroke-width: 3px; }
#trace-attrib { position: absolute; right: var(--space-1);
  bottom: var(--space-1); font-size: var(--font-size-sm);
  color: var(--text); background: var(--surface); opacity: 0.85;
  padding: 0 var(--space-1); border-radius: var(--radius-sm); }
#trace-tile-url { width: 100%; font-family: var(--font-mono); }

/* Copilot-first site screen (D46): the five manual intake methods collapse
 * behind one disclosure so a first-timer sees the AI box + one toggle. */
#manual-intake { margin-top: var(--space-4); }
#manual-intake > summary { cursor: pointer; list-style: none;
  padding: var(--space-3) var(--space-4); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  font-weight: 600; color: var(--text); }
#manual-intake > summary::-webkit-details-marker { display: none; }
#manual-intake > summary::before { content: "⚙ "; color: var(--muted); }
#manual-intake[open] > summary { margin-bottom: var(--space-4);
  border-color: var(--color-accent); }
#manual-intake > summary:hover { border-color: var(--muted); }

/* Sticky primary action so "Continue / run it" is always reachable on the
 * long Confirm screen without scrolling to the bottom. */
/* Slice-1 C1 fix: the bar is a sticky footer, so it floats over the
   content behind it — with a transparent-topped gradient that content
   BLED THROUGH, reading as a rendering glitch (the location input
   showed through the bar on the first-run site screen). An opaque
   ground + a top hairline + a soft upward shadow makes it a clean
   layered action bar instead: content passes cleanly under it. */
.sticky-actions { position: sticky; bottom: 0; z-index: 20;
  background: var(--bg);
  border-top: 1px solid var(--border);
  box-shadow: 0 -8px 18px -10px rgba(0, 0, 0, 0.75);
  padding: var(--space-3) 0; margin-top: var(--space-4); }

/* Premium photoreal render (D45) — the returned MP4 sits under its card. */
.render-video { width: 100%; max-height: 60vh; border-radius: var(--radius-lg);
  border: 1px solid var(--color-console-border);
  box-shadow: 0 0 0 1px var(--border-bright), var(--shadow-md);
  background: var(--color-console-bg); display: block;
  margin-top: var(--space-2); }
#render-card strong { color: var(--color-tier-L3); }

/* crisper progress stepper: a done step reads as completed (✓), the
 * current one is emphasised */
#steps li.done::before { content: "✓ "; color: var(--color-kind-beam); }
#steps li.current { position: relative; }

/* Map-anchored 3D view (D44, scene3d.js) — a WebGL canvas over the
 * satellite site, with a collapse playback bar (shown in replay mode). */
.scene3d-wrap { position: relative; width: 100%; height: 440px;
  max-height: 70vh; border-radius: var(--radius-md); overflow: hidden;
  background: var(--color-console-bg);
  border: 1px solid var(--color-console-border); }
.scene3d-canvas { display: block; width: 100%; height: 100%;
  cursor: grab; touch-action: none; }
.scene3d-canvas:active { cursor: grabbing; }
#member-inspect { margin-top: var(--space-2); padding: var(--space-3);
  background: var(--elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); }
#member-inspect[hidden] { display: none; }
#member-inspect .inspect-head { display: flex; justify-content: space-between;
  align-items: center; gap: var(--space-2); }
#member-inspect strong { display: block; }
.scene3d-controls { position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  padding: var(--space-2); background: linear-gradient(to top,
    rgba(10, 12, 18, 0.92) 60%, rgba(10, 12, 18, 0)); }
/* D507: authored delay chips sit above the scrubber so the firing
   sequence is visible on the replay, not only in the method statement. */
.scene3d-fireplan { flex: 1 1 100%; display: flex; gap: 6px;
  overflow-x: auto; padding-bottom: 2px; }
.scene3d-fireplan[hidden] { display: none; }
.scene3d-fireplan .s3-chip { flex: 0 0 auto; font-size: var(--font-size-xs);
  white-space: nowrap; }
.scene3d-fireplan .s3-chip.on {
  background: var(--color-accent); color: var(--color-rf-offWhite);
  border-color: var(--color-accent); }
.scene3d-seekwrap { position: relative; flex: 1 1 auto; min-width: 60px; }
.scene3d-ticks { position: absolute; left: 0; right: 0; top: 0; height: 10px;
  pointer-events: none; }
.scene3d-ticks .s3-tick { position: absolute; top: 0; width: 2px; height: 10px;
  background: #c2262d; transform: translateX(-1px); }
.scene3d-controls button { flex: 0 0 auto; }
/* the cinematic orbit reads as engaged while it drives the camera */
.scene3d-controls [data-a="orbit"].on {
  background: var(--color-accent); color: var(--color-rf-offWhite);
  border-color: var(--color-accent); }
.scene3d-controls input[type="range"] { flex: 1 1 auto; min-width: 60px; }
.scene3d-controls [data-a="t"] { color: var(--text); font-size:
  var(--font-size-sm); font-variant-numeric: tabular-nums; min-width: 74px; }
/* the X-ray reads as engaged while it is ghosting the structure */
.scene3d-controls [data-a="xray"].on {
  background: var(--color-accent); color: var(--color-rf-offWhite);
  border-color: var(--color-accent); }
/* What the recorded cut's two colours mean, and what the drawing is NOT
 * (D415). The caveat sits in the same element as the swatches so the
 * picture cannot be read without it. Colours are the LOCK-STEP pair the
 * replay's own legend uses (D228/D412) — change these only with
 * `webviewer.py`'s `.sw-mouth` / `.sw-hinge`. */
.scene3d-legend { position: absolute; left: 0; right: 0; top: 0;
  padding: var(--space-2); font-size: var(--font-size-xs);
  color: var(--muted); line-height: 1.5;
  /* it lies OVER the canvas, and the canvas is dragged to orbit — a
     caption that eats pointer events makes the top strip of the view
     dead to the mouse, which reads as a broken 3D panel */
  pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(10, 12, 18, 0.92) 55%, rgba(10, 12, 18, 0)); }
.scene3d-legend em { font-style: normal; opacity: 0.85; }
.scene3d-legend .s3-sw { display: inline-block; width: 10px;
  height: 10px; border-radius: 2px; margin: 0 4px 0 8px;
  vertical-align: -1px; }
.scene3d-legend .s3-sw:first-child { margin-left: 0; }
.scene3d-legend .s3-mouth { background: #c2262d; }
.scene3d-legend .s3-hinge { background: #e8b84b; }
/* a pre-weakened member — the replay's own weakened blue (D412) */
.scene3d-legend .s3-weak { background: #3b82f6; }
/* member kinds (D461) — lock-step with scene3d.js KIND_COLOR */
.scene3d-legend .s3-col { background: #4778cf; }
.scene3d-legend .s3-beam { background: #6bcc66; }
.scene3d-legend .s3-slab { background: #b8b8b8; }
.scene3d-legend .s3-wall { background: #d6ba66; }

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

/* AI Summary card (Wave 1) */
.ai-summary-text {
  background: var(--elevated); padding: var(--space-3);
  border-radius: var(--radius-sm); border: 1px solid var(--border);
  font-size: var(--font-size-sm); line-height: 1.6;
  margin-bottom: var(--space-2); user-select: all;
}

/* Quick-start template buttons (Wave 1) */
.quickstart-grid {
  display: grid; gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  margin-top: var(--space-3);
}
.quickstart-btn {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--space-1); padding: var(--space-4) var(--space-3);
  background: var(--elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); cursor: pointer;
  transition: transform 90ms cubic-bezier(0.2, 0, 0, 1);
  color: var(--text); font-family: inherit;
}
.quickstart-btn:hover {
  border-color: var(--color-accent);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.quickstart-icon { font-size: 2rem; }
.quickstart-label { font-weight: 600; font-size: var(--font-size-md); }
.quickstart-sub { font-size: var(--font-size-xs); color: var(--muted); text-align: center; }

.streetview-wrap { margin-top: var(--space-3); }
.streetview-wrap iframe { width: 100%; border-radius: var(--radius-md); }
.streetview-link { display: inline-block; margin-bottom: var(--space-2); color: var(--color-accent); text-decoration: none; font-weight: 500; }
/* Dashboard Layout */
.dashboard-layout {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
  /* A demolition study is planned on a desk monitor, and 1400 px left a
     wide screen half empty while the work column was squeezed to ~330
     px. Capped rather than uncapped: past ~1800 px the map stops
     gaining and the eye starts travelling. */
  max-width: 1800px;
}
.wizard-panel {
  flex: 1 1 620px;
  /* the FLOOR under the middle column. Without it the canvas's fixed
     basis wins every squeeze and the step column collapses to whatever
     is left — which is how a card of three choices became three
     one-word columns. */
  min-width: 420px;
}
.live-preview-panel {
  /* the CANVAS (D133): the dominant partner now that the satellite map
     lives here — wide enough to trace on, capped so the step column
     keeps a usable ~480px+ on a laptop */
  flex: 0 1 clamp(460px, 38vw, 820px);
  min-width: 0;
  position: sticky;
  top: 80px;
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  /* the LENS bezel (D194): dark glass -> machined dark rim -> warm paper
     hairline -> paper. Two hairlines one pixel apart is the whole
     "instrument set into the sheet" effect — every dark island gets
     exactly this treatment, so they read as ONE component. */
  border: 1px solid var(--color-console-border);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04),
    0 0 0 1px var(--border-bright), var(--shadow-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-height: calc(100vh - 100px);
  overflow-y: auto;
}
/* The canvas was the ONE dark island in a paper app (D139) and needed a
   token rebind to get there. D206 made the whole console dark, so the
   rebind is gone: the canvas simply inherits, which is the point — one
   instrument, not a lens set into a document. `color-scheme` stays, so
   native pickers and scrollbars match. */
#site-canvas {
  background: var(--bg);
  color: var(--text);
  border-color: var(--border);
  color-scheme: dark;            /* native pickers/scrollbars match */
}
#site-canvas .hint { color: var(--muted); }
#site-canvas input, #site-canvas select, #site-canvas textarea {
  background: var(--elevated); color: var(--text);
  border-color: var(--border);
}
#site-canvas button.ghost { border-color: var(--border); }
/* The tab strip was deliberately PAPER under D194 — light controls on a
   dark instrument. With the console dark end to end that reads as a
   stray light island, so it is now a recessed segmented control: the
   selected tab is the raised plate, the rest are quiet. */
#site-canvas .preview-tabs { gap: var(--space-1);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 3px; }
#site-canvas .preview-tabs button { flex: 1; min-height: 34px;
  border-radius: var(--radius-sm); }
#site-canvas .preview-tabs button.ghost {
  color: var(--muted); border-color: transparent; background: transparent; }
#site-canvas .preview-tabs button.ghost:hover:not(:disabled) {
  background: var(--elevated); color: var(--text);
  border-color: var(--border-bright); }
#site-canvas #trace-map { border-color: var(--border); }
.live-preview-panel figure {
  margin: 0;
  width: 100%;
}
/* the trace panel keeps its ids and class but is no longer a card INSIDE
   a card — and "Hide" is meaningless on a dedicated tab (an empty canvas
   is all it could produce) */
#site-canvas #trace-panel { border: none; padding: 0; box-shadow: none;
  background: transparent; }
#site-canvas #trace-hide { display: none; }
.preview-tabs button {
  flex: 1;
}

.streetview-link:hover { text-decoration: underline; }

/* The three-column layout (rail + steps + canvas) needs ~1250px to give
   the step column a usable width — below that the canvas was starving
   the cards until their inputs painted UNDER it (D142, seen on the
   owner's own deployment). Stack EARLY, not at phone width. */
@media (max-width: 1250px) {
  .dashboard-layout { flex-direction: column; }
  .live-preview-panel {
    flex: none;
    width: 100%;
    position: static;
    max-height: 62vh;
    /* the canvas holds the MAP now (D133) — stacked, it must lead, not
       trail four steps of forms */
    order: -1;
  }
}

/* Feature 3.6: Mobile Field Mode */
@media (max-width: 768px) {
  body {
    font-size: var(--font-size-md, 16px);
  }
  .split {
    flex-direction: column;
  }
  .grid {
    grid-template-columns: 1fr;
  }
  form {
    flex-direction: column;
  }
  .form-row, .nav-row {
    flex-direction: column;
    align-items: flex-start;
  }
  button, select, input[type="text"], input[type="number"] {
    min-height: 48px;
    width: 100%;
    margin-bottom: 8px;
  }
  /* Slice 5 (D455, audit H4): the top nav used to become a COLUMN of
     full-width buttons on a phone — Ask AI / Branding / My studies /
     Project plan each spanning the screen, so the whole first viewport
     was chrome. Keep the topbar a wrapping ROW and its controls compact,
     so the nav is one or two tidy rows and the content is what fills the
     screen. */
  .topbar {
    padding: var(--space-2);
    gap: var(--space-2);
    row-gap: var(--space-2);
  }
  .topbar button {
    width: auto;
    min-height: 44px;
    margin-bottom: 0;
    flex: 0 1 auto;
  }
  main {
    padding: var(--space-2);
  }
  .card, .panel {
    padding: var(--space-3);
  }
  /* Mobile view-first landing (D457). The view-first block leads the
     doors screen on a phone. And when the doors are the entry, hide the
     persistent map canvas AND the plan rail: on desktop they sit beside
     the doors (room for both), but stacked on a phone they buried the
     doors under 62vh of empty map plus seven rail steps — a signed-in
     user landed on a wall of chrome with the actual entry off-screen.
     Slice 3 (D453) made the doors stand alone on desktop; this completes
     it for the stacked mobile layout. Once a door is chosen the plan is
     no longer empty, doors-open clears, and both come back. */
  body.doors-open .live-preview-panel,
  body.doors-open #plan-rail { display: none; }
  /* The desktop min-width floor (420px on .wizard-panel — the middle
     column's floor beside the canvas) overflows a 390px phone by itself,
     giving the sideways scroll the audit's "body never scrolls
     horizontally" rule forbids. On mobile the canvas is stacked/hidden,
     so the floor buys nothing — drop it and let the panel fit (D457). */
  .wizard-panel { min-width: 0; }
  /* Run / Results at phone width (D468, D457's named follow-up — the
     "own look" the audit left for a live-loaded run). Measured at 390px
     via a reopened study: two content-driven overflows the min-width
     drop above does not reach, because they are a CHILD's intrinsic
     min-content bubbling up through the stacked panel. */
  /* (1) Results: the 3D replay canvas carries its own bitmap width
     (640px) as its min-content, dragging the whole panel — and the
     page — into sideways scroll. Cap the replaced element to its
     container and collapse the wrap's min-content. */
  .scene3d-wrap { min-width: 0; }
  .scene3d-canvas { max-width: 100%; }
  /* the on-canvas transport (play / speed / orbit / x-ray / view +
     scrubber + time) is one non-wrapping row ~640px wide — it does not
     size the panel (absolute), but it spills off a phone; let it wrap
     onto two rows over the canvas foot. */
  .scene3d-controls { flex-wrap: wrap; }
  /* (2) Run: the 32-char mono job id is unbreakable, so the status
     grid's value column cannot shrink below ~308px and forces the
     panel to 439px. Let the mono ids wrap; the grid tracks may then
     collapse. */
  .status { min-width: 0; }
  .status > * { min-width: 0; }
  .status .mono { overflow-wrap: anywhere; }
  /* (3) Results, the 2D replay half: `#viewer` (flex 2 1 520px,
     aspect-ratio 16/10) and `#result-panel` (flex 1 1 300px) share a
     `.split` that stacks to a column at ≤1250px. Stacked, the 520px
     flex-basis becomes the iframe's HEIGHT, and aspect-ratio then
     computes width = 520 × 1.6 = 832px — wider than the phone,
     regardless of min-width. Make the viewer WIDTH-driven when stacked
     (full width, height from the aspect ratio) and collapse the panel's
     mono grids the same way the status grid above is capped. */
  #viewer { width: 100%; flex: 0 0 auto; }
  #result-panel { min-width: 0; }
  #result-metrics, #provenance { min-width: 0; }
  #result-summary, #result-metrics dd, #provenance dd {
    overflow-wrap: anywhere; }
}

/* Slice 5 (D455, audit M4): the persistent map/3D canvas sits beside
   EVERY screen because they all live in `.wizard-panel`. On a pure LIST
   (My studies) or a DOCUMENT (the method statement) it shows nothing and
   just halves the width the content could use. Yield it there — the
   wizard panel then takes the full row. `:has()` keys off the visible
   screen, so no navigation JS changes. Screens with real spatial context
   (the site steps, the 3D massing on review) keep the canvas. */
main.dashboard-layout:has(#screen-studies:not([hidden])) .live-preview-panel,
main.dashboard-layout:has(#screen-plan:not([hidden])) .live-preview-panel {
  display: none;
}

/* ------------------------------------------ demolition method picker (D66) */
.method-picker { display: flex; flex-direction: column; gap: var(--space-4);
  margin: var(--space-3) 0; }
.method-family h4 { margin: 0 0 var(--space-2); font-size: var(--font-size-xs);
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted);
  font-weight: 600; border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-1); }
.method-grid { display: grid; gap: var(--space-2);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.method-card { flex-direction: row; align-items: flex-start;
  gap: var(--space-3); padding: var(--space-3); cursor: pointer;
  background: var(--elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); font-size: inherit; }
.method-card:hover { border-color: var(--muted); }
.method-card.on { border-color: var(--color-accent);
  background: var(--color-accentSoft);
  box-shadow: inset 0 0 0 1px var(--color-accent); }
.method-card input { min-height: 0; margin-top: 4px; flex: 0 0 auto; }
.method-body { display: flex; flex-direction: column; gap: 2px; }
.method-label { font-weight: 600; }
.method-desc { font-size: var(--font-size-sm); color: var(--muted); }
.method-caveat { margin: 0; font-size: var(--font-size-sm);
  color: var(--color-warn); }
.method-caveat:empty { display: none; }
.method-params { margin-top: var(--space-2); }

/* ------------------------------------------------------- AI dock (D67) */
#ai-dock {
  position: fixed; right: var(--space-4); bottom: var(--space-4);
  width: min(420px, calc(100vw - 2 * var(--space-4)));
  max-height: min(70vh, 720px); overflow: auto; z-index: var(--z-modal);
  background: var(--elevated); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: var(--space-3);
}
.ai-dock-head { display: flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-2); }
.ai-dock-head .hint { flex: 1; }
.ai-dock-head button { min-height: 28px; padding: 0 var(--space-3);
  line-height: 1; }
#ai-dock #copilot-card { border: 0; box-shadow: none; padding: 0; }
#ai-dock #copilot-card h2 { font-size: var(--font-size-md); }
@media (max-width: 640px) {
  #ai-dock { right: var(--space-2); left: var(--space-2); bottom: var(--space-2);
    width: auto; max-height: 80vh; }
}

/* Slice-3 (D453): calm the entry (audit H2). While the four doors are
   the entry — empty plan, no door chosen yet — the site-workspace body
   is hidden BEHIND them, so the "ways in" stand alone instead of
   stacking under a second "YOUR SITE" heading and a full toolset (two
   mental models on one page). Choosing a door sets `state.entered`
   (app.js) and the workspace reveals. `#screen-map` itself is NOT
   hidden — only its children — so exactly one screen stays visible
   (the boot invariant). */
body.doors-open #screen-map > * { display: none; }

/* --------------------------------------------- site screen flow (D68) */
.site-step { margin: 0 0 var(--space-5); }
/* Slice-2 (D452): the inline ①②③ number circles were dropped — they
   were a THIRD progress numbering competing with the top act-nav and
   the left rail for the same journey (audit H1), and three red discs
   compounding the red overload (M1). The headings stay as plain
   sub-sections of the rail's "Site & boundary" step. */
.site-step > h2 { font-size: var(--font-size-lg); }
#site-jump { flex: 1; min-width: 220px; }
.tool-row { display: flex; flex-wrap: wrap; gap: var(--space-2);
  margin: var(--space-3) 0; }
button.tool { background: var(--elevated); color: var(--text);
  border: 1px solid var(--border); font-weight: 600; }
button.tool:hover:not(:disabled) { border-color: var(--color-accent);
  background: var(--color-accentSoft); color: var(--color-accent); }
#site-inventory { font-size: var(--font-size-md); color: var(--text); }
#other-intake { margin: 0 0 var(--space-5); }
#other-intake > summary { cursor: pointer; padding: var(--space-3) 0;
  color: var(--muted); font-size: var(--font-size-sm); }
#other-intake > summary:hover { color: var(--text); }

/* ------------------------------------ blast sequence planner (D69) */
.bs-groups { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2); margin: var(--space-3) 0; }
button.bs-group { min-width: 44px; min-height: 36px; padding: 0 var(--space-2);
  background: var(--elevated); color: var(--text);
  border: 2px solid var(--border); font-weight: 700; }
button.bs-group.on { border-color: var(--g, var(--color-accent));
  background: var(--g, var(--color-accent)); color: var(--color-rf-offWhite); }
button.bs-clear { font-weight: 500; }
/* drafting-grid vellum (D194): the planner cards read as gridded sheets —
   the faintest possible 24px grid, character at zero cost; the plan SVG
   itself stays clean paper (figure-ground for the member dots) */
.bs-storey { margin: var(--space-4) 0; padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background:
    repeating-linear-gradient(0deg, transparent, transparent 23px,
      rgba(24, 20, 16, 0.035) 23px, rgba(24, 20, 16, 0.035) 24px),
    repeating-linear-gradient(90deg, transparent, transparent 23px,
      rgba(24, 20, 16, 0.035) 23px, rgba(24, 20, 16, 0.035) 24px),
    var(--elevated); }
.bs-storey h4 { margin: 0 0 var(--space-2); font-size: var(--font-size-md); }
.bs-plan { width: 100%; max-width: 420px; height: auto; display: block;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); }
.bs-member { cursor: pointer; }
.bs-member:hover circle { stroke-width: 0.35; }
.bs-member:focus-visible { outline: 2px solid var(--color-accent); }
#blastseq-warnings { border-left-color: var(--color-warn); }
/* pre-weakening planner (D193) — shares the member grid, red by cut depth */
.pw-levels { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2); margin: var(--space-3) 0; }
button.pw-level { min-width: 44px; min-height: 36px; padding: 0 var(--space-2);
  background: var(--elevated); color: var(--text);
  border: 2px solid var(--border); font-weight: 700; }
button.pw-level.on { border-color: var(--pw, var(--color-warn));
  background: var(--pw, var(--color-warn)); color: var(--color-rf-offWhite); }
button.pw-clear { font-weight: 500; }
.pw-plan { width: 100%; max-width: 420px; height: auto; display: block;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); }
.pw-member { cursor: pointer; }
.pw-member:hover circle { stroke-width: 0.35; }
.pw-member:focus-visible { outline: 2px solid var(--color-accent); }
.pw-table-fallback { margin-top: var(--space-3); }
.pw-table-fallback > summary { cursor: pointer; color: var(--muted);
  font-size: var(--font-size-sm); }
.bs-schedule { margin: var(--space-4) 0; padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--elevated); }
.bs-schedule h4 { margin: 0 0 var(--space-2); font-size: var(--font-size-md); }
.bs-sched-list { list-style: none; margin: var(--space-2) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-2); }
.bs-sched-row { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--space-2);
  padding: var(--space-2) 0; border-top: 1px solid var(--border); }
.bs-sched-row:first-child { border-top: 0; }
.bs-sched-lead { display: flex; align-items: center; gap: var(--space-2);
  flex-wrap: wrap; }
.bs-sched-grp { display: inline-flex; align-items: center;
  justify-content: center; min-width: 26px; height: 26px; padding: 0 6px;
  border-radius: var(--radius-pill); background: var(--g, var(--color-accent));
  color: var(--color-rf-offWhite); font-weight: 700;
  font-size: var(--font-size-sm); }
.bs-sched-delay { display: inline-flex; align-items: center; gap: var(--space-1);
  color: var(--muted); font-size: var(--font-size-sm); }
.bs-sched-delay input { width: 5em; }
.bs-fire-time { color: var(--muted); font-size: var(--font-size-sm);
  font-variant-numeric: tabular-nums; font-family: var(--font-mono);
  font-weight: 600; }

/* ---------------------------------------------------------- workspace
   The four intake screens are steps of ONE workspace (D73). The rail is
   permanently on the left: the plan outline and the navigation are the
   same object, so the contractor can always see the whole job. */
.ws-grid { display: grid; grid-template-columns: 260px minmax(0, 1fr);
  gap: var(--space-5); align-items: start; }
.ws-steps { min-width: 0; }

.plan-rail { position: sticky; top: var(--space-4);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: var(--space-4); }
.rail-title { font-size: var(--font-size-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted); margin: 0 0 var(--space-3);
  font-weight: 600; border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-1); }
.plan-rail ol { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px; }

.rail-row { display: flex; gap: var(--space-3); align-items: flex-start;
  padding: var(--space-3); border-radius: var(--radius-sm);
  cursor: pointer; border: 1px solid transparent; }
.rail-row:hover { background: var(--elevated); }
.rail-row:focus-visible { outline: 2px solid var(--color-accent);
  outline-offset: 2px; }
.rail-row.active { background: var(--color-accentSoft);
  border-color: var(--color-accent); }
.rail-mark { flex: 0 0 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center; font-size: var(--font-size-sm);
  background: var(--elevated); color: var(--muted);
  font-variant-numeric: tabular-nums; }
.rail-row.done .rail-mark { background: var(--color-ok); color: var(--color-rf-offWhite); }
.rail-row.active .rail-mark { background: var(--color-accent); color: var(--color-rf-offWhite); }
.rail-body { display: flex; flex-direction: column; min-width: 0; }
.rail-label { font-weight: 600; }
/* the value is the whole point of the rail — what IS set, or plainly that
   nothing is; never let it be quieter than the label it explains */
.rail-value { font-size: var(--font-size-sm); color: var(--muted);
  font-variant-numeric: tabular-nums; }

.rail-action { margin-top: var(--space-4); padding-top: var(--space-4);
  border-top: 1px solid var(--border); }
.rail-run { width: 100%; }
.rail-run:disabled { opacity: .55; cursor: not-allowed; }

.eng-log { margin-top: var(--space-4); padding-top: var(--space-4);
  border-top: 1px solid var(--border); }
.eng-log .flow-note { margin: 0 0 var(--space-2); font-size: var(--font-size-xs); }
#eng-log { list-style: none; margin: 0; padding: 0;
  max-height: 180px; overflow-y: auto;
  font-family: var(--font-mono); font-size: var(--font-size-xs);
  font-variant-numeric: tabular-nums; }
#eng-log li { display: grid; grid-template-columns: 5.5em 1fr;
  gap: var(--space-2); padding: 2px 0;
  border-bottom: 1px solid var(--border); }
#eng-log time { color: var(--muted); }
#eng-log span { color: var(--text); }

.go-nogo { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3); margin: 0 0 var(--space-4);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border); }
.go-nogo .gn-cell { min-width: 0; }
.go-nogo dt { font-size: var(--font-size-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted); font-weight: 600; }
.go-nogo dd { margin: var(--space-1) 0 0;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 600; }
.go-nogo .gn-ok { color: var(--color-okText); }
.go-nogo .gn-bad { color: var(--color-dangerText); }
.go-nogo .gn-warn { color: var(--color-warnText); }
.go-nogo .gn-muted { color: var(--muted); }
details.pack > summary { cursor: pointer; font-weight: 600;
  font-size: var(--font-size-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted); }
details.pack > summary:hover { color: var(--text); }
@media (max-width: 768px) {
  .go-nogo { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
  .ws-grid { grid-template-columns: 1fr; }
  .plan-rail { position: static; }
}

/* Quick-start is a strip of shortcuts that pre-fill the assistant, not a
   titled card competing with the assistant it feeds (D73). */
.quickstart-strip { margin-bottom: var(--space-4); }
.quickstart-strip .hint { margin-bottom: var(--space-2); }

/* A rail row stating a problem, not progress (D76) — e.g. a target that
   falls outside the working boundary the contractor drew. */
.rail-row.warn .rail-mark { background: var(--color-warn); color: var(--color-rf-offWhite); }
.rail-row.warn .rail-value { color: var(--color-warn); }

/* Position correction on a confirm row (D76/D77) */
/* flexible, never truncated: beside the canvas (D133) the step column is
   narrower, and a fixed 8.5ch pair overflowed under the Move button */
.pos-cell .pos-row { display: flex; gap: var(--space-2); align-items: center;
  flex-wrap: wrap; }
.pos-cell input { flex: 1 1 9ch; width: auto; min-width: 9ch; }
/* A run the contractor should think twice about before starting (D77) */
.cost-long { color: var(--color-warn); }

/* Remove a structure from the plan (D79) — two-step, because losing a
   traced footprint to a stray click is worse than one extra tap. */
.row-del { padding: 2px var(--space-2); font-size: var(--font-size-sm); }
.row-del.armed { background: var(--color-warn); color: var(--color-rf-offWhite);
  border-color: var(--color-warn); }

/* fall-direction dial (D88) — free 360°, not eight buttons */
.fall-cell { min-width: 150px; }
.fall-dial { display: flex; flex-direction: column; align-items: center;
  gap: var(--space-1); }
.fall-dial-face { touch-action: none; cursor: grab;
  border-radius: 50%; }
.fall-dial-face:focus-visible { outline: 2px solid var(--color-accent);
  outline-offset: 2px; }
.fall-dial-face.dragging { cursor: grabbing; }
.fd-ring { fill: var(--surface); stroke: var(--border); stroke-width: 1; }
.fd-tick { stroke: var(--muted); stroke-width: 1; }
.fd-tick-major { stroke: var(--text); stroke-width: 1.5; }
.fd-card { fill: var(--muted); font-size: 11px; font-weight: 600; }
.fd-needle { stroke: var(--color-danger); stroke-width: 3;
  stroke-linecap: round; }
.fd-head { fill: var(--color-danger); }
.fd-hub { fill: var(--text); }
.fall-dial-read { font-size: var(--font-size-sm); font-weight: 600;
  text-align: center; }
.fall-dial-num { width: 9.5em; text-align: center; min-height: 32px;
  padding: 0 var(--space-1); }

/* the drawings route, on the screen the dimensions live on (D99) */
.drawings-cta { border: 1px solid var(--color-accent);
  background: var(--color-accentSoft); margin-bottom: var(--space-4); }
.drawings-cta h2 { font-size: var(--font-size-md); }

/* past studies (D103) */
.studies-rows { list-style: none; margin: var(--space-3) 0; padding: 0; }
.study-row { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0; border-top: 1px solid var(--border); }
.study-row.warn .study-title { color: var(--color-danger); }
.study-main { flex: 1 1 auto; display: flex; flex-direction: column; }
.study-title { font-weight: 600; }

/* Slice 6 (D456, audit P1): a designed empty state, not a sentence dropped
   into a status line. A contained, centred panel — a mark, a headline, one
   supportive line, and the single action that ENDS the emptiness — reads as
   a considered screen rather than a void. Reusable component; My studies is
   the first adopter. The operator-honesty note (no durable storage → history
   is wiped on redeploy, D195) survives here as a distinct caption. */
.empty-state { text-align: center; padding: var(--space-7) var(--space-4);
  border: 1px dashed var(--border-bright); border-radius: var(--radius-lg);
  background: var(--surface); display: flex; flex-direction: column;
  align-items: center; gap: var(--space-3); }
.empty-state .es-mark { font-size: 40px; line-height: 1; opacity: 0.55; }
.empty-state .es-head { margin: 0; font-family: var(--font-display);
  font-weight: 400; letter-spacing: -0.01em; }
.empty-state p { margin: 0; max-width: 46ch; color: var(--muted); }
.empty-state .es-note { font-size: var(--font-size-sm);
  color: var(--color-warnText); max-width: 54ch; }
.empty-state .es-actions { margin-top: var(--space-2); }

/* drawings kept as a study's evidence (D104) */
.kept-list { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
.kept-row { display: flex; align-items: baseline; gap: var(--space-2);
  padding: var(--space-1) 0; font-size: var(--font-size-sm); }

/* What is switched on for this deployment (D115). Quiet when everything
   is on — it is a diagnostic, not a banner — and never red for a service
   that is merely absent by choice. Durable storage is the exception: an
   instance holding evidence on a disk a redeploy can clear is a fact the
   operator has to see. */
.cap-strip { margin: var(--space-2) 0 0; font-size: var(--font-size-sm); }
.cap-strip > summary { cursor: pointer; color: var(--muted); }
.cap-strip[data-all-on="false"] > summary { color: var(--text); }
/* D217: something is broken and app.js auto-opens the strip for it — read
   as an actionable alert, not fine print, mirroring #manual-intake's own
   disclosure treatment (this file, above) rather than the default marker
   this strip otherwise keeps while everything is working. */
.cap-strip[data-all-on="false"] > summary { list-style: none;
  padding: var(--space-3) var(--space-4); background: var(--surface);
  border: 1px solid var(--color-warn); border-radius: var(--radius-md);
  font-weight: 600; }
.cap-strip[data-all-on="false"] > summary::-webkit-details-marker { display: none; }
.cap-strip[data-all-on="false"] > summary::before { content: "⚠ "; }
.cap-strip ul { margin: var(--space-2) 0 0; padding-left: 1.1em; }
.cap-row { margin: 0 0 var(--space-2); color: var(--muted); }
.cap-row[data-on="true"] { color: var(--text); }
.cap-row[data-on="true"] strong::before { content: "✓ "; }
.cap-row[data-on="false"] strong::before { content: "○ "; }
.cap-row[data-warn="true"] strong { color: var(--color-tier-L1); }
.cap-row[data-warn="true"] strong::before { content: "⚠ "; }

/* Project documents (D122) — a register, not a gallery. What is held and
   what each sheet gave, in the order they arrived. */
.docs-list { list-style: none; margin: var(--space-2) 0; padding: 0; }
.doc-row { border-left: 2px solid var(--muted);
  padding: var(--space-2) 0 var(--space-2) var(--space-3);
  margin: 0 0 var(--space-3); }
.doc-row .doc-name { font-weight: 600; display: block; }
.doc-figs { margin: var(--space-2) 0 0; padding-left: 1.1em;
  font-size: var(--font-size-sm); }
/* a figure whose sheet is gone is the one case where provenance has a
   hole; it is flagged rather than quietly listed like the rest */
.doc-orphan { border-left-color: var(--color-tier-L1); }

/* recorded water monitors (D164) — one card per monitor, fields inline */
.monitor-row { border: 1px solid var(--border); border-radius: 8px;
  padding: var(--space-3); margin: var(--space-3) 0; }
.monitor-head { display: flex; justify-content: space-between;
  align-items: center; margin-bottom: var(--space-2); }
.monitor-field { font-size: var(--font-size-sm); }

/* RazeForce sign-in gate (D204). A full-viewport cover over the booted
   wizard — presentation for the server-side session gate, never the
   enforcement itself. Paper ground so it reads as the app's own front
   door, not an error state. */
/* The sign-in screen is the first thing anyone sees of DemSim, so it
   carries the mark, the one-line pitch and the product's own signature
   event (D211, gatehero.js). Dark ground, hot-red on dark, gold once. */
#rf-gate { position: fixed; inset: 0; z-index: 400;
  background: var(--bg); overflow: hidden; }
/* The hero sits BEHIND the card rather than beside it, so one rule set
   serves phone and desktop — no second layout to keep in step. */
#rf-gate .rf-hero { position: absolute; inset: 0; }
#rf-gate .rf-hero-canvas { width: 100%; height: 100%; display: block; }
/* A scrim so body copy stays AA-legible over the collapse — ONE gradient,
   left to right. Two stacked gradients (a radial vignette as well) each
   looked reasonable and together darkened almost the whole frame, so the
   hero was invisible: opacity multiplies, it does not average. Judged
   from a real screenshot, not from the rule. */
#rf-gate .rf-hero::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, var(--bg) 0%, var(--bg) 26%,
    color-mix(in srgb, var(--bg) 55%, transparent) 46%, transparent 66%); }
#rf-gate .rf-gate-inner { position: relative; height: 100%;
  display: flex; flex-direction: column;
  align-items: flex-start; justify-content: center;
  gap: var(--space-4); padding: var(--space-6);
  max-width: 34rem; }
#rf-gate .rf-card { padding: 0; border: 0; background: none;
  text-align: left; max-width: 26rem; }
#rf-gate .rf-gate-brand { display: flex; flex-direction: column;
  align-items: flex-start; gap: 2px; margin-bottom: var(--space-5); }
#rf-gate .rf-gate-brand .brand-mark { height: 2.1rem; width: auto; }
#rf-gate .rf-pitch { font-family: var(--font-display);
  font-size: var(--font-size-xl); line-height: 1.15;
  margin: 0 0 var(--space-4); color: var(--text);
  text-wrap: balance; }
#rf-gate h2 { margin: 0 0 var(--space-2); font-size: var(--font-size-md); }
#rf-gate .hint { margin: 0; max-width: 30rem; }
#rf-gate button { margin: var(--space-4) 0 0; }
#rf-gate .rf-points { list-style: none; margin: var(--space-5) 0 0;
  padding: 0; display: grid; gap: var(--space-2);
  font-size: var(--font-size-sm); color: var(--muted); }
#rf-gate .rf-points li { display: flex; gap: var(--space-2); }
/* a felling-column tick, echoing the wordmark's I */
#rf-gate .rf-points li::before { content: ""; flex: 0 0 3px;
  height: 1.05em; border-radius: 4px;
  background: var(--color-accent); opacity: 0.8; }
#rf-gate .rf-hero-note { position: absolute; left: var(--space-6);
  bottom: var(--space-5); margin: 0;
  font-family: var(--font-mono); font-size: var(--font-size-xs);
  color: var(--muted); opacity: 0.75; max-width: 22rem; }
@media (max-width: 40rem) {
  #rf-gate .rf-hero::after {
    background: linear-gradient(180deg, transparent 0%, var(--bg) 58%); }
  #rf-gate .rf-gate-inner { justify-content: flex-end;
    padding-bottom: var(--space-6); max-width: none; }
  #rf-gate .rf-hero-note { position: static; margin-top: var(--space-3); }
}
.rf-user { font-size: var(--font-size-xs); color: var(--muted);
  white-space: nowrap; }

/* ------------------------------------------- RazeForce chrome (D206) */

/* The drill-and-charge elevation (blastplan.js) is ONE SVG string shown
   both here and in the printed method statement, so its inks are vars
   whose fallbacks are the paper values. On dark, the hole marks take the
   READABLE red (crimson on near-black is nearly invisible) and the
   dimension lines lift to steel. */
:root, :root[data-theme='dark'] {
  --blast-hole: var(--color-dangerText);
  --blast-dim: var(--color-rf-steelGrey);
}
:root[data-theme='light'] {
  --blast-hole: var(--color-rf-crimson);
  --blast-dim: #5a6472;
}

/* The masthead: wordmark over a small-caps sub-brand line. Stacked so the
   two read as one lock-up rather than two labels. */
.brand-lockup { display: inline-flex; flex-direction: column;
  gap: 1px; line-height: 1; }
.brand-sub { font-family: var(--font-family); font-size: 9px;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--color-rf-steelGrey); font-weight: 600; }
.brand-sub .rf-name { color: var(--muted); }

/* The access key is legacy plumbing next to a real sign-in (D204), so it
   sits at the bottom of the hierarchy — muted, quiet, not a control the
   eye lands on. */
#api-key-field { color: var(--color-rf-steelGrey);
  font-size: var(--font-size-xs); }
#api-key-field input { background: var(--bg);
  border-color: var(--border); min-height: 30px;
  font-size: var(--font-size-xs); }
#api-key-field input:hover { border-color: var(--border-bright); }

/* Signed-in identity (D204's #rf-user) reads as a plate, not a button. */
.rf-user { font-family: var(--font-mono);
  color: var(--color-rf-steelGrey);
  border: 1px solid var(--border); border-radius: var(--radius-tag);
  padding: 2px var(--space-2); }
/* #rf-account (D213) shares the plate look but IS clickable — an <a>,
   not `button.ghost` (that class is button-scoped and does nothing on an
   anchor), so its own affordance is set here rather than inherited. */
a#rf-account { text-decoration: none; cursor: pointer; }
a#rf-account:hover { border-color: var(--border-bright); color: var(--text); }

/* Topbar nav: quiet dark buttons that go hot-red on hover/active. */
.topbar button.ghost { border-color: var(--border); }
.topbar button.ghost:hover:not(:disabled),
.topbar button.ghost[aria-expanded="true"] {
  border-color: var(--color-accent); color: var(--color-rf-offWhite);
  background: var(--color-accentSoft); }

/* The step strip: Archivo Black numerals, hot-red for the step you are
   on. An industrial progress bar, not a breadcrumb. */
.rail-mark { font-family: var(--font-display); font-weight: 400;
  border-radius: var(--radius-pill); }
.rail-row.active .rail-mark {
  box-shadow: 0 0 0 3px var(--color-accentSoft); }
.rail-row { border-radius: var(--radius-md); }
.rail-row.active { border-left: 3px solid var(--color-accent); }

/* Gold is a SPARING accent — a hairline on the one thing that is a
   provisional figure, never a fill. */
.chip.assumed, .prov-assumed { border-color: var(--color-rf-gold); }

/* The viewer plate (D494): read-only is a ROLE, not an error, so it
   takes the muted/elevated tokens only — no status colour, no red. It
   sits beside the tier badge because both answer "what may I trust /
   do here" before anything else is read. */
#rf-readonly { color: var(--muted); background: var(--elevated);
  border-color: var(--border-bright); cursor: help;
  white-space: nowrap; }
/* Authoring entries muted by the viewer band (readonly.js). Buttons
   already carry the global :disabled treatment; this covers the file
   inputs and keeps the whole set visually one state — LOOKING off is
   the point (D115), the server refusal is the gate. */
.read-only-muted:disabled, .read-only-muted[aria-disabled="true"] {
  opacity: 0.45; cursor: not-allowed; }

/* The tier badge (UI rule 1: prominent on every screen, never
   ignorable) was a light pill designed for a paper topbar. On dark it
   read as a stray light island — so it becomes an OUTLINED plate: the
   tier colour carries the meaning as text + ring, not as a slab. */
.tier-badge { color: var(--color-tier-L0);
  background: transparent; border-radius: var(--radius-tag);
  box-shadow: inset 0 0 0 1px currentColor;
  font-family: var(--font-mono); font-weight: 600;
  letter-spacing: 0.02em; }
.tier-badge[data-tier="L1"] { background: transparent;
  color: var(--color-tier-L1); }
.tier-badge[data-tier="L2"] { background: transparent;
  color: var(--color-tier-L2); }
.tier-badge[data-tier="L3"] { background: transparent;
  color: var(--color-tier-L3); }
/* the printed deliverables keep the filled pill — a ring on paper
   reads as a form field, and their ground is light anyway */
:root[data-theme='light'] .tier-badge {
  color: #111; background: var(--color-tier-L0);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12); }
:root[data-theme='light'] .tier-badge[data-tier="L1"] {
  background: var(--color-tier-L1); }
:root[data-theme='light'] .tier-badge[data-tier="L2"] {
  background: var(--color-tier-L2); }
:root[data-theme='light'] .tier-badge[data-tier="L3"] {
  background: var(--color-tier-L3); }

/* The step strip is the industrial progress bar: Archivo Black numerals,
   hot-red on the step you are on, a rule under the whole thing so it
   reads as one instrument rather than three words. */
/* Slice-2 (D452): the coarse act-nav is KEPT (D280 — it is the only
   journey indicator once the rail's screen is left, on Run / Results /
   Deliverable) but QUIETED. It used to paint the current act hot-red
   with the display face, so on the workspace screen it competed head-on
   with the rail's own red current-step and piled onto the red overload
   (audit H1 + M1). Now it reads as a recessive breadcrumb: the current
   act is bone (emphasis by weight, not accent), past acts are muted with
   a soft green ✓, and red is reserved for the one primary action. */
#steps { border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-2); }
#steps li::before { font-family: var(--font-display); font-weight: 400;
  letter-spacing: 0.02em; }
#steps li.current { color: var(--text); }
#steps li.current::before { color: var(--text); }
#steps li.done { color: var(--muted); }
#steps li.done::before { color: var(--color-okText); }

/* Per-neighbour collapse outcome (D231). The row colour encodes the
   MEASURED level, so a struck structure is visible without reading the
   sentence — the same status family as everywhere else (never a new hue). */
#nbr-rows { width: 100%; border-collapse: collapse; margin-top: var(--space-2); }
#nbr-rows th, #nbr-rows td { padding: var(--space-1) var(--space-2);
  text-align: left; border-bottom: 1px solid var(--border); }
#nbr-rows th { font-size: 0.85em; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted); font-weight: 600; }
#nbr-rows td.num { text-align: right; font-family: var(--font-mono);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
#nbr-rows tr.nbr-struck { background: var(--color-dangerSoft); }
.ss-tbl { border-collapse: collapse; width: 100%;
  margin-top: var(--space-2); }
.ss-tbl th, .ss-tbl td { padding: var(--space-1) var(--space-2);
  text-align: left; border-bottom: 1px solid var(--border); }
.ss-tbl th { font-size: 0.85em; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted); font-weight: 600; }
.ss-tbl td.num { text-align: right; font-family: var(--font-mono);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.ss-tbl tr.ss-stop td { background: var(--color-dangerSoft); }
.ss-tbl tr.ss-stop td:last-child { color: var(--color-dangerText); }
#nbr-rows tr.nbr-struck td:last-child { color: var(--color-dangerText); }
#nbr-rows tr.nbr-debris { background: var(--color-warnSoft); }
#nbr-rows tr.nbr-debris td:last-child { color: var(--color-warnText); }
#nbr-rows tr.nbr-clear td:last-child { color: var(--color-okText); }
/* multi-target group header inside the neighbour table (D108 shape): a
   structure can be clear of one demolition and struck by another, so each
   target gets its own block rather than one merged verdict. */
#nbr-rows th.nbr-head { padding-top: var(--space-3);
  color: var(--color-accent); text-transform: none; font-size: 0.95em;
  letter-spacing: 0; }
/* Coherence read-out (D232): the band is encoded in colour so "came apart
   in the air" is visible before the sentence is read. Status family only. */
#coherence-note[data-level="body"] { color: var(--color-okText); }
#coherence-note[data-level="partial"] { color: var(--color-warnText); }
#coherence-note[data-level="fragmented"] { color: var(--color-dangerText); }
/* D334: a run that never landed makes no coherence claim at all — its
   count is breakage-so-far, so the note must not wear a verdict colour
   (a green "body" over a structure still falling is the flattering
   direction, the documented misreading). */
#coherence-note[data-level="provisional"] { color: var(--color-warnText); }

/* Typology limits, stated on the row that produced them (D260). A
   measured limit — like a squat silo not coming down from a base mouth
   cut — belongs beside the height and diameter that trigger it, not in a
   warnings panel three screens away. */
.type-note { margin: var(--space-1) 0 0; font-size: var(--font-size-sm);
  padding: var(--space-2); border-radius: var(--radius-sm);
  border-left: 3px solid var(--color-tier-L1); background: var(--surface); }
.type-warn { border-left-color: var(--color-warning); }
.type-note-note, .type-note { color: var(--text-muted); }
.type-warn { color: var(--text); }

/* The doors (D276) — the empty state that starts a job. Deliberately
   plain: three equal choices, no hierarchy between them, because which
   one is fastest depends entirely on what the contractor has in front
   of them. Hidden by `[hidden]` the moment anything is placed. */
#ws-doors { margin-bottom: var(--space-6, 32px); }
.door-list { list-style: none; padding: 0; margin: 0; display: grid;
  gap: var(--space-3, 12px);
  /* CONTAINER-responsive, never viewport-responsive. This was a
     `@media (min-width: 720px) { repeat(3, 1fr) }`, and the media query
     asks about the WINDOW while the doors live in the workspace's
     middle column — which is ~330 px between the rail and the canvas.
     So on a wide monitor the query fired, three columns were forced
     into 330 px, and each door rendered about one word per line.
     `auto-fit` + `minmax` asks the only question that matters: how wide
     is the box these are actually in.

     The floor is 260 px rather than something tighter because there
     are FOUR doors: at 210 px a 714 px column fits three and orphans
     the fourth onto its own row, which reads as one door being
     different from the others when the whole point is that they are
     equal ways in. 260 gives a clean 2 x 2 here and 4 across on a wide
     screen. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.door { display: flex; flex-direction: column; gap: 6px; width: 100%;
  height: 100%; text-align: left; padding: var(--space-4, 16px);
  border: 1px solid var(--color-border, #272727);
  border-radius: var(--radius-card, 12px);
  background: var(--color-surface-raised, #1a1a1a);
  color: inherit; cursor: pointer;
  transition: border-color var(--dur-fast, .12s),
              background var(--dur-fast, .12s); }
.door:hover, .door:focus-visible {
  border-color: var(--color-accent, #C0392B);
  background: var(--color-surface, #111); }
.door-title { font-weight: 600; font-size: 1.05rem; }
.door-note { font-size: .875rem; color: var(--color-text-muted, #c7c7c7);
  font-weight: 400; line-height: 1.45; }

/* Mobile view-first landing (D457). Hidden on desktop entirely — the
   authoring doors are the desktop entry, unchanged. On a phone it leads
   the doors screen so the primary action is REVIEWING a study, not
   building one. Shown by the mobile media query below. */
.mobile-first { display: none; }
.mobile-first > button { width: 100%; min-height: 48px;
  font-size: var(--font-size-md); margin: var(--space-2) 0; }
.mobile-first > p { color: var(--muted); }
.mobile-first > h1 { margin-bottom: var(--space-2); }
/* The "show on mobile" rule MUST live after the base `display:none` above:
   a @media query adds no specificity, so if it sat earlier in the file the
   later base rule would win and the block would never appear (D457, learned
   by rendering — it was invisible until this moved). */
@media (max-width: 768px) { .mobile-first { display: block; } }

/* A locked rail row (D278) — the journey's post-run acts before a run
   exists. Visibly not-yet rather than merely dim: the reason sits on the
   row, so the click is never where someone finds out (D115). */
.rail-row.locked { cursor: default; opacity: .62; }
.rail-row.locked:hover { background: none; }
.rail-why { display: block; font-size: .75rem;
  color: var(--color-text-muted, #c7c7c7); font-style: italic; }

/* Wide panels SCROLL rather than clip (D293).
 *
 * MEASURED at three widths: the proximity plan's SVG is 630 px of
 * content, and on a 1280 px laptop it had 436 px to sit in — 194 px, or
 * 31 % of the safety plan, simply gone, with `overflow-x: visible` and
 * no way to reach it. That drawing carries the debris rings and the
 * measured neighbour distances, so the part a contractor cannot see is
 * the part the plan exists for. At 1440 px it was still 95 px short.
 *
 * The body must never scroll sideways — it does not, which is why this
 * was invisible: an ancestor was clipping silently. So the CONTENT gets
 * its own scroll instead. Same rule the printed deliverables already
 * follow for wide tables.
 */
#proximity-box, #copilot-card, #fall-measure,
#sequence-timeline, #sequence-timeline-plan, #blastseq-editor {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
/* the SVG keeps its true size inside that scroller — shrinking it to
   fit would make a scale plan lie about its scale */
#proximity-box > svg { max-width: none; }

/* A card's own title owns its line (D293). These were `<strong>` inside
   a `nav-row`, so the title, its button and its explanatory note laid
   out side by side — "View the collapse in 3D on the / map" wrapping
   mid-phrase into a narrow column beside a button, reading as three
   unrelated fragments rather than one card. */
.card-lead { display: block; font-size: 1.05rem; font-weight: 600;
  margin-bottom: var(--space-2, 8px); }

/* "Why this matters", collapsed (D294).
   The paragraph's OWN first sentence is the summary — nothing is
   reworded, so a caveat cannot be misstated by a signpost I wrote. A
   `.why` block carries PROSE ONLY: a control inside a `<details>`
   nobody opens is unreachable (D218), which is why that is gated. */
details.why { margin: var(--space-2, 8px) 0 var(--space-3, 12px); }
details.why > summary { cursor: pointer; list-style: none;
  font-size: var(--font-size-sm); color: var(--text);
  padding: 2px 0; }
details.why > summary::-webkit-details-marker { display: none; }
details.why > summary::after { content: " — why?"; color: var(--muted);
  font-style: italic; }
details.why[open] > summary::after { content: " — why?  ▾"; }
details.why > summary:hover { color: var(--color-accent); }
details.why > p { margin-top: var(--space-2, 8px); }
