/* layout.css — The page grid: body rows, .main, the .stage shell, .sheet and its scroller.
   Cascade position 5 of 18. Order is load-bearing and is asserted by
   verify-fixtures.mjs; index.html links these explicitly (never @import) because Caddy's
   try_files turns a missing file into a 200 text/html SPA shell.
   Owner after Step 1: section (o).

   Section (o), runs alone after (a)-(n). Items 16/24 (mobile half), 51, 52, 53, 55, 56,
   57, 94, 112, 113, 118, 138, 147, plus the 6 Sep testing-surface switch. The authority
   is DESIGN-SPEC §3 — §3.2's grid table, §3.5's stacking and scroll model, §3.6's
   wireframes — with COMPONENT-PLAN §2(o) and mocks/NOTES.md §B9/§F on top of it.

   THIS FILE OWNS THE CONTAINING BLOCKS, not the components inside them. Four rules below
   reach into a box another section styles (.sheet__dock, .prep__head p, .top's file chip
   is in topbar.css and is handled there). Each says why it is here, and each is filed to
   its owner in /tmp/sn-ui/batch3-contract.md to be folded home. Nothing else in any other
   component stylesheet is touched. */

/* ══ 0. The named z-index scale (item 94) ═══════════════════════════════════════════════
   Before this block the file had eight raw z-indexes that could not be compared, because
   `.sheet__dock`'s `backdrop-filter` made the dock a stacking context and a Backdrop Root:
   its `z-index: 20` could never beat `.prep`'s `12` no matter what number it held
   (IMPLEMENTATION-PLAN §5.2). With the filter gone (see §3 below) the numbers mean what
   they say for the first time, so they get names.

   Two stacking contexts, and only two, are opened deliberately:
     · `.stage`  — `isolation: isolate`. Everything the viewer paints (bands, the intake
       veil, the drop card) is scoped inside it, so a viewer overlay can never be compared
       against a rail overlay by accident.
     · `.sheet__dock` — a `position: sticky` box WITH a z-index, which is what makes it
       paint over the rail content sliding under it.

   Above every number here sits the browser's TOP LAYER, which is where the auth dialog,
   the passcode gate, the account menu, the download menu and every tooltip now live
   (5 Sep native-components decision). Nothing in this scale competes with them, and that
   is the point: items 14, 57 and 77 were closed by leaving the z-index race, not winning it. */
:root {
  /* inside .stage's own context */
  --sn-z-band: 5;          /* the four viewer overlay bands (css/viewer.css) */
  --sn-z-busy: 6;          /* the intake veil (css/progress.css) */
  --sn-z-stage-card: 7;    /* the empty-state / refusal card (css/drop.css) */
  --sn-z-toolbar: 8;       /* the floating viewport toolbar + crumb (css/shell.css), above the card */
  /* inside .sheet */
  --sn-z-dock: 3;          /* retired with the sticky dock (UI Quiet); kept so the name still resolves */
  --sn-z-prep: 12;         /* the preparing takeover, covers the whole rail incl. the dock */
  /* document level */
  --sn-z-header: 30;       /* header.top, if it ever needs one — measured: it does not */
  --sn-z-runlog: 40;       /* the floating run log (testing-phase surface) */
  --sn-z-toast: 200;       /* #toastHost (css/notices.css) */
}

/* ══ 1. The workspace grid — UI Quiet (23 Sep), DESIGN §3 ════════════════════════════════
   left pane · viewport · right rail, 8 px apart and 8 px from the window edge, under the
   56 px header. The panes are cards on the page (surface-1, radius 12); the page itself
   is surface-0. SUPERSEDES the two-column split below, which is kept as the record:
     the old `.main` was `minmax(0, 1fr) clamp(420px, 37.5vw, 720px)` — viewer left, a
     720/540/480/420 px run sheet right — and `body.is-empty` collapsed it to one column.
   In the Quiet layout the left pane is always there (it carries "New print" and the
   locked steps in the empty state), so the is-empty collapse is gone.

   `minmax(0, 1fr)` on the viewer and `min-height: 0` on the grid are item 53's fix and
   still hold: a bare `1fr` has an auto minimum and lets a tall child push the row past the
   window. `overflow: clip` keeps nothing inside from becoming unreachable overflow. */
:root {
  --sn-pane-w: 360px;   /* ≥ 1501 w; 336 below (DESIGN §3) */
  --sn-rail-w: 64px;    /* the collapsed rail; it expands as an OVERLAY, never reflows */
}
@media (max-width: 1500px) { :root { --sn-pane-w: 336px; } }
.main {
  display: grid;
  grid-template-columns: var(--sn-pane-w) minmax(0, 1fr) var(--sn-rail-w);
  gap: var(--sn-space-2);
  padding: 0 var(--sn-space-2) var(--sn-space-2);
  min-height: 0; overflow: clip;
}

/* Prepared-file history is an account destination, not a model setting. UI Quiet: it is
   now the #/files page (js/shell.js re-parents #historyPanel into #filesPage and the whole
   workspace <main> is hidden by html[data-route="files"]). These three rules only matter
   in the one microtask between history.js opening its panel and the route catching up,
   and they keep that frame free of a flash; lane 3 deletes them with the panel. */
body.is-empty.history-open .stage { display: none; }
body.is-empty.history-open .sheet { display: grid; border-left: 0; }
body.is-empty.history-open .sheet__scroll > :not(#historyPanel) { display: none; }

/* ══ 2. The stage ═══════════════════════════════════════════════════════════════════════
   The canvas has an intrinsic aspect ratio. Letting its grid item keep an auto minimum
   height enlarges the shared row when the window grows. `isolation: isolate` is the
   stacking context named in §0: it costs nothing, opens no containing block for fixed
   descendants, and stops the viewer's five overlays from being in the same z-index race
   as the rail's one. */
.stage {
  position: relative; isolation: isolate; min-width: 0; min-height: 0;
  background: radial-gradient(80% 70% at 50% 42%, var(--sn-surface-2-alt) 0%, var(--sn-surface-0) 72%);
  /* A card like the two panes beside it (DESIGN §3). `clip`, not `hidden`: no scrollport. */
  border-radius: var(--sn-radius-md); overflow: clip;
}
.stage canvas { display: block; width: 100%; height: 100%; }

/* ══ 3. The rail, its ONE scroller, and the sticky dock ═════════════════════════════════
   DESIGN-SPEC §3.5. `.sheet` is a single-row grid rather than a flex column so the row
   itself carries `minmax(0, 1fr)`: the scroller is then bounded by the rail and the rail
   by .main, with no auto minimum anywhere on the chain from `body` down. That chain is
   item 53's actual mechanism, and it is why `align-self: stretch` (dash-findings L-5's
   proposed fix) was a no-op — .sheet was already stretched; what it lacked was a zero
   minimum on its own row.

   `position: relative` here is load-bearing beyond appearances: it is the containing block
   for `.prep`, which is `position: absolute; inset: 0` in css/progress.css. IMPLEMENTATION
   -PLAN §5.3 warns that .prep "follows whatever .sheet becomes". It still does — and that
   is now the correct behaviour rather than a hazard, because .sheet is this file's element
   and it stays the rail. Deriving the overlay's box from the rail itself keeps ONE source
   of truth for the rail geometry; a `position: fixed` overlay pinned to the grid area, as
   §3.5 pt 5 sketches, would restate the clamp above in a second place and the two would
   drift the first time either changed. MEASURED identical at all five viewports — see the
   lane report. */
/* UI Quiet: three rows now — #paneHead, the scroller, #paneFoot — and the row that
   scrolls is still the only one with `minmax(0, 1fr)`, so the chain from body down has no
   auto minimum anywhere (item 53). The border became a card edge: surface-1, radius 12. */
.sheet {
  background: var(--sn-surface-1); border-radius: var(--sn-radius-md);
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 0; min-width: 0; position: relative; overflow: clip;
}

/* The ONLY scroll container in the rail. `overscroll-behavior: contain` stops a flick at
   the end of the rail from becoming a scroll of the page behind it. */
.sheet__scroll { min-height: 0; overflow-y: auto; overscroll-behavior: contain; }

/* ── the dock (items 52, 53, 112, 113, 118) — MOVED, UI Quiet (23 Sep) ──────────────────
   The dock was the sticky last child of this scroller (`.sheet .sheet__dock { position:
   sticky; bottom: 0; backdrop-filter: none }`). DESIGN §5.4 puts the primary action in the
   floating viewport toolbar, so #sheetDock now lives in #viewToolbar inside #stage and its
   geometry is css/shell.css's. Items 52/53 cannot come back: the pane's scroller no longer
   contains anything that can grow the pane. */

/* The two blocks that left the dock have to bring the dock's gutter with them. MEASURED
   after the move and before this rule: #loadNotice's own padding is `1px 0 1px 11px`, so
   with the dock's `0 16px` gone its sentence ran to x = 1440 — hard against the right edge
   of the window, with 0 px of gutter. #notice was flush on both sides for the same reason.
   Their neighbours in the scroller are full-bleed sections with internal padding; these two
   are tinted CARDS, so they are inset instead, on the dock's 16 px gutter so the column
   still reads as one edge. */
.sheet__scroll > #notice,
.sheet__scroll > #loadNotice { margin-inline: var(--sn-space-4); margin-bottom: var(--sn-space-3); }

/* `.prep` covers the rail INCLUDING the sticky dock: 12 over 3, in .sheet's context. The
   number is restated from the scale here so the two are visibly the same decision. */
.sheet .prep { z-index: var(--sn-z-prep); }

/* ── item 55: one text measure, and it is the rail's ────────────────────────────────────
   The `34ch` caps were written when the rail was 392 px and prose still wrapped; at a
   480-720 px rail a 34ch paragraph leaves half its container empty. DESIGN-SPEC §3.5 sets
   the measure at `68ch`, chosen precisely so that it never binds inside a rail this wide —
   the cap exists to stop prose running past ~75ch, not to narrow it.
   `.prep__head p` is css/progress.css's (section l) and is the ONE 34ch cap that sits
   inside the rail; the override is here rather than there for the same reason as the dock,
   and is filed back. The other two 34ch caps are NOT defects: css/drop.css's is on the
   centred upload card, where a measure is exactly right, and css/feedback.css's is on a
   surface the 6 Sep scope cut hides in production. */
.sheet .prep__head p { max-width: 68ch; }

/* ══ 4. Below 940 px the grid stacks — ONE block, not three (item 56) ═══════════════════
   940 keeps its number so the shipped mobile layout is not disturbed; DESIGN-SPEC §3.2
   settles that this is the only stacking breakpoint. The viewer takes 46vh and the rail
   takes the rest, both with a zero minimum so neither can push the other off the screen.
   The two `min-width` breakpoints DESIGN-SPEC §3.2 asks for are not here: the dossier's
   two-up is `@media (min-width: 1024px)` in css/result.css (section j, mocks/NOTES.md
   §B9), and the 1600 one is not written at all — section (n) built the overlay bands with
   `max-width: min(360px, 40%)`, which reaches its full width the moment the stage passes
   900 px. A media query that changes nothing is worse than no media query. */
@media (max-width: 940px) {
  /* UI Quiet: the viewport first (it is where a file is dropped and the primary action
     lives), then the pane; the rail is dropped and "Your files" stays in the header. */
  .main { grid-template-columns: minmax(0, 1fr); grid-template-rows: 46vh minmax(0, 1fr); min-width: 0; }
  .main > .stage { grid-row: 1; }
  .main > .sheet { grid-row: 2; }
  .main > .rail-pane { display: none; }
  /* The feedback panel is a testing-phase surface (§5 below), so the geometry it asks for
     is scoped to the switch: with the switch off the class can still be set by
     js/feedback.js on a panel nobody can see, and it must not shrink the viewer. */
  html[data-testing] .main.generation-feedback-active { grid-template-rows: minmax(148px, 30vh) minmax(0, 1fr); }
  html[data-testing] .sheet.generation-feedback-active .sheet__dock .cta__note { display: none; }
}

/* ══ 5. THE TESTING SWITCH — five surfaces, one attribute ═══════════════════════════════
   Aditya's 6 Sep scope cut: some surfaces exist only while we are testing and must be off
   in production, without being deleted from the codebase. index.html's head sets
   `<html data-testing>` on stage and on the local dev hosts and nowhere else, and this is
   the ONLY block that reads it.

   Every selector is an ID, deliberately. An id (1,0,0) outranks any class rule in any
   component stylesheet at any cascade position, so no later file can un-hide a surface
   production is not supposed to have — including `.runlog.on { display: flex }`, which a
   class-based rule here would lose to on source order.

   Not `!important`: item 94 holds the `!important` count where it is, and an id already
   wins. The one surface CSS cannot safely handle is #gate, because a hidden modal
   <dialog> is an inert page with no way out — index.html's end-of-body guard takes that
   door off its hinges instead, and the rule below is only for the closed element. */
html:not([data-testing]) #gate,
html:not([data-testing]) #runLog,
html:not([data-testing]) #btnRunLog,
html:not([data-testing]) #resultActions,
html:not([data-testing]) #feedbackForm,
html:not([data-testing]) #feedbackStatus,
html:not([data-testing]) #generationFeedbackPanel { display: none; }

/* Hiding a button is not the same as removing it from a row that also has separators.
   MEASURED: with #btnRunLog hidden the toolbar was 588.41 px against section (n)'s 575.36
   for the same row without that button — 13.05 px of dangling separator and its two gaps,
   trailing off the end of the toolbar with nothing after it. `:has(+ #btnRunLog)` names
   the one separator that belongs to the hidden button, and it removes a JS branch rather
   than adding one, which is the condition COMPONENT-PLAN §2(o) puts on using it. */
html:not([data-testing]) #tools .sep:has(+ #btnRunLog) { display: none; }
