/* ============================================================
   coachOS Shell — Stage/Dock ZoneLayout catalog (BS-3)
   ------------------------------------------------------------
   The locked zone configurations (shell_zone_catalog.md, 2026-07-08;
   rail-stack added by user ruling 2026-07-09): 10 Stage configs +
   4 Dock configs, addressed by semantic id — the id is the text enum
   a Setting persists (`lead-aside`, never an alias).

     Stage  .shell__stage--<id>   reader · canvas · duo · lead-aside ·
                                  stack-rail · rail-stack · thirds ·
                                  header-split · split-footer · quad
     Dock   .shell__dock--<id>    duo · thirds · quarters · quint

   Mobile-first: every Stage config is a single column with zones
   stacking in z-order (z1, z2, …); desktop grids apply at ≥768px.
   Configs never touch Stage padding — the standard/bleed contract
   (shell.css) stays the single source of inset; gap and the banner
   strip derive from the same --content-pad so they track it.

   Per-zone toggles (catalog defaults all off/none; the Shell payload
   overrides per zone):
     scroll     .shell__zone--scroll         (desktop-only fill-stage)
     scrollbar  .shell__zone--no-scrollbar
     padding    .shell__zone--pad
     bleed      .shell__zone--bleed        (payload owns the Card, edge to edge)
     style      .shell__zone--card | --well | --outline   (enum; none = flat)

   Stacking-surfaces rule (locked): a STYLED zone owns the surface —
   any direct-child .card auto-flattens. The payload cannot stack a
   card on a styled zone by accident; deliberate card compositions
   nest inside a plain wrapper (no longer direct children).

   All values are existing design-system tokens (+ --stage-max-width,
   approved 2026-07-08). User visual approval: 2026-07-08 (VC pass).
   ============================================================ */

/* ── STAGE configs · mobile-first base ─────────────────────── */
.shell__stage--reader, .shell__stage--canvas, .shell__stage--duo,
.shell__stage--lead-aside, .shell__stage--stack-rail, .shell__stage--rail-stack,
.shell__stage--thirds,
.shell__stage--header-split, .shell__stage--split-footer, .shell__stage--quad {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--content-pad);
  align-content: start;
  /* px, NOT rem (locked "fixed 22rem" = 352px at the standard 16px root):
     html[data-mode] scales the root, so a rem aside was ~308px on admin vs
     352px on member surfaces — Shell geometry is mode-immune (2026-07-09). */
  --zone-aside: 352px;
}
.shell__stage--canvas { gap: 0; }

/* Canvas is fill-stage: always the full workspace height below the Crown
   (down to the Dock when present, else the workspace bottom). Editors and
   tools (Org Editor, Experience Editor, Participation Hub) declare canvas
   so their surfaces stretch; long-form configs (reader/duo/…) stay
   content-sized with workspace scroll. */
.shell__workspace:has(> .shell__stage > .shell__stage__body.shell__stage--canvas) {
  overflow: hidden;
}
.shell__stage:has(> .shell__stage__body.shell__stage--canvas) {
  flex: 1 1 0;
  min-height: 0;
}
.shell__stage__body.shell__stage--canvas {
  flex: 1 1 0;
  min-height: 0;
  align-content: stretch;
  grid-template-rows: minmax(0, 1fr);
}

/* reader centers one column at the content measure (all widths) */
.shell__stage--reader {
  grid-template-columns: minmax(0, var(--content-max-width));
  justify-content: center;
}

.shell__stage__body, .shell__zone { min-width: 0; }

/* Every zone is a content-height surface, never stretched to a taller row
   sibling (user ruling 2026-07-11 — extends the 2026-07-09 scroll-zone ruling
   to ALL zones): break the grid's default item stretch. Fill-stage scroll
   zones still cap at their track (.shell__zone--scroll max-height below). */
.shell__zone { align-self: start; }

/* Multi-zone grids cap left-anchored on large displays — leftover space
   grows on the right (approved). canvas stays full-bleed; reader has its
   own centered measure above. */
.shell__stage--duo, .shell__stage--lead-aside, .shell__stage--stack-rail,
.shell__stage--rail-stack,
.shell__stage--thirds, .shell__stage--header-split, .shell__stage--split-footer,
.shell__stage--quad {
  max-width: calc(var(--stage-max-width) + 2 * var(--content-pad));
}

/* ── STAGE configs · desktop grids (≥768px, Convention 11) ─── */
@media (min-width: 768px) {
  .shell__stage--duo          { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shell__stage--lead-aside   { grid-template-columns: minmax(0, 2fr) minmax(0, var(--zone-aside)); }
  .shell__stage--stack-rail   { grid-template-columns: minmax(0, var(--zone-aside)) minmax(0, 1fr); }
  .shell__stage--stack-rail .shell__zone--z1,
  .shell__stage--stack-rail .shell__zone--z2 { grid-column: 1; }
  .shell__stage--stack-rail .shell__zone--z3 { grid-column: 2; grid-row: 1 / span 2; }
  /* rail-stack = stack-rail flipped (user-approved catalog addition 2026-07-09):
     z1 full-height fluid rail LEFT, z2/z3 stacked in the fixed aside RIGHT. */
  .shell__stage--rail-stack   { grid-template-columns: minmax(0, 1fr) minmax(0, var(--zone-aside)); }
  .shell__stage--rail-stack .shell__zone--z1 { grid-column: 1; grid-row: 1 / span 2; }
  .shell__stage--rail-stack .shell__zone--z2,
  .shell__stage--rail-stack .shell__zone--z3 { grid-column: 2; }
  .shell__stage--thirds       { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .shell__stage--header-split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shell__stage--header-split .shell__zone--z1 { grid-column: 1 / -1; }
  .shell__stage--split-footer { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shell__stage--split-footer .shell__zone--z3 { grid-column: 1 / -1; }
  .shell__stage--quad         { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Per-zone toggles ──────────────────────────────────────── */
/* Zone geometry is Shell geometry: paddings are px/vw, NOT the rem space
   tokens — html[data-mode] scales the root, so a rem padding differed across
   the admin vs member family seam (user ruling 2026-07-09: every such
   difference is removed or user-approved).
   Fluid (user ruling 2026-07-11): 6px at ≤375px growing continuously (1.6vw)
   to the unchanged 16px desktop value (the 2026-07-09 BS-3-derived value) at
   ~1000px. One variable feeds the pad toggle AND the card/well/outline style
   surfaces. */
/* Floor is 12px, the tight end of consumer-grade (iOS/Material default 16px;
   this is deliberately more compact) — 6px read as cramped for body text against
   a phone edge (user gate 2026-07-23, on the participation paper zone). Every
   styled zone + the --pad toggle share this, so the tight mobile gutter is one
   value app-wide. Desktop is unchanged at the 16px ceiling. */
.shell__zone { --zone-pad: clamp(12px, 1.6vw, 16px); }
.shell__zone--pad { padding: var(--zone-pad); }

/* scroll — desktop-only by design: on mobile the page scrolls, never a
   zone. A Stage with any scrolling zone becomes a fill-stage (exactly the
   visible workspace height); rows get bounded so zones overflow internally. */
@media (min-width: 768px) {
  /* Scroll zones hug their content and cap at the grid track (align-self
     breaks the default stretch — a styled zone is a content-height surface,
     never an empty full-column slab; user ruling 2026-07-09). */
  .shell__zone--scroll { overflow-y: auto; min-height: 0; align-self: start; max-height: 100%; }
  /* A scrolling zone makes the Stage fill the workspace and the body (the grid)
     fill the Stage; the body's rows are bounded so the zone overflows internally. */
  .shell__stage:has(.shell__zone--scroll) {
    flex: 1 1 auto;
    min-height: 0;
    padding-bottom: var(--content-pad);
  }
  .shell__stage__body:has(> .shell__zone--scroll) {
    flex: 1 1 auto;
    min-height: 0;
    grid-template-rows: minmax(0, 1fr);
  }
  .shell__stage--stack-rail:has(> .shell__zone--scroll)   { grid-template-rows: auto minmax(0, 1fr); }
  .shell__stage--rail-stack:has(> .shell__zone--scroll)   { grid-template-rows: auto minmax(0, 1fr); }
  .shell__stage--header-split:has(> .shell__zone--scroll) { grid-template-rows: auto minmax(0, 1fr); }
  .shell__stage--quad:has(> .shell__zone--scroll)         { grid-template-rows: repeat(2, minmax(0, 1fr)); }
  /* split-footer has TWO possible scroll positions, so it cannot bound a fixed
     row like the configs above. Bound the row that HOLDS the scroll zone; the
     other row stays content-height. The footer (z3, full-width) scrolling — a
     to-do/report list as the primary panel — must bound ROW 2, or the top split
     (e.g. a Profile card) is squeezed into a 1fr track its content overflows
     onto the list below (the overlapping-zones bug, 2026-07-15). A scroll zone
     in the top split bounds ROW 1 instead. Both scrolling → the z1/z2 rule wins
     (source order): the top list scrolls internally, the footer list expands. */
  .shell__stage--split-footer:has(> .shell__zone--z3.shell__zone--scroll) { grid-template-rows: auto minmax(0, 1fr); }
  .shell__stage--split-footer:has(> .shell__zone--z1.shell__zone--scroll),
  .shell__stage--split-footer:has(> .shell__zone--z2.shell__zone--scroll) { grid-template-rows: minmax(0, 1fr) auto; }
  /* Single-column measures (reader · canvas) carrying a scroll (list) zone
     ALONGSIDE another zone: the leading content zone must keep its natural
     height. The generic fill-stage `grid-template-rows: minmax(0, 1fr)` above
     squeezes it into a flex track its content overflows, landing on the scroll
     zone below — the overlapping-zones bug (a profile card over a to-do list).
     Same correction as stack-rail/header-split: content rows auto, the scroll
     zone fills the remainder. Scoped to multi-zone (`> .shell__zone--z2`) so a
     lone reader/canvas zone (an editor's fill-stage) keeps the single-track fill. */
  .shell__stage--reader:has(> .shell__zone--z2):has(> .shell__zone--scroll),
  .shell__stage--canvas:has(> .shell__zone--z2):has(> .shell__zone--scroll) {
    grid-template-rows: auto;
    grid-auto-rows: minmax(0, 1fr);
  }
}

/* scrollbar (matches the tables.css / question-picker.css precedent) */
.shell__zone--no-scrollbar { scrollbar-width: none; }
.shell__zone--no-scrollbar::-webkit-scrollbar { display: none; }

/* ── style enum: card · well · outline (none = no class, flat) ── */
.shell__zone--card {
  background-color: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: 12px; /* --radius-xl, px (mode-immune) */
  box-shadow: var(--shadow-sm);
  padding: var(--zone-pad);
}
.shell__zone--well {
  background-color: var(--surface-sunken);
  border-radius: 12px;
  padding: var(--zone-pad);
}
.shell__zone--outline {
  border: 1px solid var(--border-subtle);
  border-radius: 12px;
  padding: var(--zone-pad);
}
/* paper — a bordered white sheet (2026-07-23). White fill + subtle border, no
   shadow-lift: the surface the Web Delivery Method step body carries, and a
   fourth choice everywhere a zone style can be set. Distinct from card (which
   adds the shadow) and outline (border only, no fill). */
.shell__zone--paper {
  background-color: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: 12px; /* --radius-xl, px (mode-immune) */
  padding: var(--zone-pad);
}

/* Auto-flatten (locked): the styled zone IS the surface; a direct-child
   .card sheds its container chrome so surfaces never stack. */
.shell__zone--card > .card,
.shell__zone--well > .card,
.shell__zone--outline > .card,
.shell__zone--paper > .card {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}

/* ── bleed — the payload IS the surface, edge to edge ─────────
   The Stage's `--bleed` opt-out, one level down. A fill whose content already
   runs to its own edges (the Profile Hero's wash) must not be inset by the
   zone, or it loses the very pixels it has on its own page.

   CARD only (user ruling 2026-07-14): a Card is a raised surface the payload
   can OWN, so the payload may take it over completely. Well and Outline are
   containers — their inset is the point — and they keep it.

   Declared on the FILL, never in configuration (Shell::ZoneFill#toggles):
   whether a payload bleeds belongs to whoever wrote the partial, not to the
   coach choosing a background. `overflow: hidden` so the payload's own
   backgrounds are clipped by the Card's radius instead of squaring it off. */
.shell__zone--bleed.shell__zone--card,
.shell__zone--bleed.shell__zone--paper {
  padding: 0;
  overflow: hidden;
}

/* ── BANNER · Stage-level band above the zone container ────── */
/* Renders ONLY with a payload — no payload, zero bytes in the HTML
   (locked). Treatment enum: strip (default) · callout · inset. The
   Banner is the first child of .shell__stage, ABOVE .shell__stage__body
   (the width/zone container): strip bleeds full-width WITH the Stage;
   callout + inset share the content width constraint. The callout
   treatment is the plain band hosting a DS .callout — no band chrome. */
.shell__banner--strip {
  margin: calc(-1 * var(--banner-bleed)) calc(-1 * var(--banner-bleed)) 0;
  padding: var(--space-3) var(--content-pad);
  background-color: var(--status-info-bg);
  color: var(--status-info-text);
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--text-sm);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.shell__banner--inset {
  background-color: var(--accent-subtle);
  color: var(--text-primary);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* callout + inset align left with the content width container (the same
   left-anchored cap as multi-zone grids); only strip bleeds full-width. */
.shell__banner:not(.shell__banner--strip) {
  max-width: calc(var(--stage-max-width) + 2 * var(--content-pad));
}

/* ── DOCK configs · zone-count grids ───────────────────────── */
/* Additive: today's data-dock-layout attribute rules (shell.css) keep
   driving the shipped docks until BS-4/BS-5 migrate them onto these. */
.shell__dock--duo .shell__dock-content,
.shell__dock--thirds .shell__dock-content,
.shell__dock--quarters .shell__dock-content,
.shell__dock--quint .shell__dock-content { display: grid; align-items: center; }
.shell__dock--duo .shell__dock-content      { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.shell__dock--thirds .shell__dock-content   { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.shell__dock--quarters .shell__dock-content { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.shell__dock--quint .shell__dock-content    { grid-template-columns: repeat(5, minmax(0, 1fr)); }

/* Button-row docks (duo/thirds — participation Back · … · Next) justify
   start/center/end and breathe like today's zones layout; tab docks
   (quarters/quint) keep self-centering tabs with the indicator hugging
   the chrome's top edge. */
.shell__dock--duo .shell__dock-content > *,
.shell__dock--thirds .shell__dock-content > * {
  flex: unset;
  min-width: 0;
  display: flex;
  align-items: center;
}
.shell__dock--duo .shell__dock-content > :first-child,
.shell__dock--thirds .shell__dock-content > :first-child { justify-content: flex-start; }
.shell__dock--thirds .shell__dock-content > :nth-child(2) { justify-content: center; }
.shell__dock--duo .shell__dock-content > :last-child,
.shell__dock--thirds .shell__dock-content > :last-child { justify-content: flex-end; }
/* No top padding on the button-row dock: since the Dock now matches the Crown
   height (48px mobile), the footer button (btn--lg, 44px) is vertically CENTRED
   in the bar — an 8px top inset would push it past the shorter bar's bottom
   edge (Dock↔Crown match, user ruling 2026-07-23). The button already scales
   down with the scroll-shrink (shell/participation.css) to fit the 40px
   scrolled bar. */
