/* ============================================================
   Capt by Moyo — full-page app styles
   ============================================================ */

/* ─── Locked type stack (Söhne / Söhne Mono) ───
   Trial fonts copied into fonts/ from Klim. The test fonts have a
   LIMITED character set (A–Z, a–z, 0–9, comma, period, hyphen) — any
   character outside that falls back to the system stack. Replace with
   full licensed files when ready.
   (Reckless RETIRED 16 July 2026 — the OLD brand's serif; the wordmark
   is Molly's PNG marks now and nothing renders Reckless. Its @font-face,
   the --wordmark serif value, and the manifest entry were removed.) */
@font-face {
  font-family: "Söhne";
  font-style: normal;
  font-weight: 400; /* Buch */
  font-display: swap;
  src: url("fonts/Soehne-Buch.woff2") format("woff2");
}
@font-face {
  font-family: "Söhne";
  font-style: normal;
  font-weight: 500; /* Kräftig */
  font-display: swap;
  src: url("fonts/Soehne-Kraftig.woff2") format("woff2");
}
@font-face {
  font-family: "Söhne Mono";
  font-style: normal;
  font-weight: 400; /* Buch */
  font-display: swap;
  src: url("fonts/SoehneMono-Buch.woff2") format("woff2");
}

/* ─── The Edits type set ─────────────────────────────────────────────────
   MOVED to fonts.css on 17 Aug 2026 (#284). offscreen.html needs these same
   @font-face rules to composite type into a video — canvas type draws a
   fallback unless its own document declares the face — and two copies of a
   font block is two places to keep in step. app.html links fonts.css before
   this file; Söhne above stays here, as Loey's own chrome. */

/* ---------- Tokens (light) ---------- */
.app {
  /* ─── Capt palette (locked, monochrome — no chromatic accents) ─── */
  --canvas: #E6E6E6;     /* App background */
  --paper: #FAFAFA;      /* Surfaces (panels, cards, inputs) */
  --ink: #0E0E0C;        /* Primary type and primary fill */
  --ink-soft: #3A3A38;   /* Secondary type */
  --ink-mute: #8A8A88;   /* Muted type, placeholders, inactive */
  --rule-faint: #D4D4D2; /* Hairlines, borders, dividers */

  /* ─── Compatibility aliases for existing callsites ─── */
  /* App background pinned to paper for now per Mark — Canvas
     #E6E6E6 reserved for a future elevation pass. Cards already use
     --bg2 (paper) so the surface and the shell share the same tone;
     elevation between them is carried by borders/shadows. */
  --bg: var(--paper);
  --bg2: var(--paper);
  /* Work-area (centre .main) background. Paper look = a whisper of ink over
     paper; the Warm-grey look repoints this to --canvas. */
  --work-bg: rgba(14, 14, 12, .035);
  --ink65: rgba(14, 14, 12, .65);
  --ink50: rgba(14, 14, 12, .50);
  --ink32: rgba(14, 14, 12, .32);
  --ink18: rgba(14, 14, 12, .18);
  --ink08: rgba(14, 14, 12, .08);
  /* Accordion header band. Default = a whisper of ink; the Soft and Warm
     looks repoint these to solid tones so the bands carry the look. */
  --acc-band: var(--ink08);
  --acc-band-hover: var(--ink18);
  --border: var(--rule-faint);
  --bmed: rgba(14, 14, 12, .16);
  --bstr: rgba(14, 14, 12, .32);
  --pill: rgba(14, 14, 12, .06);
  /* Soft fill used by Single Page Tour sequence cards. Slightly
     lighter than --pill (#F2F2F2 vs the ~#ECECEC --pill computes to
     over paper) — gives the sequence stops a hint of warmth without
     dropping all the way back to the page bg. Token lives here so
     it's tunable from one place. */
  --card-fill: #FFFFFF;
  /* Secondary (tier 2) button fill — soft solid, no border. A step
     darker than --card-fill so a button still reads as a button when
     it sits on a sequence card. Placeholder weights, tunable here. */
  --btn2bg: rgba(14, 14, 12, .14);
  --btn2bg-hover: rgba(14, 14, 12, .22);
  --btnbg: var(--ink);
  --btnfg: var(--paper);

  /* ─── Type stacks (locked) ─── */
  /* The wordmark is Molly's PNG marks; this token only styles the
     TEXT FALLBACK behind them, so it's Söhne like everything else
     (Reckless — the old brand serif — retired 16 July 2026). */
  --wordmark: "Söhne", ui-sans-serif, system-ui, sans-serif;
  /* --serif alias now points at the sans face so legacy callsites
     using var(--serif) for editorial moments resolve to Söhne. Only
     the .brand-name (wordmark) rule should reference --wordmark
     explicitly. */
  --serif: var(--sans);
  /* Söhne — body, headlines, all UI text. Weight 400 (Buch) for body,
     500 (Kräftig) for headlines and emphasis. */
  --sans: "Söhne", -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* Söhne Mono — small-caps labels (STILLS, VIDEO, DEVICE), numerals,
     captions, metadata. Tracked +0.14em when uppercase. */
  --mono: "Söhne Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

/* The type stacks again, on the document root.
   Every other token above is a PALETTE token: it varies by look (.warm) and by
   theme (.dark), so it belongs on .app where those classes live. The type
   stacks don't vary — they are locked, one set, everywhere. Keeping them only
   on .app meant that anything appended outside the app shell resolved var(--sans)
   to nothing: `body { font-family: var(--sans) }` became an invalid declaration
   and the browser default (a serif) won. The floating #loey-note pill — the one
   that says "Saved to sequence" — is exactly that case (Mark, 10 Aug 2026:
   "saved to sequence message in black pill was not correct font"). Declared
   here as well as on .app, so .app still wins inside the shell and nothing
   downstream changes. */
:root {
  --wordmark: "Söhne", ui-sans-serif, system-ui, sans-serif;
  --sans: "Söhne", -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: "Söhne Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --serif: var(--sans);
}

/* ---------- "Warm grey" look (light-mode alternative to the "Paper" look).
   Toggled by the .warm class on .app (Appearance → Look). Scoped :not(.dark)
   so dark mode is never affected. Only the surface/control TONES change —
   type, borders and the whole layout are shared with the Paper look.
   - paper  #f6f6f4  → panels, inputs, chrome (a hair warmer than the shell)
   - canvas #edede8  → work-area background, unselected cards, buttons/options
   - cards match the work area (was pure-white --card-fill) so nothing pops
   ---------- */
.app.warm:not(.dark) {
  --canvas: #e2e2dd;
  --paper: #f6f6f4;
  --work-bg: var(--canvas);        /* centre work area = warm grey */
  --card-fill: #f6f6f4;            /* cards a touch lighter than the work area */
  --pill: #edede8;                 /* options / selected / chip fills */
  --btn2bg: #edede8;               /* buttons + options fill */
  --btn2bg-hover: #e4e4dd;         /* darker control hover */
  --acc-band: #e2e2dd;             /* accordion band = Edit/Done tone */
  --acc-band-hover: #d6d6d0;
  --ink08: rgba(62, 62, 8, .10);   /* warm wash — same rationale as Mix */
}
/* Edit / Done toggles read as the warm-grey canvas tone (Mark). */
.app.warm:not(.dark) .tour-edit-btn,
.app.warm:not(.dark) .tour-flow-spine-edit { background: #e2e2dd; border: 0; }
.app.warm:not(.dark) .tour-edit-btn:hover,
.app.warm:not(.dark) .tour-flow-spine-edit:hover { background: #d6d6d0; }

/* ---------- "MIX" look (trial, replaces "Soft" — Mark, 12 July 2026):
   Paper's WHITES with Warm grey's GREYS. Panels, cards and inputs keep the
   Paper look's white (NO --paper/--card-fill overrides — they inherit the
   base tokens); the work area, pills, buttons and accordion bands take the
   warm-grey tones. The .soft class name stays so saved settings carry over.
   ---------- */
.app.soft:not(.dark) {
  --canvas: #e2e2dd;               /* warm-grey work area */
  --work-bg: var(--canvas);
  --pill: #edede8;                 /* options / selected / chip fills (warm) */
  --btn2bg: #edede8;               /* buttons + options fill (warm) */
  --btn2bg-hover: #e4e4dd;
  --acc-band: #e2e2dd;             /* accordion band = Edit/Done tone (warm) */
  --acc-band-hover: #d6d6d0;
  /* The translucent ink wash (hovers, bands, chips — mode-menu items, the
     topbar nav Library/Guide/Settings, the range card's "Full page" strip)
     reads COOL over white; on Mix every grey is warm, so the wash warms
     too. The tint must be STRONG at this alpha to survive compositing:
     rgba(62,62,8,.10) lands on ≈#ECECE6 over white — same red-over-blue
     spread as the pill (#EDEDE8) and canvas (#E2E2DD) family, a step
     darker so hovers still register. (Mark, 18 July 2026: "the hover greys
     … are not the correct grey" — the first pass was imperceptibly warm.) */
  --ink08: rgba(62, 62, 8, .10);
}
.app.soft:not(.dark) .tour-edit-btn,
.app.soft:not(.dark) .tour-flow-spine-edit { background: #e2e2dd; border: 0; }
.app.soft:not(.dark) .tour-edit-btn:hover,
.app.soft:not(.dark) .tour-flow-spine-edit:hover { background: #d6d6d0; }
/* Cool greys that DON'T go through --ink08 (hardcoded rgba/hex washes):
   warmed on Mix + Warm grey so no surface breaks the family. */
.app.soft:not(.dark) .sidebar,
.app.warm:not(.dark) .sidebar,
.app.soft:not(.dark) #tour-multi-pages .tour-group,
.app.warm:not(.dark) #tour-multi-pages .tour-group,
.app.soft:not(.dark) #manrec-markers .tour-group,
.app.warm:not(.dark) #manrec-markers .tour-group { background: rgba(62, 62, 8, .06); }
.app.soft:not(.dark) .lib-chip:hover,
.app.warm:not(.dark) .lib-chip:hover { background: rgba(62, 62, 8, .13); }
.app.soft:not(.dark) .setup-save-menu button:hover,
.app.warm:not(.dark) .setup-save-menu button:hover { background: rgba(62, 62, 8, .08); }
.app.soft:not(.dark) .layouts-slot-ui .layouts-add-dashed:hover,
.app.warm:not(.dark) .layouts-slot-ui .layouts-add-dashed:hover { background: rgba(62, 62, 8, .06); }

/* ---------- Tokens (dark) — NOT part of the locked monochrome
   palette. Dark mode kept as an inverse approximation until Mark
   defines its own locked tokens. ---------- */
.app.dark {
  /* Framer-style surface ladder (Mark: "too dark, and the lighter dark
     isn't light enough"). Three clear steps with real jumps between
     them — base #111 → raised surfaces ~#2A2A28 — mirroring Framer's
     #111 / #1A1A1C / #2A2A2C layering, instead of two near-identical
     darks. Text runs near-white (#F2F2F0 ≈ Framer #F5F5F5); borders are
     translucent hairlines so edges survive on every surface (the old
     solid #2A2A28 border DISAPPEARED against the new paper). */
  --canvas: #111110;
  --paper: #2A2A28;
  --ink: #F2F2F0;
  --ink-soft: #CFCFCC;
  --ink-mute: #9A9A98;
  --rule-faint: rgba(240, 240, 238, .10);

  --bg: var(--canvas);
  --bg2: var(--paper);
  --ink65: rgba(242, 242, 240, .78);
  --ink50: rgba(242, 242, 240, .62);
  --ink32: rgba(242, 242, 240, .40);
  --ink18: rgba(242, 242, 240, .20);
  --ink08: rgba(242, 242, 240, .10);
  --border: var(--rule-faint);
  --bmed: rgba(242, 242, 240, .20);
  --bstr: rgba(242, 242, 240, .38);
  --pill: rgba(242, 242, 240, .12);
  /* Raised card fill — a real step above paper, Framer-control weight. */
  --card-fill: rgba(242, 242, 240, .12);
  /* Secondary (tier 2) button fill, dark theme. */
  --btn2bg: rgba(242, 242, 240, .20);
  --btn2bg-hover: rgba(242, 242, 240, .28);
  --btnbg: var(--ink);
  --btnfg: #161614;
}


/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }

body {
  background: var(--bg);
  /* Söhne Buch (400) at 15px — the locked body spec. Tabular
     numerals apply globally so dimensions and counters align in
     columns regardless of context. */
  font-family: var(--sans);
  font-weight: 400;
  font-size: 15px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; background: none; border: 0; }
input:focus, button:focus, select:focus, [tabindex]:focus { outline: none; }
input:focus-visible, button:focus-visible {
  outline: 1px solid var(--bstr);
  outline-offset: 2px;
}

/* ---------- App shell ---------- */
.app {
  display: grid;
  grid-template-rows: auto 1fr;
  height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
}

/* ---------- Topbar ---------- */
.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  /* Brand + Source on the LEFT (the source is the work's identity —
     it reads like a document title), stage controls on the right.
     Guide/settings/theme live at the sidebar's foot now. */
  padding: 18px 24px 14px;
  background: var(--bg);
  min-height: 36px;
}

/* Topbar left cluster — collapse toggle paired with the brand
   wordmark so the toggle is visually associated with the rail it
   controls, but lives outside the nav itself. */
.topbar-start { display: flex; align-items: center; gap: 14px; }
.brand { display: flex; align-items: baseline; gap: 8px; }
.brand-name {
  /* Wordmark — the PNG marks below; this text rule is only the
     fallback if the images ever fail. Söhne now (Reckless retired
     16 July 2026 — old brand; no revert path). */
  font-family: var(--wordmark);
  font-size: 22px;
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.03em;
  text-transform: lowercase;
}
/* Wordmark trial (9 Jul 2026): PNG marks, exported at 3× from Molly's
   hi-res files (trimmed to the letterforms). 24px display height sits
   optically where the 22px Reckless text did. Ink mark in light mode,
   cream mark in dark — same switch as every other theme swap. */
.brand-name .brand-img { display: block; height: 24px; width: auto; }
.brand-name .brand-img-light { display: none; }
.app.dark .brand-name .brand-img-dark { display: none; }
.app.dark .brand-name .brand-img-light { display: block; }
.brand-by {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink50);
}

/* ---------- Top-left chrome: wordmark + view-mode switch ----------
   Framer-style corner: the editor's Visual/Classic switch sits beside
   the wordmark. Visual is primary (shown on the trigger); Classic is
   tucked into the menu. A hairline blocks the zone off from the canvas
   only while the switch is present — no hue (monochrome locked). */
.brand-chrome { display: flex; align-items: center; gap: 12px; }
.view-switch { position: relative; }
.view-switch[hidden] { display: none; }
/* Quiet by default — no fill or border, dimmed label; the corner reads
   as a label you can click, not a button competing with the wordmark.
   It firms up on hover / while the menu is open. */
.view-switch-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border-radius: 8px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink65);
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.view-switch-trigger:hover {
  background: var(--pill, var(--ink08));
  color: var(--ink);
}
.view-switch-trigger[aria-expanded="true"] {
  background: var(--pill, var(--ink08));
  border-color: var(--bmed);
  color: var(--ink);
}
.view-switch-caret { color: var(--ink32); flex: none; transition: transform .14s ease; }
.view-switch-trigger[aria-expanded="true"] .view-switch-caret { transform: rotate(180deg); }
.view-switch-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 184px;
  padding: 5px;
  background: var(--bg2, var(--paper));
  border: 1px solid var(--bmed);
  border-radius: 11px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, .16), 0 2px 8px rgba(0, 0, 0, .08);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.view-switch-menu[hidden] { display: none; }
/* The relocated switch button (#tour-preview-toggle) reads as a plain
   menu row inside the dropdown — strip its pill/ghost styling. Its text
   already names the mode you'd switch TO; the prefix completes it. */
.view-switch-menu > #tour-preview-toggle {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  white-space: nowrap;
}
.view-switch-menu > #tour-preview-toggle::before {
  content: "Switch to ";
  color: var(--ink50);
  white-space: pre;
}
.view-switch-menu > #tour-preview-toggle:hover { background: var(--pill, var(--ink08)); }

.tabs { display: flex; gap: 28px; justify-self: center; height: 36px; }
.tab {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink50);
  display: inline-flex;
  align-items: center;
  border-bottom: 2px solid transparent;
  transition: color .15s ease, border-color .15s ease;
}
.tab:hover { color: var(--ink); }
.tab.is-active { color: var(--ink); border-bottom-color: var(--ink); }

/* Topbar utility cluster — Guide, Settings, theme toggle. Sits to
   the right of the centered main tabs. */
.topbar-utils {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 36px;
}
.source-bar { justify-content: flex-start; min-width: 0; }

/* Sidebar foot: guide / settings / theme. Pinned low by flex; in the
   collapsed icon rail they stack vertically and stay centred. */
.sidebar { display: flex; flex-direction: column; }
.sidebar-utils {
  margin-top: auto;
  padding-top: 18px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.app.sidebar-collapsed .sidebar-utils {
  flex-direction: column;
  gap: 10px;
}
/* Guide + Settings nav rows (moved up under the library): a hairline in the
   gap above Guide sets them clearly apart from All captures / Saved setups —
   the bare gap alone read as "together" (Mark). They're .side-item, so they
   collapse to icons with the rest of the nav automatically. */
.side-item:not(.side-item-util) + .side-item-util {
  position: relative;
  margin-top: 18px;
}
.side-item:not(.side-item-util) + .side-item-util::before {
  content: '';
  position: absolute;
  top: -9px;
  left: 0;
  right: 14px;
  height: 1px;
  background: var(--ink08);
}
.topbar-util-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 100px;
  color: var(--ink50);
  transition: color .15s ease, background .15s ease;
}
.topbar-util-btn:hover { color: var(--ink); background: var(--pill); }
.topbar-util-btn.is-active { color: var(--ink); background: var(--pill); }
.topbar-util-btn svg { width: 16px; height: 16px; display: block; }
.topbar-util-sep {
  width: 1px;
  height: 18px;
  background: var(--rule-faint);
  margin: 0 2px;
}

.mode-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
}
.mode-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 22px;
  border-radius: 100px;
  color: var(--ink50);
  transition: background .15s ease, color .15s ease;
}
.mode-pill:hover { color: var(--ink); }
/* Selected pill: solid ink fill + paper glyph — the same as the app's
   segmented controls (.pill.is-selected), so the theme toggle reads as a
   standard app control rather than its own thing (Mark). */
.mode-pill.is-active { background: var(--ink); color: var(--paper); }
.mode-pill svg { width: 12px; height: 12px; display: block; }
/* Look toggle (Paper / Warm grey) — text pills, same shell as the mode toggle. */
.look-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
}
.look-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 22px;
  padding: 0 10px;
  border: 0;
  background: transparent;
  border-radius: 100px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: .01em;
  color: var(--ink50);
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s ease, color .15s ease;
}
.look-pill:hover { color: var(--ink); }
.look-pill.is-active { background: var(--ink); color: var(--paper); }

/* ---------- Workspace ----------
   Shared three-column grid: sidebar | main | preview.
   Source bar lives on row 1 spanning columns 2-3 so it aligns with
   the main column start (not floating centred). Row 2 holds the
   three primary columns.
   Container: max-width 1520, centred, 24px lateral padding so the
   layout breathes inside a wider browser window.
   Locked dimensions:
     Expanded:  260 / minmax(680, 780) / 360  · column-gap 48
     Collapsed:  64 / minmax(760, 860) / 360  · column-gap 48 */
.workspace {
  display: grid;
  /* Sidebar 200px (was 260 — unnecessarily wide). Main takes remaining
     width via 1fr with a 0 floor so it can shrink under pressure. Preview
     flexes between 280px (a usable minimum) and 360px (the design target). */
  grid-template-columns: 200px minmax(0, 1fr) minmax(280px, 360px);
  column-gap: 48px;
  /* width: 100% forces the workspace to fill its parent up to
     max-width regardless of how narrow the active panel's content
     is. Without it, `margin: 0 auto` lets the grid contract to fit
     content — so panels with short content (Full page, Area when
     all sections collapsed) made the whole rail+main+preview rig
     shrink and re-centre, looking like the layout was jumping
     between panels. */
  width: 100%;
  max-width: 1520px;
  margin: 0 auto;
  /* Side padding matches the topbar's 24px so the wordmark on the
     left and the day/night toggle on the right line up with the
     sidebar and preview-panel edges below. */
  padding: 12px 24px 24px;
  min-height: 0;
  /* The workspace scrolls as one piece (sidebar + preview sit
     sticky alongside). The previous overflow: hidden created an
     awkward inner scrollbar between main and preview when the
     panel content was taller than the viewport. */
  overflow-y: auto;
  transition: grid-template-columns .25s ease;
}
.workspace > .sidebar-col { grid-column: 1; }
.workspace > .main { grid-column: 2; }
.workspace > .preview-panel { grid-column: 3; }

/* Sidebar column wrapper — toggle lives outside .sidebar (per
   earlier feedback) but is absolutely positioned so it doesn't
   push the nav down. The .sidebar then starts at the same y as
   the .main eyebrow and .preview-panel title — three columns
   level horizontally. The toggle sits in the same vertical space
   the other columns use as their top padding (the 24px breathing
   room above their first content). */
.sidebar-col {
  position: sticky;
  top: 24px;
  align-self: start;
}

/* Collapsed sidebar — the rail narrows to 64px showing just the dot
   icons + the chevron toggle. Main column grows to fill the freed
   space; preview stays at 360px. */
.app.sidebar-collapsed .workspace {
  /* Same flexible main + preview shape as the expanded grid, just
     with a 64px sidebar instead of 260. */
  grid-template-columns: 64px minmax(0, 1fr) minmax(280px, 360px);
}
/* Panels with no right-hand preview (Library, Saved setups, Guide) drop
   the reserved third column so their content spans the full width. */
.workspace.no-preview { grid-template-columns: 200px minmax(0, 1fr); }
.app.sidebar-collapsed .workspace.no-preview { grid-template-columns: 64px minmax(0, 1fr); }

/* ---------- Tour Preview side-by-side mode (P3) ----------
   While the embedded setup preview is open in Page Tour, the sequence
   rail + footer relocate into the right column (replacing the
   storyboard, which the live page makes redundant). The column widens
   so the cards keep a workable line length; the middle column gives
   the freed space to the embedded page. */
.workspace.tour-preview-mode {
  grid-template-columns: 200px minmax(0, 1fr) minmax(264px, 300px);
}
.app.sidebar-collapsed .workspace.tour-preview-mode {
  grid-template-columns: 64px minmax(0, 1fr) minmax(264px, 300px);
}
/* Flyout editor (P5): the step being edited floats beside the rail,
   over the page's right edge — the grid NEVER resizes (the page
   re-scaling on every Edit was unsettling). At 420px the card's
   normal label+control grammar fits, so it needs no compaction.
   Veiled (not hidden — input state survives) while the user is
   picking in the page or rehearsing. */
.tour-flyout {
  position: fixed;
  z-index: 60;
  width: 420px;
  overflow-y: auto;
  background: var(--bg);
  /* Warm hairline matching every other card (Mark: the old --border was a
     different, cooler grey that read as an odd frame around the card). */
  border: 1px solid var(--bmed);
  border-radius: 16px;
  box-shadow: 0 18px 48px rgba(17, 17, 17, 0.16);
  /* The flyout IS the card now — its padding is the card's padding, and the
     inner .tour-section-card blends in (below), so there's ONE surface, not
     a card sitting inside an outer frame (Mark). */
  padding: 18px 20px;
}
.tour-flyout[hidden] { display: none; }
/* Tooltips inside the flyout flip BELOW their control — the flyout is an
   overflow-y:auto box, so the default above-the-control placement clipped
   at its top edge (Mark: "the tooltip on hover is cut off"). */
.tour-flyout .info-tip::after,
.tour-flyout .tour-flow-info::after,
.tour-flyout .capt-tip::after {
  bottom: auto;
  top: calc(100% + 8px);
}
.tour-flyout.is-veiled { visibility: hidden; }
/* The relocated row's .tour-section wrapper carries the base bordered
   box style — inside the flyout that read as a stray square frame
   around the card. Strip it; the flyout IS the frame. */
.tour-flyout .tour-section {
  border: 0;
  padding: 0;
}
/* The expanded card blends INTO the flyout — no own background, border or
   padding — so the editor reads as one card instead of a card inside an
   outer frame (Mark). The flyout carries the box + padding now. */
.tour-flyout .tour-section-card {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  min-width: 0;
}
.tour-flyout .tour-section-card .tour-section-body,
.tour-flyout .tour-section-card .tour-section-interactions {
  border-top: 0;
  padding-top: 0;
}
.tour-flyout .tour-section-card .tour-section-body { margin-top: 16px; }
.tour-flyout .tour-action-sub {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
  margin-left: 24px;
}
/* Nested action sub-cards DO keep their own card box — they're genuinely
   separate objects beneath the main card (only the main card blends in).
   Recessed ink wash + hairline so they read distinct from option rows
   (Mark) — matches the rail treatment. */
.tour-flyout .tour-action-sub .tour-section-card {
  background: var(--card-fill);
  border: 1px solid var(--bmed);
  border-radius: 14px;
  padding: 10px 16px;
}
/* Same two trims as the rail: "Show" is redundant beside the live
   page, and info-tip bubbles clip against the flyout's scroll edge. */
.tour-flyout .tour-section-edit { display: none; }
.tour-flyout .info-tip { display: none; }
/* Flyout head, tidied: the summary line ("Set pause position") was
   crowding the name input down to three characters — the POSITION row
   below already carries that instruction. Name gets the room; chip,
   name, Done, × — nothing else. */
.tour-flyout .tour-section-summary { display: none; }
.tour-flyout .tour-section-head {
  gap: 10px;
  flex-wrap: nowrap;
  align-items: center;
}
.tour-flyout .tour-section-label-input {
  flex: 1 1 140px;
  min-width: 110px;
}
.tour-flyout .tour-section-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Position methods + actions: equal-height rows, consistent gaps. */
.tour-flyout .position-methods { display: flex; flex-wrap: wrap; gap: 8px; }
.tour-flyout .tour-actions-add { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
/* Action rows: four flex items (seg · target · wait · ×) overflow a
   420px card — without wrap the Pick target button got crushed under
   the "hold for" label. Wrap, and take the Click/Hover seg down a
   notch so the row usually fits on one line anyway. */
.tour-flyout .tour-interaction-row {
  flex-wrap: wrap;
  gap: 6px 8px;
}
.tour-flyout .tour-interaction-row .pill-select.seg .pill {
  font-size: 11px;
  padding: 4px 11px;
}
/* Field-row segs in the flyout (Page Clips Mode / Speed) at the flyout's
   compact register — the base 8px/18px pills read huge here (Mark, 24 July). */
.tour-flyout .tour-field-row .pill-select.seg { padding: 2px; gap: 3px; }
.tour-flyout .tour-field-row .pill-select.seg .pill {
  font-size: 11.5px;
  padding: 5px 13px;
}
.tour-flyout .tour-interaction-target-wrap { flex: 1 1 auto; }
.tour-flyout .tour-interaction-target-wrap .ghost-btn { white-space: nowrap; }
/* Field control floor relaxed — 420px minus padding leaves ~330px of
   row; the default 260px control min fits beside the 64px label. */
.tour-flyout .tour-field-control { min-width: 200px; }
/* HOLD steppers: full row under the label so "1s before · 0.7s after"
   stays on one line instead of the trailing word wrapping alone. */
.tour-flyout .tour-field-control.stop-timing {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 8px;
}
.tour-flyout .stop-timing-unit { white-space: nowrap; }
/* The relocated rail scrolls within the sticky column — long tours
   scroll the sequence independently of the page preview. The panel
   keeps its paper ground (--bg); the WHITE --card-fill cards pop on
   it. (The earlier card-fill ground was white-on-white — inverted.) */
.preview-panel.is-tour-sequence {
  padding: 14px;
}
/* "Show all steps"/"Show all clips" open the capture-window
   annotations overlay — redundant beside the live page (cards
   navigate, dots annotate). */
#previewPanel.is-tour-sequence #tour-show-annotations { display: none; }
/* Window-rehearse buttons are the CLASSIC editor's rehearse — the VE
   rail has its own Rehearse beside the frame. */
#previewPanel.is-tour-sequence #tour-rehearse-window { display: none; }
#previewPanel.is-tour-sequence #page-clips-rehearse-window { display: none; }
#previewPanel.is-tour-sequence [data-tour-multi-rehearse-window] { display: none; }
#previewPanel.is-tour-sequence #page-clips-show-annotations { display: none; }
/* #iso-show-all used to open the source-page overlay (hidden in the rail);
   it's now the "Show all areas on the preview" toggle, which belongs IN the
   visual editor — so it's no longer hidden here (its visibility is managed
   in renderIsolatorSections by the area count). */
/* The "scrolls · fast" travel label between cards: noise at rail
   density. Keep only its thin connector spine so the sequence still
   reads as one thread; speed itself is set in the Scroll step.
   The base style draws the spine in two halves with a gap where the
   label sits — with the label hidden, join the halves at the middle
   so the thread is continuous. */
#previewPanel.is-tour-sequence .tour-travel-label { display: none; }
/* Rail: the travel divider is JUST the connector spine (label hidden), so it
   doesn't need 14px of height — it was stacking ~6+14+6≈26px between cards
   (Mark "still quite a gap"). Slim it right down; the spine still threads
   through via the ::before/::after halves. */
#previewPanel.is-tour-sequence .tour-travel { height: 4px; min-height: 4px; }
/* Line halves reach the card edges but don't cross into them. The rail
   gap is 4px, so -6px overshot ~2px under the cards on each side (Mark:
   "the connector lines ever so slightly go into the cards"). -4px lands
   flush on the gap boundary. */
#previewPanel.is-tour-sequence .tour-travel::before { top: -4px; bottom: 50%; }
#previewPanel.is-tour-sequence .tour-travel::after { top: 50%; bottom: -4px; }

/* (Rail collapse toggle removed — it didn't earn its place.) */

/* (Nav rethink block moved to end of file — it must WIN the cascade
   over the original sidebar rules below.) */

/* ---------- Canvas mode (P14, phase 1) ----------
   With the Visual Editor open the app reads as canvas + controls:
   the stage owns the centre, identity prose and the Device form-step
   disappear (device pills + Record/status live in the top bar). */
.app.canvas-mode .panel.is-active > .eyebrow,
.app.canvas-mode .panel.is-active > .title,
.app.canvas-mode .panel.is-active > .subtitle,
.app.canvas-mode .panel.is-active > .step-label,
/* A mode's setup can ride inside a door wrapper ([data-canvas-door]:
   #page-door, #pc-door, the video doors), so the prose layer there is a
   GRANDCHILD of the panel — the direct-child rules above never saw it, which
   is how a mode's headline and an orphan step-label ended up painted above
   the live canvas (16 Aug 2026). Doors are transparent to canvas-mode
   exactly as they are to moveOptionsToSide. Capture's door speaks its own
   prose vocabulary (glyph / pc-h / pc-sub), hidden by the same rule. */
.app.canvas-mode .panel.is-active [data-canvas-door] > .eyebrow,
.app.canvas-mode .panel.is-active [data-canvas-door] > .title,
.app.canvas-mode .panel.is-active [data-canvas-door] > .subtitle,
.app.canvas-mode .panel.is-active [data-canvas-door] > .step-label,
.app.canvas-mode .panel.is-active [data-canvas-door] > .hint,
.app.canvas-mode .panel.is-active [data-canvas-door] > .pc-glyph,
.app.canvas-mode .panel.is-active [data-canvas-door] > .pc-h,
.app.canvas-mode .panel.is-active [data-canvas-door] > .pc-sub,
.app.canvas-mode .panel.is-active [data-canvas-door] > .pc-help,
/* Direct-child hints are part of the prose layer the stage replaces.
   Page Clips carries two ('records at' + 'Up to five clips') BEFORE
   its embed-preview mount, which pushed the stage down vs Page Tour
   (which has none). Hiding only DIRECT children leaves nested option
   hints (option-expand, sticky readouts) untouched. */
.app.canvas-mode .panel.is-active > .hint {
  display: none;
}
/* The Device step: hidden in-panel (the top-bar icon control proxies
   it); the full selector keeps rendering so its logic stays warm. */
.app.canvas-mode .panel.is-active .step-label:has(+ .device-selector) { display: none; }
.app.canvas-mode .panel.is-active .device-selector[data-selector-host] { display: none; }
/* Capture's door renders its devices into .pc-devs (renderDeviceSelector's
   pre-capture branch) — same proxy rule as above now that the mode is a VE
   (16 Aug 2026): the top-bar control owns the device while the canvas is up. */
.app.canvas-mode .panel.is-active .pc-devs[data-selector-host] { display: none; }
/* Top-bar compact device control: icon trio + sizes dropdown. */
.topbar-device-slot { position: relative; display: flex; align-items: center; }
.topbar-device-slot:empty { display: none; }
/* DEVICE ICONS ARE THEIR OWN BUTTONS (Mark, 13 Aug 2026: "move the device icons
   out of a single pill and make them their own single buttons"). The wrapper
   keeps them together as a cluster and nothing else — no border, no padding, no
   pill. Its gap is the bar's own 4px, so the spacing between two device buttons
   is the spacing between the two area tools is the spacing between anything else
   in here. One rhythm across the whole toolbar.

   26px tall and 8px radius: the same box as .stage-tool and the same radius as
   every .ghost-btn, so a square icon button and a text button read as members of
   one set rather than two. */
.tb-device {
  display: flex;
  align-items: center;
  gap: 4px;
}
.tb-dev {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 1px solid var(--bmed);
  border-radius: 8px;
  background: transparent;
  color: var(--ink50);
  cursor: pointer;
}
.tb-dev:hover { color: var(--ink); border-color: var(--ink); }
/* Black only for the one that is on — same rule the area tools follow. */
.tb-dev.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.tb-dev.is-on:hover { background: var(--ink); color: var(--paper); }
/* The sizes caret is a peer button, narrower because it carries no icon of its
   own — it belongs to the cluster it sits beside. */
.tb-dev-more { width: 20px; }
/* CRITICAL: our display:flex would otherwise OVERRIDE the hidden
   attribute (UA display:none) — which kept the menu stuck open no
   matter what the close handlers did. */
.tb-device-menu[hidden] { display: none !important; }
.tb-device-menu {
  position: fixed; /* escapes the stage canvas's overflow clipping */
  z-index: 70;
  min-width: 210px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px;
  box-shadow: 0 14px 36px rgba(17, 17, 17, 0.16);
}
.tb-device-menu .tb-ori { display: flex; gap: 4px; margin-bottom: 6px; }
.tb-size {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  text-align: left;
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink65);
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: 6px 9px;
  cursor: pointer;
}
.tb-size .tb-size-dims {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.02em;
  color: var(--ink50);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.tb-size:hover { background: var(--ink08); color: var(--ink); }
.tb-size:hover .tb-size-dims { color: var(--ink65); }
.tb-size.is-on { background: var(--card-fill); color: var(--ink); font-weight: 500; }
.tb-size-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tb-size-eg { font-size: 10px; color: var(--ink50); line-height: 1.25; }
/* Centre every line (name / example / dims) in the toolbar device dropdown.
   These buttons also carry .dv (the classic card), which left-aligns and
   space-betweens — reads poorly here, so override with higher specificity
   to stack centred and tight (Mark). */
.tb-device-menu .tb-size { align-items: center; justify-content: center; text-align: center; gap: 1px; }
.tb-device-menu .tb-size .tb-size-main { align-items: center; text-align: center; width: 100%; }
/* Action footer, toolbar-compact: button + status inline; the
   "Ready to capture" head and summary stay in the panel layout only. */
.topbar-actions-slot { display: flex; align-items: center; }
.topbar-actions-slot:empty { display: none; }
.topbar-actions-slot .action-footer {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  padding: 0;
  border: 0;
  position: relative; /* anchors the Export quality menu (30 Aug 2026) */
  background: transparent;
}
/* In the top bar only the trigger + status earn space — panels like
   Viewport carry instructions and secondary buttons in their footer,
   which exploded the bar when relocated wholesale. */
.topbar-actions-slot .action-footer > :not(.btn-primary):not(.status) { display: none; }
/* The Export quality menu is opted back in past the hide rule (30 Aug 2026,
   Mark: "export button has no drop down" — a wrapper span around Export fell
   into the rule above and swallowed the button whole; the menu now sits as a
   sibling and carries its own opt-in). Right-aligned under Export. */
.topbar-actions-slot .action-footer > #reel-export-menu { display: block; top: calc(100% + 6px); left: auto; right: 0; }
.topbar-actions-slot .action-footer > #reel-export-menu[hidden] { display: none; }
/* …but Layouts shows a secondary "Save to library" beside Export — opt it back
   in past the single-primary hide rule above (ties on specificity, wins by order). */
.topbar-actions-slot .action-footer > .ghost-btn.layouts-save-lib,
.topbar-actions-slot .action-footer > .ghost-btn.layouts-add-reel,
.topbar-actions-slot .action-footer > .ghost-btn.reel-save-top,
/* 16 Aug round 11: Review's "Capture more", back beside Done (Mark: "Maybe we
   do have 'capture more' but it just brings up the capture window"). */
.topbar-actions-slot .action-footer > .ghost-btn.iso-more-top,
/* 3 Aug: and the shot step's Cancel. It moved into this slot on 1 Aug when the
   white bar came off, and landed straight in the hide rule above — so the step
   showed a lone "Add shot" with no way out but Escape, which nothing told you
   about. Cancel is the one secondary that must never be optional: the step
   covers the whole editor, so without it the board is a room with no door. */
.topbar-actions-slot .action-footer > .ghost-btn.shotstep-cancel {
  display: inline-flex; align-items: center;
  margin: 0; /* .action-footer .ghost-btn adds margin-left:12px ON TOP of the flex gap — uneven spacing (Mark) */
  font-family: var(--sans); font-size: 13px; font-weight: 500;
  padding: 7px 13px; border-radius: 9px; white-space: nowrap;
  color: var(--ink); background: transparent; border: 1px solid var(--bmed); cursor: pointer;
}
.topbar-actions-slot .action-footer > .ghost-btn.layouts-save-lib:hover,
.topbar-actions-slot .action-footer > .ghost-btn.layouts-add-reel:hover,
.topbar-actions-slot .action-footer > .ghost-btn.reel-save-top:hover,
.topbar-actions-slot .action-footer > .ghost-btn.iso-more-top:hover,
.topbar-actions-slot .action-footer > .ghost-btn.shotstep-cancel:hover { border-color: var(--ink); }
.topbar-actions-slot .action-footer > .ghost-btn.layouts-save-lib:disabled,
.topbar-actions-slot .action-footer > .ghost-btn.layouts-add-reel:disabled,
.topbar-actions-slot .action-footer > .ghost-btn.reel-save-top:disabled,
.topbar-actions-slot .action-footer > .ghost-btn.shotstep-cancel:disabled { opacity: .45; cursor: default; }
.topbar-actions-slot .action-footer-head { display: none; }
.topbar { flex-wrap: nowrap; min-width: 0; }
.topbar-utils { min-width: 0; flex: 0 1 auto; }
.topbar-actions-slot { min-width: 0; overflow: hidden; }
.topbar-actions-slot .status {
  margin: 0;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Phase 2 — Flodesk-simple: option sections are a hairline LIST in
   the right column, not boxed widgets. Sentence-case labels, one rule
   between groups, whitespace does the structure. */
#canvas-settings {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 18px;
}
#canvas-settings:first-child { margin-top: 0; }
.canvas-acc {
  border: 0;
  border-radius: 0;
  background: transparent;
}
.canvas-acc > summary {
  list-style: none;
  cursor: pointer;
  padding: 13px 2px;
  font-family: var(--sans);
  font-size: 13px;
  /* Medium weight marks these as interactive section headers, a step up
     from the regular-weight primary text (nav items, values). */
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.canvas-acc > summary:hover { color: var(--ink); opacity: 0.75; }
.canvas-acc > summary::-webkit-details-marker { display: none; }
.canvas-acc > summary::after {
  content: '';
  width: 6px;
  height: 6px;
  border-right: 1.2px solid var(--ink50);
  border-bottom: 1.2px solid var(--ink50);
  transform: rotate(45deg);
  transition: transform .15s ease;
  margin-right: 2px;
}
.canvas-acc[open] > summary::after { transform: rotate(225deg); }
/* Optional info "i" beside a dropdown title (data-acc-tip) — keep it tucked
   against the label so space-between only pushes the chevron to the right. */
.canvas-acc > summary .canvas-acc-sum-label { display: inline-flex; align-items: center; gap: 6px; }
.canvas-acc-body { padding: 0 2px 16px; font-size: 12px; }
.canvas-acc-body .step-label { display: none; }
.canvas-acc-body .hint { font-size: 11px; }

/* Canvas dress code (Flodesk simplicity):
   – sidebar loses its grey slab and reads as a quiet text rail
   – right column loses its border/box; content floats on the page
   – ONE dark compact action button; no yellow shouting
   – stage toolbar buttons quiet until hovered */
/* (Obsolete canvas-mode padding overrides removed — their higher
   specificity was flattening the shell's 32px margins to 0/6px in
   canvas mode, which is the mode users live in. Geometry now comes
   from the shell + generous-margins blocks ONLY.) */
.app.canvas-mode .preview-panel.is-tour-sequence {
  border-left: 1px solid var(--ink08);
  background: var(--bg);
  min-height: 0;
}
.topbar-actions-slot .btn-primary {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  background: var(--ink);
  color: var(--bg);
  width: auto;
  margin: 0;
  padding: 9px 20px;
  white-space: nowrap;
}
.app.canvas-mode #tour-preview-bar .ghost-btn { border-color: transparent; }
.app.canvas-mode #tour-preview-bar .ghost-btn:hover { border-color: var(--bmed); }
/* Caption row UNDER the window (canvas mode), cleaned: message on the
   left in a clear voice, the two view buttons grouped on the right
   ("Open full size · Classic editor"). */
.app.canvas-mode #embed-preview-pane { display: flex; flex-direction: column; }
.app.canvas-mode #tour-preview-canvas { order: 1; }
/* The caption is INSIDE the canvas now — between the chrome bar and the page —
   so it needs no order of its own here. Two corrections got it there (28 Aug):
   under the frame first, which on a portrait 390 × 844 or 834 × 1210 frame put
   it at or past the fold and bought a scrollbar for one sentence; then above
   the whole stage, which captioned the toolbar as well. Mark: "i didnt mean the
   message above the toolbar as well, only the preview. above preview, under
   toolbar." It is the PREVIEW's caption, so it lives in the preview's box. */
.app.canvas-mode #tour-preview-bar {
  order: 2;
  margin: 10px 2px 0;
  display: flex;
  align-items: center;
  gap: 6px;
}
.app.canvas-mode #tour-preview-bar .lab-status {
  flex: 1;
  order: -1; /* message first (left), buttons trail right */
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink65);
  white-space: normal;
  max-width: none;
}
.app.canvas-mode #tour-preview-bar .lab-status:empty { visibility: hidden; }
.app.canvas-mode #tour-preview-bar .ghost-btn { flex: none; }
.app.canvas-mode #tour-preview-hint { order: 3; }

/* Narrow (portrait) frames — final arrangement:
   – device + Rehearse + Capture: a vertical PILL column at the
     frame's right shoulder (beside the page, never over it)
   – Open full size + Classic editor: bottom-RIGHT of the canvas
   – messages: the toast. */
.app.canvas-mode #tour-preview-bar.is-edge { justify-content: flex-end; }
.app.canvas-mode #tour-preview-bar.is-edge .lab-status { display: none; }
#stage-side-tools {
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  background: transparent;
  border: 0;
  padding: 0;
  box-shadow: none;
}
#stage-side-pill {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
#stage-side-tools .tb-device {
  flex-direction: column;
  border: 0;
  padding: 0;
  gap: 6px;
  background: transparent;
}
#stage-side-tools .ghost-btn,
#stage-side-tools #tour-preview-capture {
  font-size: 10.5px;
  padding: 4px 8px;
  white-space: nowrap;
  writing-mode: horizontal-tb;
}
/* The "Embedded · host" status duplicates the stage chrome's URL —
   keep the slot for meaningful messages only, fade the routine one. */
.app.canvas-mode #tour-preview-status { color: var(--ink32); }
.preview-panel.is-tour-sequence .tour-list-footer {
  margin-top: 10px;
  flex-wrap: wrap;
}

/* Compact card grammar for the side rail. Same DOM, same handlers —
   the layout is REDESIGNED for ~330px rather than shrunk: field rows
   stack (label above a full-width control instead of beside it), the
   head wraps with the name truncating, resolved-position rows wrap
   their buttons under the value, and per-card "Show" is hidden — the
   live page beside the rail IS the show. (#previewPanel id keeps these
   heavier than the #tour-sections defaults they override.) */
#previewPanel.is-tour-sequence .tour-sections { gap: 4px; }
/* Two visual ranks on the paper ground — every card OPAQUE so the
   rail's connector spine never shows through:
   1. STOP cards (Start, pauses, End) — white card-fill, bordered.
      Anchors are stops with different names; they dress the same.
   2. ACTION cards — paper-filled, dashed outline: riders. */
#previewPanel.is-tour-sequence .tour-section-card {
  padding: 12px;
  border-radius: 10px;
  font-size: 11.5px;
  background: var(--card-fill);
  border: 1px solid var(--bmed);
  /* Pause card body = click to navigate there; action card body =
     click to fire that hover/click (tourPreviewGoToStop /
     tourPreviewTestAction). Buttons/inputs inside keep their jobs. */
  cursor: pointer;
}
#previewPanel.is-tour-sequence .tour-section-card:hover {
  border-color: var(--ink32);
}
/* Start/End anchors: SAME card as the pauses — they're stops with
   different names, and a third visual treatment just looked odd.
   The full-width opaque card also terminates the connector cleanly. */

/* The + insert, rail-sized: smaller circle, guide line trimmed to the
   rail's tight gaps (the ±14px extents ran under the cards), and the
   line segments stop AT the circle instead of running behind it. */
#previewPanel.is-tour-sequence .tour-stop-insert { min-height: 26px; }
#previewPanel.is-tour-sequence .tour-stop-insert::before {
  top: -7px;
  bottom: -7px;
}
#previewPanel.is-tour-sequence .tour-stop-insert-btn {
  width: 24px;
  height: 24px;
}
#previewPanel.is-tour-sequence .tour-stop-insert-btn::before { width: 9px; }
#previewPanel.is-tour-sequence .tour-stop-insert-btn::after { height: 9px; }
/* Action cards read as DISTINCT objects (Mark: white-on-paper blended in
   with the option rows): solid hairline, no fill (the grey wash felt
   heavy — Mark round 2). Solid, not dashed — dashed is the add-affordance
   grammar (+ Add pause / clip). */
#previewPanel.is-tour-sequence .tour-action-sub .tour-section-card {
  background: var(--bg);
  border: 1px solid var(--bmed);
}
/* Rail action cards, decluttered (Mark: seg + name + Change + grip +
   duplicate + × all fought for one narrow row). Two calm rows: the
   Click|Hover seg with the tools up top, the target name + Change on
   their own line below. Timing is NOT shown in the rail — it lives in
   the expanded flyout card, which has the room for it. */
#previewPanel.is-tour-sequence .tour-action-sub .tour-interaction-row {
  flex-wrap: wrap;
  row-gap: 8px;
}
#previewPanel.is-tour-sequence .tour-action-sub .tour-interaction-row .wait-inline { display: none; }
#previewPanel.is-tour-sequence .tour-action-sub .tour-interaction-target-wrap {
  flex: 1 1 100%;
  order: 5;
  min-width: 0;
}
#previewPanel.is-tour-sequence .tour-section-label,
#previewPanel.is-tour-sequence .tour-section-label-input { font-size: 11.5px; }
#previewPanel.is-tour-sequence .num-inp,
#previewPanel.is-tour-sequence .num-inp-sm,
#previewPanel.is-tour-sequence .num-step-unit,
#previewPanel.is-tour-sequence .wait-inline { font-size: 11px; }
#previewPanel.is-tour-sequence .tour-section-head {
  flex-wrap: wrap;
  row-gap: 6px;
  margin-bottom: 8px;
  padding-bottom: 0;
}
#previewPanel.is-tour-sequence .tour-section-head:last-child { margin-bottom: 0; }
#previewPanel.is-tour-sequence .tour-section-num {
  font-size: 10px;
  margin-right: 4px;
}
/* Name truncates instead of pushing the controls out. */
#previewPanel.is-tour-sequence .tour-section-label,
#previewPanel.is-tour-sequence .tour-section-label-input {
  flex: 1 1 40px;
  min-width: 40px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* "Show" opens the capture window scrolled to the stop — redundant
   with the live page sitting right there. Hidden in the rail only. */
#previewPanel.is-tour-sequence .tour-section-edit { display: none; }
/* Expanded summary line is nice-to-have, not worth a wrap row here. */
#previewPanel.is-tour-sequence .tour-section-summary { display: none; }

/* Field rows: stacked. Mono label sits as its own line over a
   full-width control — the side column never has room for the
   64px-label + 260px-control side-by-side grid. */
#previewPanel.is-tour-sequence .tour-section-card .tour-section-body { margin-top: 10px; }
#previewPanel.is-tour-sequence .tour-section-card .tour-section-interactions { margin-top: 8px; }
#previewPanel.is-tour-sequence .tour-field-row {
  display: block;
  margin-top: 12px;
}
#previewPanel.is-tour-sequence .tour-field-label {
  display: block;
  margin-bottom: 6px;
}
#previewPanel.is-tour-sequence .tour-field-control {
  min-width: 0;
  width: 100%;
}
/* Resolved position: value on its own line, quiet actions wrap under. */
#previewPanel.is-tour-sequence .picked-target-display {
  flex-wrap: wrap;
  gap: 6px 8px;
  padding: 8px 10px;
}
#previewPanel.is-tour-sequence .picked-target-name {
  flex: 1 1 100%;
  white-space: normal;
}
#previewPanel.is-tour-sequence .position-methods {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
/* Buttons one notch down so wrapped rows stay light. */
#previewPanel.is-tour-sequence .ghost-btn {
  font-size: 11px;
  padding: 4px 10px;
}
/* Info tips: their hover bubbles position outside the card and get
   clipped by the column's overflow — and a narrow rail is the wrong
   place for paragraph help anyway. The full help lives in the wide
   layout when the preview is closed. */
#previewPanel.is-tour-sequence .info-tip { display: none; }
/* …but the OPTIONS bands (canvas-settings) keep their info-tips — they're
   the same explanatory tips as the classic editor (e.g. Hidden elements in
   Prepare page). Only the rail CARDS drop theirs (Mark). */
#previewPanel.is-tour-sequence #canvas-settings .info-tip { display: inline-flex; }
/* …and the Slow-for control keeps its "i": the inline help text was removed,
   so this tooltip is now the only place the slow-down beat is explained, and
   it must be reachable in the flyout + rail where info-tips are hidden (Mark). */
.tour-flyout .stop-timing:has([data-tour-slow-secs]) .info-tip,
#previewPanel.is-tour-sequence .stop-timing:has([data-tour-slow-secs]) .info-tip { display: inline-flex; }
/* Collapsed cards carry NO timers in the rail — the row is for
   scanning and click-to-test; changing a hold is an Edit action
   (flyout). Keeps rows to chip · name · Edit · ×. (The inline hold
   stays in the wide middle-column layout when the preview is closed.) */
#previewPanel.is-tour-sequence .stop-timing-inline { display: none; }
#previewPanel.is-tour-sequence .stop-timing-label { display: none; }
/* Hold steppers flow as their own wrapped row under the HOLD label —
   "1s before · 0.7s after" breaks cleanly instead of colliding. */
#previewPanel.is-tour-sequence .tour-field-control.stop-timing {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
}

/* Action cards: shallow indent (no width for a 56px gutter) and
   wrapping rows — pill pair, target, wait control flow naturally. */
#previewPanel.is-tour-sequence .tour-action-sub {
  margin-left: 14px;
  margin-top: 6px;
  gap: 6px;
}
#previewPanel.is-tour-sequence .tour-action-sub .tour-section-card { padding: 12px; }
#previewPanel.is-tour-sequence .tour-interaction-row {
  flex-wrap: wrap;
  gap: 6px 8px;
}
#previewPanel.is-tour-sequence .tour-interaction-target-wrap {
  min-width: 0;
  flex: 1 1 120px;
}
#previewPanel.is-tour-sequence .tour-interaction-row .pill-select.seg .pill {
  font-size: 10.5px;
  padding: 3px 9px;
}
#previewPanel.is-tour-sequence .tour-interaction-target-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* In collapsed mode, hide group labels + item text + pro badges,
   and reveal the per-item icons. Everything centres on the rail's
   axis: equal sidebar side-padding, centred items, square active
   pill — so icons sit dead-centre of the rail and of the pill
   instead of hugging the old label x-position. */
.app.sidebar-collapsed .side-item .side-label,
.app.sidebar-collapsed .side-item .pro-badge {
  display: none;
}
/* Group headings become short centred divider lines when collapsed —
   same box height as the expanded text (so the rail keeps its height
   and the icons hold position), but no text width. The earlier
   visibility:hidden kept the TEXT's width too, which overflowed the
   64px rail and produced a horizontal scrollbar. */
.app.sidebar-collapsed .sidebar-label {
  font-size: 0;
  letter-spacing: 0;
  padding-left: 0;
  height: 19px; /* ≈ 13px mono line box expanded */
  display: flex;
  align-items: center;
  justify-content: center;
}
.app.sidebar-collapsed .sidebar-label::after {
  content: '';
  width: 18px;
  height: 1px;
  background: var(--ink15, rgba(14, 14, 12, 0.15));
}
.app.sidebar-collapsed .side-item .side-icon {
  display: block;
}
.app.sidebar-collapsed .sidebar { padding: 16px 10px; }
.app.sidebar-collapsed .side-item {
  justify-content: center;
  gap: 0;
  padding: 9px 0;
  border-radius: 14px;
}

/* ---------- Sidebar ----------
   Now a tangible boxed surface (matches the preview panel treatment)
   rather than a quietly-bordered column. Groups stack vertically with
   breathing space between, and when the Capture tab is active both
   Stills and Video groups show together. */
.sidebar {
  /* No longer sticky itself — the parent .sidebar-col handles that.
     Sidebar is its own soft box now. */
  background: #0e0e0c0f;
  border: 0;
  border-radius: 20px;
  padding: 22px 12px 22px;
  max-height: calc(100vh - 132px);
  overflow-y: auto;
  transition: opacity .2s ease, transform .25s ease;
}
/* Collapse toggle lives in the topbar's left cluster (next to the
   brand). Visually associated with the sidebar it controls, but
   well clear of the STILLS label below. */
.sidebar-toggle {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 100px;
  color: var(--ink-mute);
  transition: color .15s ease, background .15s ease, transform .25s ease;
  flex: none;
}
.sidebar-toggle:hover { color: var(--ink); background: var(--pill); }
.sidebar-toggle svg { width: 14px; height: 14px; display: block; }
/* Flip the chevron when the rail is collapsed so it points outward
   (rightwards) to suggest re-expanding. The toggle keeps its left
   margin so it stays anchored in line with the icons below — the
   whole rail reads as a single left-aligned column. */
.sidebar-group {
  display: none;
}
.sidebar-group.is-active {
  display: block;
}
/* Only add the gap when two groups are visible together (Capture tab
   shows both Stills and Video). For single-group tabs the selector
   doesn't match, so there's no orphan margin. */
.sidebar-group.is-active + .sidebar-group.is-active {
  margin-top: 22px;
}

.sidebar-label {
  /* Section heading — Söhne Mono small caps, +0.14em per the locked
     type spec. Bumped from 10px to 13px and from ink50 to ink so the
     groups (STILLS / VIDEO / LIBRARY) read as proper headings, not
     fine-print labels. */
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink);
  margin-bottom: 14px;
  /* Match the side-item text inset so labels and items align flush. */
  padding-left: 10px;
}

.side-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  /* Text aligns with the section labels (no item indent). The 10px left
     padding is the active pill's breathing room, not an indent — the
     section label gets the same 10px so the column reads flush. */
  padding: 8px 10px;
  font-size: 14px;
  font-weight: 400;
  color: var(--ink50);
  text-align: left;
  white-space: nowrap;
  border-radius: 20px;
  transition: color .15s ease, background .15s ease;
}
.side-item:hover { color: var(--ink); }
/* "← Capture modes" back item at the top of Guide / Settings — gives an
   obvious route back to the capture modes (was only the topbar icon). */
.side-back { color: var(--ink50); margin-bottom: 18px; }
.side-back .side-label { letter-spacing: 0.01em; }
.side-item.is-active {
  color: var(--ink);
  font-weight: 400;
  background: #0e0e0c24;
}
/* Side icons — rendered inline next to the label in collapsed mode,
   hidden when the rail is expanded (label-only view). */
.side-item .side-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--ink-mute);
  transition: color .15s ease;
  /* Expanded mode hides the icon — the textual label is the
     identifier. Collapsed mode (below) reveals it. */
  display: none;
}
.side-item:hover .side-icon { color: var(--ink); }
.side-item.is-active .side-icon { color: var(--ink); }

/* ---------- Main ----------
   Main no longer manages its own scroll — the workspace scrolls
   as one piece, with sidebar and preview sticky alongside. This
   gives a single page-edge scrollbar rather than an inner one
   that read like a vertical divider between main and preview.
   No top padding so the eyebrow and title start at the same row
   top as the sidebar's header label and preview's title — three
   columns aligning horizontally. */
.main {
  min-width: 0;
  /* Top padding matches .sidebar and .preview-panel (all 24px) so
     the panel eyebrow sits on the same y baseline as the sidebar's
     STILLS label and the preview's title. */
  padding: 24px 0 80px;
}
.main-inner {
  min-width: 0;
}
/* The panel's eyebrow + title together take the visual weight that
   the sidebar/preview use a single mono caps label for. Eyebrow
   margin-top: 0 so it sits at the exact same y as the sidebar's
   STILLS label and the preview's title — no 2px drift. */
.main .panel .eyebrow:first-child {
  margin-top: 0;
}

/* ---------- Preview panel (workspace column 3) ----------
   Sticky relative to the viewport so it stays visible while the main
   column scrolls. Sits in its own grid column, no longer nested
   inside .main. */
.preview-panel {
  position: sticky;
  top: 24px;
  align-self: start;
  border: 1px solid var(--border);
  border-radius: 10px;
  /* Top padding matches .main's 12px so the panel's title sits level with the
     mode title / nav toggle in the other columns (was 24px → it sat 12px low). */
  padding: 12px 24px 24px;
  background: var(--bg);
  min-height: 360px;
  max-height: calc(100vh - 160px);
  overflow-y: auto;
}
.preview-empty {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.preview-empty-label {
  /* Sans heading like .rail-mode-title (was a mono-caps eyebrow) — Mark. */
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.preview-empty-body {
  font-size: 13px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink65);
}

/* Preview content blocks (Directed Tour sequence summary etc) */
.preview-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.preview-title {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: -0.005em;
  color: var(--ink);
}
.preview-duration {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink65);
}
/* Right side of the preview header — duration chip + the Open capture
   window button share the row's dead space. */
.preview-header-right {
  display: flex;
  align-items: center;
  gap: 12px;
}
.preview-header .preview-open-btn {
  white-space: nowrap;
}
.preview-section-group {
  margin-bottom: 22px;
}
.preview-section-group:last-child {
  margin-bottom: 0;
}
/* Motion-detected advisory — same layout as a regular preview group,
   but with a faint left rule to flag it as a warning without using
   chromatic accents (palette is locked monochrome). */
.preview-section-group.preview-warning {
  border-left: 2px solid var(--ink);
  padding-left: 12px;
  margin-top: 18px;
}
.preview-group-label {
  /* Sans heading, matching .rail-mode-title's treatment (was a mono-caps
     eyebrow) — consistent with the mode title (Mark). */
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-bottom: 6px;
}
.preview-group-body {
  font-size: 13px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink);
}
.preview-group-sub {
  font-size: 12px;
  font-weight: 400;
  line-height: 1.55;
  color: var(--ink65);
  margin-top: 2px;
}

/* Keyboard shortcuts list (Manual Recording preview) */
.preview-shortcuts {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}
.preview-shortcut-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font-size: 12px;
  color: var(--ink);
}
.preview-shortcut-row > span:last-child {
  color: var(--ink65);
  text-align: right;
}

/* Sequence list */
.preview-section {
  margin-top: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--ink08);
}
.preview-section:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}
.preview-section-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 4px;
}
.preview-section-num {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink65);
  letter-spacing: 0.14em;
  flex: none;
}
.preview-section-label {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 400;
  color: var(--ink);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.preview-section-summary {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink65);
  flex: none;
}
.preview-int-row {
  display: flex;
  gap: 8px;
  padding-left: 22px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink);
  line-height: 1.8;
}
.preview-int-num {
  color: var(--ink50);
  flex: none;
  width: 14px;
}
.preview-int-type {
  flex: none;
  width: 38px;
}
.preview-int-target {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- Element capture preview (right panel) ---------- */
.iso-preview-list {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.iso-preview-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.iso-preview-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
/* Real <img> tag — renders at its natural aspect ratio so the card
   matches the shape of the captured element. No fixed box, no grey
   gutters at the sides for wide or tall elements. */
.iso-thumb-img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  background: var(--ink08);
}
/* Latest-capture thumbnail is clickable (opens the saved file) + draggable out. */
.latest-thumb {
  cursor: pointer;
}
.latest-thumb:active {
  cursor: grabbing;
}
.iso-thumb-placeholder {
  width: 100%;
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Quiet loading state across every mode — a plain outline, no grey fill. */
  background: transparent;
  border: 1px solid var(--border);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink50);
}
.iso-thumb-failed { color: var(--ink50); }

/* Page Capture — Selected range summary. Mono eyebrow + plain value,
   e.g. "Selected range — Page top → Marker · 3,022px tall". */
.page-range-summary {
  display: flex;
  align-items: baseline;
  gap: 10px;
  /* Sits UNDER the Start/End selectors now — space above, not below. */
  margin-top: 16px;
}
.page-range-summary-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink50);
}
.page-range-summary-value {
  font-size: 13px;
  color: var(--ink);
}
/* ==== HOW TO CAPTURE BEGIN ==================================================
   Mark, 10 Aug 2026. Rendering and staging collected below Styling and Hide
   Elements, in a bordered card rather than a grey accordion band: those two are
   long lists you open when you need them, these are two lines you should be
   able to READ without opening anything. Same border, radius and inner rhythm
   as .range-card, so the rail has one card language, not two.
   -------------------------------------------------------------------------- */
.how-card {
  border: 1px solid var(--bmed);
  border-radius: 10px;
  padding: 2px 13px;
  margin-top: 14px;
}
.how-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 13px 0;
  border-top: 1px solid var(--ink08);
}
.how-row:first-child { border-top: 0; }
.how-k {
  font-family: var(--sans);
  font-size: 13px;
  color: var(--ink);
  display: block;
  min-width: 0;
}
/* The one-line explanation under the switch. Block, not a sibling row, so it
   stays tied to its control when the rail narrows. */
.how-sub {
  display: block;
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink50);
}
.how-v { margin-left: auto; display: flex; align-items: center; gap: 10px; flex: none; }
.how-val { font-size: 13px; color: var(--ink); }
/* A link, not a button: it reveals the choice rather than performing an action,
   and a second filled control in this card would out-shout the switch. */
.how-chg {
  background: transparent; border: 0; padding: 0; cursor: pointer;
  font-family: var(--sans); font-size: 12px; color: var(--ink50);
  text-decoration: underline; text-underline-offset: 2px;
}
.how-chg:hover { color: var(--ink); }
/* The opens-in-a-new-window mark, inside a primary button. Sized and spaced
   like .open-window-btn svg, which is the control it is borrowing from. */
.btn-primary .btn-ext-ico {
  width: 13px; height: 13px; flex: none;
  margin-left: 7px; vertical-align: -1px;
}
.btn-primary:has(.btn-ext-ico) { display: inline-flex; align-items: center; justify-content: center; }

.how-opts { padding: 0 0 13px; }
.how-lede { margin: 0 0 4px; font-size: 12px; line-height: 1.5; color: var(--ink50); }
/* THE RENDERING CHOICE IS THREE OUTCOMES, NOT THREE PILLS.
   Still a .pill-select[data-bind] underneath — the bound-pill handler does all
   the writing, so this is presentation only — but a pill can hold a word and
   these need a sentence: what each mode is FOR is the part that decides it, and
   it used to live in a tooltip. Every .pill property is reset here rather than
   scoped away, because the base pill is a rounded chip and this is a list row. */
.pr-list { display: block; }
.pr-list .pill.pr-opt {
  display: flex; align-items: flex-start; gap: 11px;
  width: 100%; text-align: left;
  padding: 11px 0; border: 0; border-radius: 0;
  border-top: 1px solid var(--ink08);
  background: transparent; color: var(--ink);
  font-size: 13px; letter-spacing: 0;
}
.pr-list .pill.pr-opt:first-child { border-top: 0; padding-top: 4px; }
.pr-list .pill.pr-opt:hover { background: transparent; border-color: var(--ink08); color: var(--ink); }
/* Selection is the DOT, not a filled row: a black band here would out-shout the
   staging switch below it and read as a button rather than a chosen option. */
.pr-list .pill.pr-opt.is-selected { background: transparent; color: var(--ink); }
.pr-dot {
  position: relative; width: 15px; height: 15px; margin-top: 2px; flex: none;
  border: 1.4px solid var(--bmed); border-radius: 50%;
}
.pr-list .pill.is-selected .pr-dot { border-color: var(--ink); }
.pr-list .pill.is-selected .pr-dot::after {
  content: ''; position: absolute; inset: 3px; border-radius: 50%; background: var(--ink);
}
.pr-txt { min-width: 0; }
.pr-n { display: block; font-size: 13px; color: var(--ink); }
.pr-d { display: block; margin-top: 2px; font-size: 12px; line-height: 1.5; color: var(--ink50); white-space: normal; }
/* An option the current setup cannot honour — Scroll while staging. Dimmed and
   left in place rather than removed: a control that vanishes reads as a bug and
   teaches nothing, and this one's own description says why it is out. */
.pr-list .pill.pr-opt:disabled { opacity: .45; cursor: not-allowed; }
.pr-list .pill.pr-opt:disabled:hover { color: var(--ink); }
.how-note { margin: 9px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink50); }
.how-note[hidden] { display: none; }
/* Switch. The app has no switch component yet — this is the first, so it is
   built from the same tokens as everything else and kept deliberately plain. */
.how-sw {
  position: relative; width: 34px; height: 20px; flex: none; padding: 0;
  border: 0; border-radius: 100px; background: var(--ink18); cursor: pointer;
  transition: background .15s ease;
}
.how-sw::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--paper);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .2);
  transition: left .15s ease;
}
.how-sw.is-on { background: var(--ink); }
.how-sw.is-on::after { left: 16px; }
.how-sw:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
/* ==== HOW TO CAPTURE END ==================================================== */

/* ==== CROP OVERLAY BEGIN ====================================================
   Mark, 10 Aug 2026: "when clicking crop it comes up with the crop options as
   well as a little 'view crop' button. this is hard to see. then in crop mode
   how do you get back to live page mode".

   The 12px ghost that used to live on the summary row is gone; the way in is
   "See the crop", a full-width control directly under the ratio chips, where
   the eye already is after choosing one. Deleting from this fence to CROP
   OVERLAY END, plus CROP_OVERLAY in app.js, is the whole revert.
   -------------------------------------------------------------------------- */
.see-render-btn {
  width: 100%;
  margin-top: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
}

/* The crop is a LAYER on the canvas, not a place you go — the live page stays
   put underneath, dimmed, which is what says "this is laid over your page" and
   makes the way back obvious. */
/* ── "NOT IN HERE" (28 Aug 2026) ───────────────────────────────────────────
   The app's answer to an ambient key pressed in the app. Same card as the
   ⌥Space receipt on a real page — paper, wordmark, keycap — because it is the
   same voice explaining where that card would have appeared. Bottom right,
   click or wait to dismiss, never blocks anything. */
.capt-key-note {
  position: fixed; right: 20px; bottom: 20px; width: 296px; z-index: 900;
  display: flex; flex-direction: column; gap: 9px;
  padding: 13px 14px 14px;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--bmed); border-radius: 16px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, .04), 0 16px 40px rgba(14, 14, 12, .14);
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
  cursor: pointer;
}
.capt-key-note.is-on { opacity: 1; transform: none; pointer-events: auto; }
.capt-key-note .ckn-brand { font-weight: 600; font-size: 13px; letter-spacing: -.01em; }
.capt-key-note .ckn-body { display: flex; align-items: flex-start; gap: 11px; }
.capt-key-note kbd {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em; white-space: nowrap; flex: none;
  border: 1px solid var(--bmed); border-radius: 8px; padding: 7px 10px;
  color: var(--ink); background: var(--card, #fff);
}
.capt-key-note .ckn-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.capt-key-note .ckn-tx b { font-size: 12.5px; font-weight: 500; }
.capt-key-note .ckn-tx span { font-size: 11.5px; line-height: 1.45; color: var(--ink50); }
/* ── THE FRAME'S CAPTION, IN THE CHROME BAR (28 Aug 2026, "move 1") ───────
   It rides in the bar's ADDRESS slot — see app.html. Quiet by design: a
   statement about what the buttons beside it will do, not a control. The lead
   phrase is sans so it reads as words; the measurement is mono because it is a
   measurement. One fact only: the page name it used to carry was always the
   page the source bar above is already naming (browsing the frame adopts the
   page as the source in both of these modes), so it was saying it twice. */
.lab-handoff {
  display: none; align-items: baseline; gap: 6px; min-width: 0;
  margin-left: 10px; font-family: var(--sans); font-size: 11px; line-height: 1.3;
}
.lab-handoff[hidden] { display: none; }
.app.shell-modetop .lab-chrome .lab-handoff:not([hidden]) { display: inline-flex; }
.lab-handoff-k { color: var(--ink65); flex: none; }
/* THE SCROLL GETS A SLOT, NOT A WIDTH OF ITS OWN (28 Aug 2026). The bar is
   centred and hugs its content, so a readout that gains a character at 1,000px
   moved the entire toolbar on every wheel tick. Reserved to the width of its
   own longest form, with tabular figures so the digits do not shuffle inside
   it either — the text changes, the box never does. */
.lab-handoff-y {
  /* 17ch, not 13: the slot has to fit the LONGEST form it will ever hold, and
     "· 12,345px down" is fifteen characters before the .04em tracking is
     counted. At 13 it was stable from 0–999 and jumped once at a thousand,
     which is the one place a scroll definitely crosses. */
  flex: none; min-width: 17ch; white-space: nowrap;
  font-family: var(--mono); font-size: 10px; letter-spacing: .04em; color: var(--ink32);
  font-variant-numeric: tabular-nums;
}
.lab-canvas.has-crop-overlay { position: relative; }
.iso-styled-stage.is-crop-overlay {
  position: absolute; inset: 0; z-index: 40;
  display: flex; align-items: center; justify-content: center;
  padding: 18px; overflow: auto;
}
/* Darker (Mark, 11 Aug 2026). At .34 the live page competed with the render it
   was standing behind; the point of the overlay is to be the only thing you are
   judging. */
.iso-styled-stage.is-crop-overlay .crop-ov-scrim {
  position: absolute; inset: 0; background: rgba(14, 14, 12, .62);
  backdrop-filter: blur(2px); cursor: pointer;
}
/* Square (Mark). A rounded card puts a curve around a picture whose own corners
   are a setting you control in Styling — two radii arguing about the same edge. */
.iso-styled-stage.is-crop-overlay .iso-styled-single {
  position: relative; z-index: 1; margin: auto;
  background: var(--paper); border-radius: 0; padding: 12px 14px 14px;
  box-shadow: 0 2px 4px rgba(14, 14, 12, .10), 0 22px 54px rgba(14, 14, 12, .28);
  /* THE CARD HUGS THE CROP (Mark, 12 Aug 2026: "can the crop preview overlay
     'fit' the crop instead of having lots of white space around").
     It didn't, because `.iso-styled-single` is `width: 100%` — right for the
     full-stage gallery it was written for, wrong for a card floating on a
     scrim. Measured at a 900px canvas: the card came out 864 wide around a
     354-wide picture, so a tall crop sat in the middle of about 250px of white
     paper on each side and read as a rendering fault rather than a portrait.
     `width: auto` on a centred flex item is shrink-to-fit, which is what a card
     around one picture should have been from the start.
     min-content on the floor keeps the header honest — the ratio chips and Done
     must not be squeezed by a narrow crop, so a very tall picture gets a card
     as wide as its own chrome rather than a clipped Done. */
  width: auto;
  min-width: min-content;
  max-width: 100%;
}
/* THE RATIOS, IN THE HEADER — black and clear, no grey strip behind them (Mark,
   11 Aug 2026). The same two states as every pick in the rail: outline when it
   is not chosen, filled when it is. */
.crop-ov-ratios { display: inline-flex; gap: 5px; }
/* ══ AREA STYLING OVERLAY BEGIN ═══════════════════════════════════════════
   One | All, in the Area overlay's header (Mark, 11 Aug 2026: "not sure.
   toggle for both?"). It borrows .crop-ov-ratio wholesale — outline when it is
   not chosen, filled when it is — because it is the same kind of choice in the
   same header, and inventing a second segmented control for the second overlay
   is how two overlays start looking like two products. */
.iso-ov-views { display: inline-flex; gap: 5px; }
/* Icon-only, so the pair reads as two SHAPES rather than two amounts — which is
   what "One | All" got wrong. Square, not the pill the ratio chips are, because
   these hold a glyph rather than a word. */
.crop-ov-ratio.is-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; padding: 0; height: 28px;
}
.crop-ov-ratio.is-icon svg { display: block; }
/* Step through the areas, under the single view. Quiet — the picture is the
   thing on this screen, and these only say where you are in the set. */
.iso-ov-step {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  margin-top: 2px;
}
.iso-ov-arrow {
  width: 26px; height: 26px; padding: 0; border: 0; background: transparent;
  border-radius: 6px; color: var(--ink50); font-size: 17px; line-height: 1;
  cursor: pointer; transition: color .12s ease, background-color .12s ease;
}
.iso-ov-arrow:hover { color: var(--ink); background: var(--ink08); }
.iso-ov-count {
  font-family: var(--mono); font-size: 10.5px; color: var(--ink50);
  min-width: 44px; text-align: center;
}
/* ALL: the gallery inside the overlay card. It sizes its own columns from the
   count (set inline by the renderer), so it does not inherit the full-stage
   grid's auto-fill — over a page, the card must hug what it holds rather than
   stretch to a minimum column width. */
.iso-styled-stage.is-crop-overlay .iso-styled-single.is-all { max-width: min(96%, 1100px); }
.iso-styled-single.is-all .iso-styled-grid {
  max-height: min(66vh, 640px);
  gap: 18px 16px;
  width: 100%;
  /* Centre each card in its cell. The areas are DIFFERENT shapes — that is the
     point of seeing them together — so top-aligning them scattered the captions
     down the row at three different heights. Centred, the row reads as a row
     and the shapes still differ, which is the only thing that should. */
  align-items: center;
}
/* In the gallery a card is one of several, so it gets its caption back and the
   pointer that says it can be clicked to become the single view. */
.iso-styled-single.is-all .iso-styled-card { cursor: pointer; }
.iso-styled-single.is-all .iso-styled-frame { max-width: 100%; }
/* Preview styling sits beside Reset styling — one row, both outline, the view
   action first because it is the one you reach for while styling. */
.iso-styling-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 2px; }
.iso-styling-acts .finish-reset { margin: 0; }
.iso-styling-acts .see-render-btn { margin: 0; }
/* ══ AREA STYLING OVERLAY END ═════════════════════════════════════════════ */
.crop-ov-ratio {
  font-family: var(--sans); font-size: 11.5px; line-height: 1; color: var(--ink65);
  background: transparent; border: 1px solid var(--rule-faint); border-radius: 8px;
  padding: 7px 11px; cursor: pointer;
}
.crop-ov-ratio:hover { color: var(--ink); border-color: var(--ink20); background: rgba(14, 14, 12, .04); }
.crop-ov-ratio.is-selected,
.crop-ov-ratio.is-selected:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.crop-ov-head { display: flex; align-items: center; gap: 9px; margin: 0 0 11px; }
.crop-ov-title { font-size: 13px; color: var(--ink); }
.crop-ov-size { font-size: 12px; color: var(--ink50); }
.crop-ov-spacer { flex: 1; }
/* Named, not an X. The complaint was that there was no way back — a control
   that says where it takes you answers that; a bare glyph restates it. */
/* DONE is a word, not a chip — it must not dress like the ratio control it sits
   beside. Border dropped, weight raised, so the eye separates a pick from an
   exit by shape as well as position. */
/* DONE is a BUTTON (Mark, 11 Aug 2026: "make done a button. remove tick and
   line"). Filled rather than outlined, which is what keeps it apart from the
   ratio pills now that they wear the outline: one is a pick, one is the way
   out, and the fill says which without a rule between them. */
/* Done, leaving the crop overlay. Filled ink at 12px/500 on an 8px corner —
   the same commitment shape as the library's Done and the preview banner's
   (12 Aug 2026 button pass). 11.5px here was the odd size out. */
.crop-ov-close {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--sans); font-size: 12px; font-weight: 500; line-height: 1;
  color: var(--paper); background: var(--ink); border: 1px solid var(--ink);
  border-radius: 8px; padding: 7px 15px; cursor: pointer; margin-left: 4px;
}
.crop-ov-close:hover { opacity: .84; }
/* ==== CROP OVERLAY END ==================================================== */

/* Page Capture span — Start / End endpoint rows. Each row is a tag
   ("Start"/"End") beside its result-plus-change body (page-edge default
   or a placed-marker line). */
.page-position { display: flex; flex-direction: column; gap: 10px; }
.page-position-row { display: flex; align-items: center; gap: 12px; }
.page-position-tag {
  flex: none;
  width: 48px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em; /* match the options-dropdown mono (Mark: consistency) */
  text-transform: uppercase;
  color: var(--ink50);
}
.page-position-body { flex: 1; min-width: 0; }
.page-position-body .position-methods,
.page-position-body .position-resolved { margin: 0; }

/* Preview thumbnails are clickable — they open the capture window at that
   stop/clip/page. The button is a transparent wrapper so the inner thumb
   keeps its own styling; only a pointer cursor + subtle hover lift. */
.preview-thumb-btn {
  display: block;
  width: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  text-align: inherit;
  cursor: pointer;
  border-radius: 8px;
  transition: opacity .12s ease, transform .12s ease;
}
.preview-thumb-btn:hover { opacity: 0.88; }
.preview-thumb-btn:active { transform: scale(0.992); }
.preview-thumb-btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* Site Flow preview filmstrip — pages as thumbnails connected by named
   transition links, top to bottom. Mirrors the editor spine but visual. */
.preview-filmstrip { display: flex; flex-direction: column; }
.preview-film-page { display: flex; flex-direction: column; gap: 8px; }
.preview-film-thumb {
  border-radius: 8px;
  overflow: hidden;
}
.preview-film-thumb .iso-thumb-img {
  border: 0;
  border-radius: 8px;
}
/* Filmstrip placeholder just needs the rounded corner — the quiet outline
   now comes from the base .iso-thumb-placeholder rule. */
.preview-film-thumb .iso-thumb-placeholder { border-radius: 8px; }
.preview-film-label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.preview-film-num {
  flex: none;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink50);
}
.preview-film-host {
  min-width: 0;
  font-family: var(--sans);
  font-size: 13px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Transition connector between two pages — a down arrow and the link name. */
.preview-film-transition {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 0;
}
.preview-film-arrow { font-size: 14px; color: var(--ink50); line-height: 1; }
.preview-film-translabel {
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink-soft, #5F5E59);
}
.preview-film-transition.is-empty .preview-film-translabel {
  color: var(--ink50);
  font-style: italic;
}

/* Responsive breakpoints for the workspace grid.
   The auto-collapse used to live as @media-scoped CSS that
   overrode the .sidebar-collapsed class — which meant the user's
   chevron toggle did nothing at narrow widths (class flipped but
   the breakpoint pinned the rail collapsed). Now the user toggle
   always wins. The breakpoint only collapses the preview entirely
   at very narrow widths, which is structural rather than a
   preference. */
@media (max-width: 1100px) {
  .workspace:not(.tour-preview-mode) {
    grid-template-columns: 200px 1fr;
  }
  .app.sidebar-collapsed .workspace:not(.tour-preview-mode) {
    grid-template-columns: 64px 1fr;
  }
  /* CLASSIC layout only: its storyboard panel is optional decoration.
     In canvas mode the preview-panel IS the rail (cards + options) —
     hiding it blanked the editor's right side below 1100px (Mark). */
  .workspace:not(.tour-preview-mode) > .preview-panel { display: none; }
}

.panel { display: none; }
/* Layouts is a capture-mode tool — its sidebar link has no business in
   the Library / Guide / Settings rail (Mark). */
.sidebar-group.is-suppressed { display: none !important; }
.panel.is-active { display: block; }

/* ══ MONO IS FOR META (#297, 17 Aug 2026) ══════════════════════════════════
   Mark: "regarding mono, that should only be used for meta so probably need to
   change them all." The rule is his own, twice before: 23 July ("mono should
   not be used for titles") and #295 yesterday, both of which fixed the places
   that had been noticed rather than the class they inherit from. This is that
   class, and 23 others with it.

   .eyebrow was the mono-caps label the whole app reached for whenever something
   needed naming — sections, panels, groups, cards. That is the definition of a
   title, and it was set as meta: 11px mono, uppercase, 0.14em tracked. Every
   surface that used it inherited the shout.

   What mono keeps is what the July note reserved it for and nothing else:
   counts, timestamps, nums, tags, chips, hex values, status pills — the small
   machine-ish facts where a fixed width and a technical voice are the point. 67
   rules still qualify; 24 did not.

   Sizes went UP a notch where they were under 11px. Uppercase mono at 10px
   occupies more room and reads louder than it measures, so a straight swap
   would have quietly shrunk every label in the app. */
.eyebrow {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: -0.005em;
  color: var(--ink50);
  margin: 0 0 14px;
}

.title {
  /* Locked spec: Söhne Kräftig 500 at ~40px for panel headlines.
     (Söhne everywhere — Reckless retired 16 July 2026.) */
  font-family: var(--sans);
  font-size: 40px;
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin: 0 0 14px;
}
.title em { font-style: normal; color: var(--ink-soft); font-weight: 500; }
/* Content views (Settings, Library, Saved setups) match the Guide hero
   heading (.g2-title — 30px/500) so Preferences / Guide / Library / Saved
   setups all read with one consistent heading rather than the big 40px
   work-canvas headline. Capture modes keep the large headline. (Mark) */
.panel[data-panel="settings-all"] .title {
  font-size: 30px; font-weight: 500; line-height: 1.12; letter-spacing: -0.015em;
}
/* Settings subtitle matches the Guide lede — larger, softer ink. */
.panel[data-panel="settings-all"] .subtitle {
  font-size: 15.5px; color: var(--ink50); line-height: 1.55; margin-bottom: 34px;
}

.subtitle {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.7;
  color: var(--ink65);
  margin: 0 0 44px;
  max-width: 500px;
}

/* ---------- Section label (within panels) ---------- */
.section-label {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: -0.005em;
  color: var(--ink65);
  margin: 32px 0 12px;
}

/* ---------- Device grid ---------- */
.device-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
  margin-bottom: 22px;
}
.device-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 13px 16px;
  border: 1px solid var(--bmed);
  background: transparent;
  text-align: left;
  transition: border-color .15s ease, background .15s ease;
}
.device-card:hover { border-color: var(--bstr); }
.device-card.is-selected {
  border-color: var(--ink);
  background: var(--pill);
}
.device-card .name {
  font-size: 12px;
  font-weight: 400;
  color: var(--ink);
}
.device-card .dims {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  color: var(--ink50);
  letter-spacing: 0.04em;
}

/* ---------- Orientation pills ---------- */
.orientation-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
}
.orientation-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 15px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 400;
  color: var(--ink65);
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.orientation-pill:hover { color: var(--ink); border-color: var(--bstr); }
.orientation-pill.is-selected {
  border-color: var(--ink);
  background: var(--pill);
  color: var(--ink);
}

/* ---------- Confirm bar ---------- */
.confirm-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border: 1px solid var(--bmed);
  margin-bottom: 10px;
}
.confirm-bar .dims {
  font-family: var(--serif);
  font-size: 30px;
  font-weight: 400;
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.005em;
}
.confirm-bar .device {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink50);
  margin-top: 4px;
}

/* ---------- Button system (three-tier) ----------
   Tier 1 primary   → .btn-primary (solid amber, defined far below).
                      One per screen: the capture / record / confirm.
   Tier 2 secondary → .ghost-btn (soft solid fill, no border). The
                      workhorses: Pick, Place marker, Add stop, etc.
   Tier 3 tertiary  → .ghost-btn.is-quiet and .ghost-btn.tour-section-edit
                      (text only, no fill, no border): Done, Edit, Show,
                      Show selections, Clear all.
   Borders removed throughout so the panels read as calm fields of
   space punctuated by the occasional solid shape. */
/* [hidden] IS NOT ENOUGH ON THIS CLASS (15 Aug 2026). The attribute's UA rule
   is `display: none` at the lowest possible specificity, so the `display:
   inline-flex` below silently beats it and a .ghost-btn marked hidden stays on
   screen. Found on the styling modal's "+ Paste text", which is meant to appear
   only with something on the clipboard — and it was true of the LAYOUTS "+
   Paste text" too, for as long as that button has existed. One rule repairs
   both, and every other .ghost-btn that ever gets hidden. */
.ghost-btn[hidden] { display: none; }
.ghost-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Anchors borrowing the class (e.g. Manage subscription) must not keep
     the UA underline — a ghost-btn is a button, wherever it points. */
  text-decoration: none;
  /* Sans button labels (sentence case) — mono-uppercase is reserved for
     structural eyebrows (01 DEVICE, STILLS), not interactive controls. */
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.01em;
  background: var(--btn2bg);
  color: var(--ink65);
  border: 0;
  border-radius: 8px;
  padding: 9px 14px;
  transition: background .15s ease, color .15s ease;
}
.ghost-btn:hover { color: var(--ink); background: var(--btn2bg-hover); }
.ghost-btn:disabled { opacity: .4; cursor: not-allowed; }
/* Destructive emphasis (e.g. the primed "Delete selected" in the library bar). */
.ghost-btn.is-danger { background: #b8352f; color: #fff; }
.ghost-btn.is-danger:hover { background: #a12e29; color: #fff; }
/* inline-flex above overrides the UA [hidden] rule, so restore it —
   otherwise the hidden #saveSettings trigger shows as a grey band. */
.ghost-btn[hidden] { display: none; }

/* Tier 2 variants — same weight class, differentiated by meaning so a
   row of greys stops reading as four equal lozenges:
     .is-choice → outlined chip: one option among several (the
                  Page top / Place marker / Pick element / Page bottom
                  method rows). Lighter than the filled secondary.
     .is-add    → dashed outline: adds something that doesn't exist
                  yet (+ Click, + Hover, + Add pause, + Add clip,
                  + Add reveal). Reads as an empty slot to fill.
   Filled .ghost-btn remains for direct commands (Pick target, Edit,
   Measure…). */
.ghost-btn.is-choice {
  background: transparent;
  border: 1px solid var(--bmed);
  color: var(--ink65);
  padding: 8px 13px;
}
.ghost-btn.is-choice:hover {
  background: var(--ink08);
  border-color: var(--bstr);
  color: var(--ink);
}
/* Selected chip (ratio crop choices etc.) — filled like a seg pill's
   active state so the chosen option is unmistakable (Mark). */
.ghost-btn.is-choice.is-selected {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.ghost-btn.is-choice.is-selected:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.ghost-btn.is-add {
  background: transparent;
  border: 1px dashed var(--bmed);
  color: var(--ink50);
  padding: 8px 13px;
}
.ghost-btn.is-add:hover {
  background: var(--ink08);
  border-color: var(--bstr);
  color: var(--ink);
}

/* Tier 3 — tertiary text-only actions. No fill, no border; present
   but never competing for attention. */
.ghost-btn.is-quiet,
.ghost-btn.tour-section-edit {
  background: transparent;
  color: var(--ink50);
  padding: 8px 10px;
}
.ghost-btn.is-quiet:hover,
.ghost-btn.tour-section-edit:hover {
  background: transparent;
  color: var(--ink);
}

/* Result-plus-change — the methods row (secondary buttons) shows until
   a position resolves, then collapses to a single resolved value with
   a quiet Change action pinned to the right. */
.position-methods {
  display: flex;
  gap: 6px;
  align-items: center;
  /* One line, not two: the 3-button case (Area's Set area / Pick element /
     Draw area) was wrapping. Tighter chips + nowrap keep them on a single
     row. (Mark) */
  flex-wrap: nowrap;
  margin-top: 4px;
}
.position-methods .ghost-btn.is-choice,
.position-methods .ghost-btn.is-add {
  padding: 7px 9px;
  white-space: nowrap;
}
.position-method-note {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink50);
  margin: 0 0 8px;
}
.position-resolved { margin-top: 4px; }
.picked-target-display.position-resolved .ghost-btn.is-quiet { margin-left: auto; }

/* ---------- Solid action ---------- */
.solid-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 16px 18px;
  background: var(--btnbg);
  color: var(--btnfg);
  /* was: var(--mono) - see BUTTONS: NO MONO */
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-top: 24px;
  transition: opacity .15s ease;
}
.solid-btn:hover { opacity: 0.88; }
.solid-btn:disabled { opacity: .35; cursor: not-allowed; }

/* ---------- Inline notes ---------- */
.note {
  display: flex;
  gap: 14px;
  padding: 16px 20px;
  border: 1px solid var(--bmed);
  margin-top: 20px;
}
.note-lbl {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink65);
  white-space: nowrap;
  padding-top: 2px;
}
.note-txt {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink);
  line-height: 1.6;
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
}
/* Get Pro CTA — a real button (ghost-btn), no link underline. */
.note-cta { text-decoration: none; }

/* Get Pro — its own darker box under Licence, with a black action button.
   The box uses --canvas (a step darker than the paper section in every look;
   in dark mode it flips to the near-black canvas). The button uses --ink so it
   reads black in light mode and inverts cleanly in dark. */
/* display:flex would defeat the [hidden] attribute (UA display:none) —
   restate it, same fix the g2 detail overlay needed. Hidden while Pro. */
#setting-pro[hidden] { display: none; }
#setting-pro {
  background: var(--canvas);
  border: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
}
.pro-cta-copy { min-width: 0; }
.pro-cta-title { font-family: var(--sans); font-size: 14px; font-weight: 500; color: var(--ink); }
.pro-cta-sub { font-size: 12px; font-weight: 400; color: var(--ink50); line-height: 1.5; margin-top: 3px; }
.pro-cta-btn {
  flex: none;
  text-decoration: none;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  padding: 9px 16px;
  border-radius: 8px;
  white-space: nowrap;
  transition: opacity .12s ease;
}
.pro-cta-btn:hover { opacity: .82; }

/* ---------- Toggles ---------- */
.toggle {
  position: relative;
  width: 34px;
  height: 20px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  background: var(--ink08);
  flex-shrink: 0;
}
.toggle .knob {
  position: absolute;
  top: 50%;
  left: 2px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--ink32);
  transform: translateY(-50%);
  transition: left .18s ease, background .18s ease;
}
.toggle.is-on { background: var(--ink); border-color: var(--ink); }
.toggle.is-on .knob { left: 18px; background: var(--bg); }
/* Locked settings toggle (e.g. Auto-download without an active licence) —
   visibly present but inert; the row hint explains why. */
.toggle.is-disabled { opacity: 0.4; cursor: default; pointer-events: none; }

/* ---------- Setting rows ----------
   Used for the Padding input inside Options blocks alongside the
   collapsing .option-row siblings. Border colour matches .option-row
   (var(--ink08)) so the Padding separator doesn't read darker than
   the rest of the stack. */
/* display:flex beats the UA's [hidden] rule — without this, a JS-hidden
   row (the DEV tier/intro rows on a non-flagged machine) stays VISIBLE but
   unwired: "none of the options are selectable" (Mark, 20 July 2026). */
.row[hidden] { display: none !important; }
.row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  padding: 14px 0;
  border-bottom: 1px solid var(--ink08);
}
.row:last-child { border-bottom: 0; }
.row .row-actions { display: flex; gap: 8px; align-items: center; flex: none; }
.row .row-actions .ghost-btn[hidden] { display: none; }
.row .label-block { flex: 1; }
.row .label {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink);
  margin-bottom: 4px;
}
.hint {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink65);
  line-height: 1.55;
}
.row .hint {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink65);
  line-height: 1.55;
}
/* Info tooltip — a quiet "i" beside a label that reveals a short
   explanation on hover/focus. The home for "nice to know" detail that
   used to live in permanent hint paragraphs. Discoverable (the dot
   signals help exists), on-demand (no clutter), keyboard-reachable. */
.info-tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  margin-left: 6px;
  border: 1px solid var(--ink25, var(--ink08));
  border-radius: 50%;
  background: transparent;
  color: var(--ink50);
  font-family: var(--sans);
  font-size: 10px;
  font-style: normal;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1;
  cursor: help;
  position: relative;
  vertical-align: middle;
  flex: none;
  padding: 0;
}
.info-tip::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: max-content;
  max-width: 240px;
  background: var(--ink);
  color: var(--paper);
  padding: 8px 11px;
  border-radius: 8px;
  font-family: var(--sans);
  font-size: 12px;
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease, transform .12s ease;
  z-index: 200;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}
.info-tip:hover::after,
.info-tip:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ---------- Inputs ---------- */
.text-input {
  display: inline-block;
  padding: 10px 14px;
  background: var(--bg2);
  border: 1px solid var(--bmed);
  border-radius: 8px;
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 13px;
  min-width: 220px;
  transition: border-color .15s ease;
}
.text-input:hover { border-color: var(--bstr); }
/* The input's own border is the focus signal — suppress the global
   focus-visible ring so it doesn't double up into an outline-over-box
   (Mark: "the outline with the underline does not work"). */
.text-input:focus { border-color: var(--ink); outline: none; }
.text-input:focus-visible { outline: none; }
.text-input::placeholder { color: var(--ink32); }

.num-input {
  background: var(--bg2);
  border: 1px solid var(--bmed);
  color: var(--ink);
  font-family: var(--mono);
  font-weight: 400;
  font-size: 13px;
  padding: 10px 14px;
  width: 88px;
  text-align: center;
  transition: border-color .15s ease;
}
.num-input:hover { border-color: var(--bstr); }
.num-input:focus { border-color: var(--ink); }

.field-stack { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 18px; }
.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* `.field` defaults to flex which would override the native [hidden]
   attribute. Re-assert display: none so JS toggling `field.hidden`
   actually hides the field. */
.field[hidden] { display: none; }
/* Control labels read in body sans, sentence case. Mono-uppercase is
   reserved for the structural markers (eyebrow, step labels, numbers) so
   the two voices stay distinct: caps = wayfinding, sans = content. */
.field-label {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
}

/* ---------- Pill select group ---------- */
.pill-select { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.pill {
  display: inline-flex;
  align-items: center;
  padding: 7px 14px;
  border: 1px solid var(--bmed);
  /* ROUNDED SQUARE, EVERYWHERE (25 Aug 2026, Mark: "we have pills in
     settings drop downs and rounded square buttons in other places. seems
     random and inconsistent. Shall we the rounded square buttons for all
     settings/options") — one corner for every option chip, the 9px the
     compact pr-list settled on 11 Aug. */
  border-radius: 9px;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink50);
  background: transparent;
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
/* The hidden attribute must beat .pill's display:inline-flex — used by
   conditional pills like the viewport Phone corner (mobile only). */
.pill[hidden],
[data-viewport-phone-pill][hidden] { display: none !important; }
.pill:hover { color: var(--ink); border-color: var(--bstr); }
.pill.is-selected,
#setting-appearance .pill.is-selected {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

/* ---------- Segmented control ----------
   For genuine, mutually-exclusive state choices (Click / Hover,
   Wait for). One rounded container, solid fill on the selected
   option, quiet text on the rest — the device-category switcher
   pattern, reused so every persistent choice looks the same.
   Add `seg` to a .pill-select to opt a group in. */
.pill-select.seg {
  gap: 6px;
  flex-wrap: nowrap;
  padding: 3px;
  border-radius: 11px;   /* follows the pills' 9px corner (25 Aug 2026) */
  background: transparent;
  border: 1px solid var(--bmed);
}
/* Motion controls — Speed, Easing and every More-options row share one
   layout: a fixed-width label column on the left, the segmented control
   left-aligned right after it, so all controls line up in one column.
   Sub-content (sticky offset, retina readout) flows below its row. */
.motion-line,
.motion-row { display: flex; align-items: center; gap: 16px; }
.motion-line .field-label,
.motion-row .field-label {
  min-width: 118px;
  flex: none;
  margin: 0;
  font-family: var(--sans);
  font-size: 13px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}
.motion-line .pill-select.seg,
.motion-row .pill-select.seg { flex: none; }
/* Boolean settings use a toggle switch rather than an Off/On segment —
   one glance, no labels to read. Sits in an option-row (for the divider)
   with the label left and the switch right. */
.opt-toggle-row { display: flex; align-items: center; justify-content: space-between; padding: 14px 0; }
/* ── THE TYPE PANEL BREATHES LESS (#308, 17 Aug 2026) ─────────────────────
   Mark: "also the spacing between the sliders can be reduced." Four sliders at
   14px top and bottom is 112px of air in a rail that also has to hold a font
   menu, a weight menu, a swatch grid and an icon row. Scoped to the type
   editors rather than changed globally: the same class carries the capture
   settings, where the rows are further apart on purpose because they are
   unrelated to each other. These four are one group and read better tight. */
/* Back to the rail's own 14px on 21 Sep 2026 (Mark: "all rails should have consistent spacing, fonts etc"): 48px rows everywhere. */

/* ── ALIGNMENT AND LAYER, ONE ROW (#308) ──────────────────────────────────
   Mark: "could we combine the alignment and layer so they are icons on 1 row?"
   Two segmented groups with real space between them — they answer different
   questions, and a single run of five buttons would read as one control with
   five states. */
.tx-iconrow { display: flex; align-items: center; margin: 16px 0 2px; }
.tx-iconrow-gap { width: 18px; flex: none; }
/* THE ICON PILL. Named `.is-icon` (the app's own word for an icon button — see .crop-ov-ratio.is-icon) rather than the old `.tx-ico` since #314 (17 Aug
   2026), because the slide-direction arrows are the third control to want it
   and they have nothing to do with type. Same tier idea as `.ghost-btn.is-add`:
   a class that says what KIND of pill this is, not which panel it sits in.

   Specificity deliberately matches .pill-select.seg .pill and adds one class,
   because that rule sets `padding: 8px 18px` for a WORD and would otherwise win
   — an 18px side pad round a 15px glyph is the blob this first rendered as.

   The second selector is not decoration. `.reel-side .pill-select.seg .pill`
   (line ~12758) narrows that padding to 8px 14px for the Sequence rail, and it
   carries the SAME (0,3,0) weight while sitting LATER in the file — so in
   Sequence it was quietly winning, and the alignment icons have been drawing a
   15px glyph inside a 6px content box ever since #308 shipped. Naming the rail
   here settles it by specificity instead of by line order. */
.pill-select.seg .pill.is-icon,
.reel-side .pill-select.seg .pill.is-icon {
  width: 34px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.pill-select.seg .pill.is-icon svg { width: 15px; height: 15px; display: block; }
/* Auto-stop: the seconds stepper + switch ride together on the right so
   space-between only splits them from the label. The stepper hides via
   [hidden] when Auto-stop is off (the :not guard keeps the attribute working). */
.opt-inline-end { display: inline-flex; align-items: center; gap: 12px; }
.autostop-secs:not([hidden]) { display: inline-flex; align-items: center; }
/* Sticky-header Custom row: let it wrap and keep the Pick header button's
   label on one line, so in the narrow options column the button drops to its
   own line instead of being squashed into a two-line block (Mark). */
[data-sticky-custom-row] { flex-wrap: wrap; }
[data-sticky-custom-row] [data-sticky-pick] { white-space: nowrap; flex: none; }
/* Inline padding slider (Stills Image output): a monochrome range with a
   live px value, sits inline on the right of the opt-toggle-row. */
.opt-slider-wrap { display: flex; align-items: center; gap: 10px; flex: none; }
.opt-slider-val {
  /* Mono AND the same colour as every other option value — .option-value's
     --ink50 (Mark, 16 July 2026: the px/% numbers were a different colour). */
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink50);
  font-variant-numeric: tabular-nums;
  /* A FIXED column, not a minimum (Mark, 12 Aug 2026: "the sliders for the
     text settings are all uneven"). The rows are space-between, so the wrap
     is pinned right and the SLIDER's left edge floats on whatever the readout
     happens to measure: Size sat at 6.2%, Opacity at 100%, and Spacing at
     -0.015em — 16px past the minimum — so the middle of three stacked tracks
     started 16px left of its neighbours. A min-width can only ever hold the
     SHORT values in line with each other; the long one is the whole problem.
     Sized from the content, not guessed: this is a tabular monospace, so 8ch
     is exactly the widest readout the app can produce (-0.195em, the
     letter-spacing floor), plus the 8 letter-spacings that ch does not count.
     It follows the font-size down in the sequence panel with no second
     measurement. flex: none because a flex item with a width is still
     shrinkable by default, which would put the wobble straight back. */
  width: calc(8ch + 0.32em);
  flex: none;
  text-align: right;
}
.opt-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 132px;
  height: 3px;
  border-radius: 999px;
  background: var(--bmed);
  outline: none;
  cursor: pointer;
}
.opt-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 10px; height: 10px; /* was 12 — a touch smaller (Mark) */
  border-radius: 50%;
  background: var(--ink);
  border: none;
  cursor: pointer;
}
.opt-slider::-moz-range-thumb {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--ink);
  border: none;
  cursor: pointer;
}
.opt-slider:focus-visible { outline: 2px solid var(--ink32); outline-offset: 3px; }
.canvas-acc-body .opt-slider { width: 96px; }
.opt-switch {
  width: 44px;
  height: 24px;
  flex: none;
  position: relative;
  padding: 0;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  background: transparent;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.opt-switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--ink50);
  transition: left .15s ease, background .15s ease;
}
.opt-switch.is-on { background: var(--ink); border-color: var(--ink); }
.opt-switch.is-on .opt-switch-knob { left: 22px; background: var(--paper); }
/* Options match the device switcher exactly: sans-serif Title case,
   same padding and weight, solid ink on the selected one. */
.pill-select.seg .pill {
  border: 0;
  background: transparent;
  border-radius: 8px;   /* nests in the track's 11px (25 Aug 2026) */
  padding: 8px 18px;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink50);
}
.pill-select.seg .pill:hover { color: var(--ink); background: transparent; }
.pill-select.seg .pill.is-selected {
  background: var(--ink);
  color: var(--paper);
}

/* ---------- Status indicator ---------- */
/* Errors-only surface (Mark): the line is hidden for idle ("Ready") and
   for in-progress states — you're not watching it mid-capture — and only
   shows up, prominently, when something actually needs attention. */
.status {
  display: none;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.1em;
  color: var(--ink32);
  margin-top: 14px;
}
.status .status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink18);
  flex: none;
}
.status.is-recording .status-dot { background: var(--ink); animation: pulse 1s ease-in-out infinite; }
.status.is-error {
  display: flex;
  font-size: 12px;
  color: var(--ink);
}
.status.is-error .status-dot { background: #c14a3b; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }

/* ---------- Guide steps ---------- */
.steps { margin-top: 12px; }
.step {
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 20px;
  padding: 24px 0;
  border-bottom: 1px solid var(--ink08);
  align-items: start;
}
.step:last-child { border-bottom: 0; }
.step-n {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 400;
  color: var(--ink32);
  line-height: 1.1;
}
.step-t {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink);
  margin: 0 0 6px;
}
.step-b {
  font-size: 12px;
  font-weight: 400;
  color: var(--ink65);
  line-height: 1.85;
  margin: 0;
}
.step-b ol { margin: 8px 0 0; padding-left: 18px; }
.step-b li { margin-bottom: 4px; }

/* ---------- Guide pages (one panel per mode) ----------
   Each mode guide follows the same template: a one-line "what it does"
   in the subtitle, then template blocks (When to use / How to set up /
   Settings / Tips). Mono section headings + ink65 body match the rest
   of the system. */
.guide-doc { margin-top: 18px; max-width: 640px; }
.guide-block { padding: 22px 0; border-bottom: 1px solid var(--ink08); }
.guide-block:first-child { padding-top: 4px; }
.guide-block:last-child { border-bottom: 0; }
.guide-h {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink);
  margin: 0 0 12px;
}
.guide-p {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink65);
  line-height: 1.85;
  margin: 0 0 10px;
}
.guide-p:last-child { margin-bottom: 0; }
.guide-list { margin: 0; padding-left: 18px; }
.guide-list li {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink65);
  line-height: 1.8;
  margin-bottom: 9px;
}
.guide-list li:last-child { margin-bottom: 0; }
.guide-list li strong { color: var(--ink); font-weight: 400; }

/* Guide flow mockups — recreate the app's vertical stop-card editor so a
   guide shows the REAL UI (white bead cards on a connector, the + insert,
   transition links), placed in the guide's right-hand column. Themed with
   the live vars so it tracks light/dark and matches the panels 1:1. */
.guide-split {
  display: grid;
  /* Prose keeps its normal left position (a readable measure, like the
     other panels); the mockup floats CENTRED in the open space to its
     right — not jammed against the text, not hugging the window edge. */
  grid-template-columns: minmax(0, 540px) minmax(0, 1fr);
  column-gap: 40px;
  align-items: start;
}
.guide-main { min-width: 0; }
/* Drop the aside down so its first card lines up with the subtitle /
   summary line (clears the eyebrow + 40px title), centre it in its track,
   and keep it in view while reading the steps. */
.guide-aside { width: 300px; justify-self: center; position: sticky; top: 16px; margin-top: 86px; }
@media (max-width: 1180px) {
  .guide-split { grid-template-columns: 1fr; row-gap: 28px; }
  .guide-aside { position: static; width: auto; max-width: 340px; justify-self: start; margin-top: 8px; }
}
.gm-flow {
  background: var(--bg2);
  border: 1px solid var(--ink08);
  border-radius: 18px;
  padding: 14px;
  display: flex;
  flex-direction: column;
}
.gm-card {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--card-fill);
  border-radius: 16px;
  padding: 12px 14px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04);
}
.gm-card-added { box-shadow: 0 0 0 1.5px var(--ink), 0 1px 2px rgba(14, 14, 12, 0.06); }
.gm-num { font-family: var(--mono); font-size: 11px; color: var(--ink50); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; flex: none; }
.gm-label { font-size: 12px; color: var(--ink); flex: 1; min-width: 0; }
.gm-sum { font-family: var(--mono); font-size: 10px; color: var(--ink50); flex: none; }
.gm-pill { font-size: 11px; color: var(--ink65); background: var(--ink08); border-radius: 100px; padding: 3px 10px; flex: none; }
.gm-insert { position: relative; display: flex; align-items: center; justify-content: center; min-height: 30px; }
.gm-insert::before { content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--bmed); transform: translateX(-50%); }
.gm-plus {
  position: relative; z-index: 1;
  width: 28px; height: 28px; border-radius: 100px;
  border: 1px solid var(--bmed); background: var(--card-fill);
  color: var(--ink65); display: flex; align-items: center; justify-content: center;
  font-size: 16px; line-height: 1;
}
.gm-caption { font-size: 12px; color: var(--ink50); line-height: 1.7; margin-top: 14px; }
.gm-caption strong { color: var(--ink); font-weight: 400; }
/* Stacked variant — independent cards (Asset crops, Page clips) sit with
   a small gap instead of the connector + insert used by sequential tours. */
.gm-flow.is-stack { gap: 8px; }
.gm-addbtn {
  border: 1px dashed var(--bmed);
  background: transparent;
  color: var(--ink50);
  border-radius: 14px;
  padding: 10px;
  /* was: var(--mono) / 11px / 0.04em - see BUTTONS: NO MONO */
  font-family: var(--sans);
  font-size: 12.5px;
  letter-spacing: 0;
  text-align: center;
}
/* Site Flow page cards + transition links */
.gm-page { background: var(--card-fill); border-radius: 16px; padding: 12px 14px; box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04); }
.gm-page-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.gm-page-tag { font-family: var(--mono); font-size: 10px; color: var(--ink50); letter-spacing: 0.04em; flex: none; }
.gm-page-name { font-size: 12px; color: var(--ink); }
.gm-stop { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--ink65); padding: 3px 0; }
.gm-stop-dot { width: 5px; height: 5px; border-radius: 100px; background: var(--ink32); flex: none; }
.gm-trans { position: relative; display: flex; align-items: center; justify-content: center; min-height: 34px; }
.gm-trans::before { content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--bmed); transform: translateX(-50%); }
.gm-trans-label {
  position: relative; z-index: 1;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink50); background: var(--bg2); border: 1px solid var(--bmed);
  border-radius: 100px; padding: 3px 9px;
}

/* ---------- Settings sections (anchored) ---------- */
/* Preferences reads as a column, not full-bleed across the whole work area
   (Mark: "currently full width"), with breathing room above the header
   (the eyebrow that used to provide it was removed). */
/* The three sidebar-less pages (Library / Guide / Settings) share ONE centred
   column width so switching between them doesn't jump the content area (Mark). */
.panel[data-panel="settings-all"],
.panel[data-panel="history-all"],
.panel[data-panel="history-sets"],
.panel[data-panel="history-layouts"],
.panel[data-panel="history-reels"] { max-width: 1120px; margin: 0 auto; }
.settings-section { background: var(--bg2); border: 1px solid var(--ink08); border-radius: 10px; padding: 4px 20px 8px; margin-bottom: 14px; }
.settings-section:last-child { margin-bottom: 0; }
.settings-section .row:last-child { border-bottom: 0; }
.settings-section h2 {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink50);
  margin: 14px 0 2px;
}
.settings-section .section-sub {
  font-size: 12px;
  font-weight: 400;
  color: var(--ink50);
  line-height: 1.55;
  margin: 0 0 6px;
}

/* ── Device management list (Settings · Licence). Quiet rows, hairline
   separated — a utility surface, not a feature. */
#devices-list { margin: 4px 0 10px; }
.device-row { display: flex; align-items: center; gap: 12px; padding: 9px 2px;
  border-top: 1px solid var(--ink08); }
.device-row:first-child { border-top: none; }
.device-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.device-name { font-family: var(--sans); font-size: 12.5px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.device-sub { font-family: var(--mono); font-size: 9.5px; letter-spacing: .06em;
  color: var(--ink-mute); }
.devices-empty { font-family: var(--sans); font-size: 12px; color: var(--ink-mute);
  padding: 8px 2px 10px; }

/* ── The gate card (upgrade moment). A quiet anchored card — paper surface,
   PRO chip in the mono small-caps register, sentence-case sans actions
   (utility voice). Never a modal takeover; positioned by showGateCard. */
.gate-card { position: fixed; z-index: 1200; width: 296px; box-sizing: border-box;
  background: var(--paper); color: var(--ink); border: 1px solid var(--bmed);
  border-radius: 10px; padding: 16px 16px 14px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, .05), 0 16px 40px rgba(14, 14, 12, .14);
  font-family: var(--sans); }
.gate-chip { display: inline-block; font-family: var(--mono); font-size: 9px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink);
  border: 1px solid var(--bstr); border-radius: 100px; padding: 3px 8px 2px; }
.gate-msg { font-size: 13px; line-height: 1.5; margin-top: 10px; letter-spacing: -0.01em; }
.gate-price { font-family: var(--mono); font-size: 10.5px; letter-spacing: .04em;
  color: var(--ink-mute); margin-top: 7px; }
/* The monthly fact doubles as the monthly checkout — quiet inline link. */
.gate-price-monthly { border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.gate-price-monthly:hover { color: var(--ink); }
.gate-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 13px; }
.gate-btn { font-family: var(--sans); font-size: 12.5px; font-weight: 500; cursor: pointer;
  border: none; border-radius: 100px; padding: 7px 14px;
  background: var(--btn2bg); color: var(--ink); }
.gate-btn:hover { background: var(--btn2bg-hover); }
.gate-btn-primary { background: var(--btnbg); color: var(--btnfg); }
.gate-btn-primary:hover { background: var(--btnbg); opacity: .88; }
.gate-btn[disabled] { opacity: .5; cursor: default; }
.gate-keyrow { display: flex; gap: 8px; margin-top: 12px; }
.gate-keyrow[hidden] { display: none; }
.gate-key-input { flex: 1; min-width: 0; font-family: var(--mono); font-size: 11.5px;
  color: var(--ink); background: var(--pill); border: 1px solid var(--border);
  border-radius: 8px; padding: 7px 10px; outline: none; }
.gate-key-input:focus { border-color: var(--bstr); }
.gate-status { font-size: 12px; color: var(--ink-soft); line-height: 1.45; margin-top: 9px; }

/* ── Cloud storage meter (Settings · Licence). Monochrome: the bar carries
   the proportion, the words carry the state — no green/amber/red at any
   threshold (spec). Mono small-caps label in the house register. */
#cloud-meter { margin: 18px 0 6px; padding: 0 2px; }
.cloud-meter-label { font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-mute); margin-bottom: 9px; }
.cloud-meter-track { height: 3px; border-radius: 100px; background: var(--ink08); overflow: hidden; }
.cloud-meter-fill { height: 100%; width: 0%; border-radius: 100px; background: var(--ink);
  transition: width .4s cubic-bezier(0.22, 1, 0.36, 1); }
.cloud-meter-line { font-family: var(--sans); font-size: 12px; color: var(--ink); margin-top: 9px; }
.cloud-meter-sub { font-family: var(--sans); font-size: 12px; color: var(--ink-mute); margin-top: 3px; }
.cloud-meter-note { font-family: var(--sans); font-size: 12px; color: var(--ink-soft); margin-top: 8px; line-height: 1.5; }


/* ---------- Floating version label ---------- */
/* Sits unobtrusively in the bottom-right corner of the app.
   No footer strip eating space. */
.version-floating {
  position: fixed;
  bottom: 12px;
  right: 16px;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink32);
  pointer-events: none;
  z-index: 10;
}

/* ============================================================
   v1.4 — additions for unified capture flow
   ============================================================ */

/* ---------- Step labels inside capture panels ----------
   Single-line header: a large Söhne Mono step number drawn via
   ::before from the data-step attribute, with the uppercase mono
   label inline next to it. Reads as "01 Device", "02 Options" etc.
   Acts as a chapter heading per step without relying on rules,
   boxes or other graphical elements. */
.step-label {
  display: flex;
  align-items: baseline;
  gap: 16px;
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 52px 0 18px;
}
.step-label::before {
  /* Step numerals — Söhne Mono per the locked spec for all numerals.
     Upright; Söhne italics are reserved for body-copy emphasis only.
     Sits inline with the label, scaled up to match the bumped
     label size so the proportion stays balanced. */
  content: attr(data-step);
  font-family: var(--mono);
  font-size: 28px;
  font-weight: 400;
  font-style: normal;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.step-label:first-of-type { margin-top: 32px; }

/* ---------- Action footer ----------
   Replaces the numbered "05 Capture" step at the end of each panel.
   The numbered steps now read as configuration; this unnumbered
   footer reads as execution. Title + a one-line summary of what the
   user has set up, then the primary action. */
.action-footer {
  margin-top: 56px;
  padding-top: 0;
}
.action-footer-head {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 20px;
}
.action-footer-title {
  font-family: var(--sans);
  font-size: 22px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.2;
}
.action-footer-summary {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-mute);
  letter-spacing: 0;
}
.action-footer-summary:empty {
  /* When state can't be summarised yet (no source picked, no device,
     etc.), leave the summary blank rather than reserving the row —
     keeps the title from floating awkwardly above empty space. */
  display: none;
}
.action-footer .btn-primary {
  display: inline-flex;
  width: auto;
  min-width: 220px;
}
.action-footer .ghost-btn {
  margin-left: 12px;
}
/* Viewport panel: "Open capture window" + "Quick Capture" share a row
   rather than the Quick Capture button stacking beneath the full-width
   primary. */
.viewport-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 24px;
  flex-wrap: wrap;
}
.viewport-actions .btn-primary { width: auto; margin-top: 0; flex: 0 0 auto; min-width: 220px; }
.viewport-actions .ghost-btn { flex: none; margin-left: 0; }

/* Preview action footer — small primary button mirrored at the
   bottom of the preview panel so the user can fire the capture
   from wherever their attention is (panel OR preview). Same
   underlying action as the main panel's action-footer button. */
.preview-action-footer {
  margin-top: 10px;
  padding-top: 8px;
  padding-bottom: 14px;
}
.preview-action-footer .btn-primary {
  width: 100%;
}
.preview-action-footer .btn-primary[disabled] {
  opacity: 0.45;
  pointer-events: none;
}
/* Video preview button — opens the capture window to preview stops, not
   record. Full-width pill but secondary (grey), clearly not the amber CTA. */
.preview-action-footer .preview-open-btn {
  width: 100%;
  border-radius: 100px;
  padding: 13px 18px;
  /* Font inherits .ghost-btn (sans 12px, sentence case) so it matches the
     other buttons — no mono-uppercase override. */
}
.preview-action-footer .preview-open-btn[disabled] {
  opacity: 0.45;
  pointer-events: none;
}

/* ---------- Device category tabs ---------- */
.dcat {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--ink08);
  margin-bottom: 16px;
}
.dcat-item {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 8px 18px 8px 0;
  color: var(--ink50);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .12s, border-color .12s;
}
.dcat-item:hover { color: var(--ink); }
.dcat-item.on { color: var(--ink); border-bottom-color: var(--ink); }

/* ---------- Device category pills (Desktop / Tablet / Mobile) ----------
   Compact pill row instead of the older big-card layout. The current
   selection (preset name + dimensions) reads as a quiet summary line
   below the pills so the buttons stay light. */
.dv-pill-row {
  display: inline-flex;
  gap: 6px;
  margin-bottom: 12px;
  padding: 3px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
}
.dv-pill {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  padding: 8px 18px;
  border-radius: 100px;
  color: var(--ink50);
  background: transparent;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
  border: 0;
}
.dv-pill:hover { color: var(--ink); }
.dv-pill.is-selected {
  background: var(--ink);
  color: var(--paper);
}

.dv-summary {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 400;
  color: var(--ink);
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.dv-summary-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink50);
}
.dv-summary-sep { color: var(--ink-mute); }
.dv-summary-name { color: var(--ink); }
.dv-summary-dims {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* Mobile variant of the device summary — stacks the name above two
   lines of explanatory copy instead of the inline name · dims chip
   used by Desktop/Tablet. */
.dv-summary-mobile {
  display: block;
  margin-bottom: 14px;
}
.dv-summary-mobile .dv-summary-name {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 400;
  color: var(--ink);
  margin-bottom: 4px;
}
.dv-summary-desc {
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink-soft);
  line-height: 1.45;
}

.dcat-more-row {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 6px 0 10px;
}
.dcat-more-toggle,
.dcat-custom-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 0;
  padding: 4px 0;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink65);
  cursor: pointer;
  transition: color .12s ease;
}
.dcat-more-toggle:hover,
.dcat-custom-toggle:hover { color: var(--ink); }
.dcat-more-body {
  margin-bottom: 14px;
}

/* ---------- Device grid (v1.4 short names) ---------- */
.dgrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
  margin-bottom: 14px;
}
.dv {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 9px 12px;
  border: 1px solid var(--bmed);
  border-radius: 10px;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: border-color .12s, background .12s;
}
.dv:hover { border-color: var(--bstr); }
.dv.on {
  border-color: var(--ink);
  background: var(--pill);
}
.dv-name {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink);
}
.dv-example {
  font-size: 11px;
  font-weight: 400;
  color: var(--ink65);
  line-height: 1.3;
}
.dv-dims {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  color: var(--ink50);
  letter-spacing: 0.04em;
  margin-top: 1px;
}

/* ---------- Orientation pills (v1.4 short names) ---------- */
.orient {
  display: flex;
  gap: 6px;
  margin-bottom: 14px;
}
.ori {
  display: inline-flex;
  align-items: center;
  padding: 7px 15px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 400;
  color: var(--ink65);
  cursor: pointer;
  transition: border-color .12s, background .12s, color .12s;
}
.ori:hover { color: var(--ink); border-color: var(--bstr); }
.ori.on {
  border-color: var(--ink);
  background: var(--pill);
  color: var(--ink);
}

/* ---------- Custom dims inputs ---------- */
.custom-inputs {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 0 14px;
}
.custom-inputs .num-inp,
.num-inp {
  background: var(--bg2);
  border: 1px solid var(--bmed);
  color: var(--ink);
  font-family: var(--mono);
  font-weight: 400;
  font-size: 13px;
  padding: 9px 12px;
  width: 90px;
  text-align: center;
  transition: border-color .12s;
}
.num-inp:hover { border-color: var(--bstr); }
.num-inp:focus { border-color: var(--ink); outline: none; }

/* Custom number stepper — hide the clunky native spin buttons and put
   − / + buttons either side of the value in one compact pill.
   The .num-stepper wrapper is added in JS (wrapNumberSteppers). */
.num-inp::-webkit-inner-spin-button,
.num-inp::-webkit-outer-spin-button,
.num-inp-sm::-webkit-inner-spin-button,
.num-inp-sm::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.num-inp, .num-inp-sm { appearance: textfield; -moz-appearance: textfield; }
/* Number field paired with a unit suffix (e.g. the Space "px"). */
.num-inp-unit { display: inline-flex; align-items: center; gap: 7px; }
.num-unit { font-family: var(--mono); font-size: 12px; color: var(--ink50); flex: none; }
/* Horizontal stepper pill: [−] value [+]. The wrapper carries the box
   chrome (border + fill); the input inside goes borderless and narrow,
   the − / + buttons sit flush either side.
   KEPT AT 12px in the 7 Aug 2026 sweep that took every other 12 down to 10:
   Mark's exception was "except buttons and pills etc", and this is a button
   group wearing one box, not a surface holding content. */
.num-stepper {
  display: inline-flex;
  align-items: stretch;
  vertical-align: middle;
  border: 1px solid var(--bmed);
  background: var(--bg2);
  /* Rounded to sit with the pill cards and readouts (same 12px family
     as .picked-target-display); overflow clips the − / + hover fills
     to the corners. */
  border-radius: 12px;
  overflow: hidden;
  transition: border-color .12s;
}
.num-stepper:hover { border-color: var(--bstr); }
.num-stepper:focus-within { border-color: var(--ink); }
.num-stepper > input.num-inp,
.num-stepper > input.num-inp-sm {
  border: 0;
  background: transparent;
  text-align: center;
}
.num-stepper > input.num-inp { width: 52px; padding: 9px 0; }
.num-stepper > input.num-inp-sm { width: 36px; padding: 6px 0; }
/* Unit baked into the pill ("− 2.5 s +") — quiet, not part of the
   editable value, hugging the number it qualifies. */
.num-step-unit {
  align-self: center;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink50);
  margin-left: -3px;
  padding-right: 2px;
  pointer-events: none;
  user-select: none;
}
.num-step {
  width: 24px;
  flex: none;
  padding: 0;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--ink50);
  cursor: pointer;
  transition: color .12s ease, background .12s ease;
}
.num-step:hover { color: var(--ink); background: var(--pill); }
.num-step:active { background: var(--bmed); }
.custom-inputs .sep {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink50);
}

/* ---------- Selected dimensions readout ---------- */
.device-selected {
  font-family: var(--serif);
  font-size: 26px;
  font-weight: 400;
  color: var(--ink);
  padding: 14px 0;
  border-top: 1px solid var(--ink08);
  margin-top: 4px;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.device-selected em {
  font-style: normal;
  font-size: 14px;
  color: var(--ink32);
}
.device-selected .dev-label {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink32);
  margin-left: auto;
  align-self: center;
}

/* ---------- Primary action button ----------
   The capture / record action button. Distinct from every other
   button surface (pill shape + yellow fill) so it reads as the
   one definitive call to action across the panel and the preview.
   Yellow placeholder retired (Mark): clear button, just a border —
   back inside the locked monochrome palette. Hover fills ink. */
/* ==== BUTTONS: NO MONO (Mark, 9 Aug 2026: "yes mono not for buttons") ========
   Mono is for DATA — filenames, dimensions, hosts, a licence key. A button is
   a thing you press, and setting one in mono caps made every primary action
   read like a system message. The same call was already made for headings on
   the grid home; this finishes it.

   Not just the family: mono at 11px with 0.14em tracking and uppercase is a
   whole voice, and swapping only the typeface leaves wide-tracked shouting in
   the wrong one. Each button below moves to the sans voice the app already
   uses for its actions — sentence case, normal tracking, 13-ish.

   Reverting means restoring the declarations noted against each rule. The
   licence-key input, the price and the PRO chip keep mono deliberately: those
   are data and a marker, not actions.
   --------------------------------------------------------------------------- */
.btn-primary {
  /* was: var(--mono) / 11px / 400 / 0.14em / uppercase */
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 100px;
  padding: 14px 28px;
  width: 100%;
  margin-top: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  cursor: pointer;
  transition: background .12s ease, color .12s ease, transform .12s ease, opacity .12s ease;
}
.btn-primary:hover { background: var(--ink); color: var(--paper); }
.btn-primary:active { transform: translateY(1px); }
.btn-primary:disabled { opacity: 0.4; cursor: not-allowed; background: transparent; color: var(--ink); }
.btn-primary svg { flex: none; }

/* ---------- In-app modal (confirm + form dialogs) ---------- */
.capt-modal-root {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(14, 14, 12, 0.42);
  padding: 24px;
}
/* display: flex above overrides the native [hidden] attribute, which
   would otherwise show the modal as a blocking overlay on first load. */
.capt-modal-root[hidden] { display: none; }
.capt-modal-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 30px 32px;
  width: 100%;
  max-width: 460px;
  max-height: calc(100vh - 64px);
  overflow: auto;
  box-shadow: 0 24px 64px rgba(14, 14, 12, 0.18);
}
.capt-modal-title {
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 400;
  line-height: 1.15;
  margin: 0 0 14px 0;
  color: var(--ink);
}
.capt-modal-body {
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  margin: 0 0 20px 0;
}
.capt-modal-body p { margin: 0 0 10px; }
.capt-modal-body ul { margin: 0 0 10px 18px; padding: 0; }
.capt-modal-body li { margin: 0 0 4px; }
.capt-modal-fields {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-bottom: 22px;
}
.capt-modal-field-label {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.7;
  margin-bottom: 6px;
  display: block;
}
.capt-modal-field-input,
.capt-modal-field-select {
  width: 100%;
  font-family: var(--mono);
  font-size: 13px;
  background: var(--bg2);
  color: var(--ink);
  border: 1px solid var(--bmed);
  border-radius: 8px;
  padding: 9px 12px;
  outline: none;
}
.capt-modal-field-input:focus,
.capt-modal-field-select:focus { border-color: var(--ink); }
.capt-modal-field-input:focus-visible,
.capt-modal-field-select:focus-visible { outline: none; }
.capt-modal-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.capt-modal-btn {
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--bmed);
  border-radius: 100px;
  padding: 9px 20px;
  /* Labels never wrap into tall two-line pills (Mark, 24 July) — keep
     modal button text short instead of letting long ones fold. */
  white-space: nowrap;
  cursor: pointer;
  transition: opacity .12s ease, background .12s ease;
}
.capt-modal-btn:hover { opacity: 0.78; }
.capt-modal-btn.is-primary {
  background: var(--btnbg);
  color: var(--btnfg);
  border-color: var(--btnbg);
}
.capt-modal-btn:disabled { opacity: 0.35; cursor: not-allowed; }

/* ---------- Picked-selector clear button (small × inside inputs) ---------- */
.selector-clear-btn {
  background: transparent;
  border: 0;
  color: var(--ink);
  opacity: 0.5;
  /* was: var(--mono) - see BUTTONS: NO MONO */
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1;
  padding: 4px 8px;
  cursor: pointer;
  transition: opacity .12s ease;
}
.selector-clear-btn:hover { opacity: 1; }

/* ---------- Pro upgrade prompt ---------- */
.pro-prompt {
  padding: 24px 0 8px;
}
.pro-prompt-title {
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 400;
  color: var(--ink);
  margin: 0 0 10px;
}
.pro-prompt-body {
  font-size: 13px;
  font-weight: 400;
  line-height: 1.85;
  color: var(--ink65);
  margin: 0 0 22px;
  max-width: 380px;
}
.pro-prompt-input {
  display: block;
  width: 100%;
  max-width: 360px;
  margin: 0 0 18px;
}
.pro-prompt-link {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  border-bottom: 1px solid var(--bstr);
  padding-bottom: 2px;
  text-decoration: none;
  display: inline-block;
  transition: border-color .12s, color .12s;
}
.pro-prompt-link:hover { color: var(--ink); border-color: var(--ink); }

/* ---------- Pro badge in sidebar ---------- */
.pro-badge {
  font-family: var(--mono);
  font-size: 7px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink32);
  margin-left: auto;
  padding-left: 8px;
  /* Not there all the time — only surfaces on hover (and for the active
     item) so the rail stays calm. Opacity (not display) keeps layout
     stable so the row doesn't shift when it appears. */
  opacity: 0;
  transition: opacity .15s ease;
}
.side-item:hover .pro-badge,
.side-item.is-active .pro-badge { opacity: 1; }
.side-item.is-pro {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
.side-item.is-pro > span:nth-child(2) {
  flex: 1;
}

/* ---------- Section tour: section rows ----------
   Default `.tour-section` layout used by Multi Page Tour + Area
   Capture (both kept on the original stacked-card pattern for now —
   Multi Page rolls into the journey layout once Single Page lands).
   Single Page Tour overrides this further down with a rail + connector
   treatment, scoped to `#tour-sections`. */
.tour-sections {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.tour-section {
  border: 1px solid var(--bmed);
  padding: 16px 18px;
}
/* Follow-along highlight: the card the rehearsal is currently on. A soft
   selected look — a faint fill lift + a medium edge ring (Mark wanted the
   rehearse beat soft, not the hard black of the active-card ring). */
.tour-section.is-rehearsing > .tour-section-card {
  background: var(--pill);
  box-shadow: 0 0 0 1.5px var(--bstr) inset;
  transition: box-shadow .15s ease, background .15s ease;
}
.tour-section-head {
  display: flex;
  align-items: center;
  margin-bottom: 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--ink08);
}
/* ══ THE RAIL'S ONE GRID (#354, 18 Aug 2026) ═════════════════════════════════
   Mark: "we need a lot better way of showing the click and hover buttons and
   the timing" — then, of the first pass: "make sure things are aligned and
   lined up. just looks messy."

   So the alignment is a GRID, not taste: every row in a Page Tour stop card —
   the head included — is the same three columns: content · labelled time chip ·
   tail. The chips (ARRIVE / WAIT / HOLD + value) form one exact column down
   the rail; the tail is the same fixed width whether it holds the head's
   controls or a row's hover tools, so the chips never shift. Scoped to
   #tour-sections: Site Flow and Page Clips keep the old dress until their own
   rounds (the proposal doc), then inherit these classes. */
#tour-sections .tour-section,
#page-clips-sections .tour-section,
#tour-multi-pages .tour-section,
#manrec-markers .tour-section { --tour-chipw: 80px; --tour-tailw: 46px; }
/* ==== PAGE TOUR ROWS — the row's width, spent on the row ====
   Mark: "lets use the width better so that there is more room and less wasted
   space." The name is the only part of a row that can be long and it was the
   only part being squeezed: a fixed 46px tail column for a single × that is
   invisible until you hover, plus a 74px stepper for a number that is almost
   always one digit. Between them they were taking ~40px of a ~130px lane.
   Both are sized to their contents here, and the name gets what is left. */
#tour-sections .tour-section.is-frame,
#page-clips-sections .tour-section.is-frame,
#tour-multi-pages .tour-section.is-frame,
#manrec-markers .tour-section.is-frame { --tour-chipw: 66px; --tour-tailw: 20px; }
#tour-sections .tour-section.is-frame .tour-evrow,
#page-clips-sections .tour-section.is-frame .tour-evrow,
#tour-multi-pages .tour-section.is-frame .tour-evrow,
#manrec-markers .tour-section.is-frame .tour-evrow { column-gap: 5px; }
#tour-sections .is-frame .tour-tchip.is-step .wait-inp,
#page-clips-sections .is-frame .tour-tchip.is-step .wait-inp,
#tour-multi-pages .is-frame .tour-tchip.is-step .wait-inp,
#manrec-markers .is-frame .tour-tchip.is-step .wait-inp { width: 26px; }
#tour-sections .is-frame .row-step,
#page-clips-sections .is-frame .row-step,
#tour-multi-pages .is-frame .row-step,
#manrec-markers .is-frame .row-step { width: 17px; }
/* (The row number's 9px squeeze has gone. It was written when the number was a
   grey glyph worth almost nothing — it is the beat's identity now, the name every
   mark on the page carries, so it gets a disc it can be read in. The width work
   this belonged to bought the NAME 74px; giving 9 of them back is a good trade.
   Size lives with the disc, at the PAGE TOUR ROWS fence.) */
#tour-sections .is-frame .tour-ev-main,
#page-clips-sections .is-frame .tour-ev-main,
#tour-multi-pages .is-frame .tour-ev-main,
#manrec-markers .is-frame .tour-ev-main { gap: 6px; }
/* The tag is a target you press to flip click↔hover, so it keeps its pill —
   but the pill was padded like a standalone button, not like a label inside a
   row three of them deep. */
#tour-sections .is-frame .tour-ev-tag,
#page-clips-sections .is-frame .tour-ev-tag,
#tour-multi-pages .is-frame .tour-ev-tag,
#manrec-markers .is-frame .tour-ev-tag { padding: 0 7px; gap: 4px; }
/* And the name takes every pixel the three of them gave back. */
#tour-sections .is-frame .tour-ev-what,
#page-clips-sections .is-frame .tour-ev-what,
#tour-multi-pages .is-frame .tour-ev-what,
#manrec-markers .is-frame .tour-ev-what { flex: 1; }
#tour-sections .is-rowonly .tour-section-head,
#tour-sections .tour-evrow,
#page-clips-sections .is-rowonly .tour-section-head,
#page-clips-sections .tour-evrow,
#tour-multi-pages .is-rowonly .tour-section-head,
#tour-multi-pages .tour-evrow,
#manrec-markers .is-rowonly .tour-section-head,
#manrec-markers .tour-evrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--tour-chipw) var(--tour-tailw);
  align-items: center;
  column-gap: 7px;
}
#tour-sections .is-rowonly .tour-section-head,
#page-clips-sections .is-rowonly .tour-section-head,
#tour-multi-pages .is-rowonly .tour-section-head,
#manrec-markers .is-rowonly .tour-section-head {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
  min-height: 30px;
}
#tour-sections .tour-head-main,
#page-clips-sections .tour-head-main,
#tour-multi-pages .tour-head-main,
#manrec-markers .tour-head-main { display: flex; align-items: center; min-width: 0; gap: 8px; }
#tour-sections .tour-head-main .tour-section-label-input,
#page-clips-sections .tour-head-main .tour-section-label-input,
#tour-multi-pages .tour-head-main .tour-section-label-input,
#manrec-markers .tour-head-main .tour-section-label-input { flex: 1; min-width: 0; }
#tour-sections .tour-head-tail,
#tour-sections .tour-ev-tools,
#page-clips-sections .tour-head-tail,
#page-clips-sections .tour-ev-tools,
#tour-multi-pages .tour-head-tail,
#tour-multi-pages .tour-ev-tools,
#manrec-markers .tour-head-tail,
#manrec-markers .tour-ev-tools { display: flex; align-items: center; justify-content: flex-end; gap: 2px; }
/* The rows join the card under a hairline, same rhythm as the head. */
#tour-sections .tour-evbody,
#page-clips-sections .tour-evbody,
#tour-multi-pages .tour-evbody,
#manrec-markers .tour-evbody { border-top: 1px solid rgba(14,14,12,.06); margin-top: 10px; }
#tour-sections .tour-evrow,
#page-clips-sections .tour-evrow,
#tour-multi-pages .tour-evrow,
#manrec-markers .tour-evrow { min-height: 32px; padding: 2px 0; }
#tour-sections .tour-evrow + .tour-evrow,
#page-clips-sections .tour-evrow + .tour-evrow,
#tour-multi-pages .tour-evrow + .tour-evrow,
#manrec-markers .tour-evrow + .tour-evrow { border-top: 1px solid rgba(14,14,12,.06); }
#tour-sections .tour-ev-main,
#page-clips-sections .tour-ev-main,
#tour-multi-pages .tour-ev-main,
#manrec-markers .tour-ev-main { display: flex; align-items: center; min-width: 0; gap: 7px; }
#tour-sections .tour-ev-n,
#page-clips-sections .tour-ev-n,
#tour-multi-pages .tour-ev-n,
#manrec-markers .tour-ev-n { flex: none; width: 12px; font-family: var(--mono); font-size: 9.5px; color: rgba(14,14,12,.4); text-align: center; }
#tour-sections .tour-ev-tag,
#page-clips-sections .tour-ev-tag,
#tour-multi-pages .tour-ev-tag,
#manrec-markers .tour-ev-tag {
  flex: none; display: inline-flex; align-items: center; gap: 5px;
  height: 21px; padding: 0 8px;
  font-family: var(--sans); font-size: 10.5px; color: var(--ink65);
  background: transparent; border: 1px solid var(--bmed); border-radius: 100px; cursor: pointer;
}
#tour-sections .tour-ev-tag:hover,
#page-clips-sections .tour-ev-tag:hover,
#tour-multi-pages .tour-ev-tag:hover,
#manrec-markers .tour-ev-tag:hover { border-color: var(--ink); color: var(--ink); }
#tour-sections .tour-ev-what,
#page-clips-sections .tour-ev-what,
#tour-multi-pages .tour-ev-what,
#manrec-markers .tour-ev-what {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: inherit; font-size: 11.5px; color: var(--ink); text-align: left;
  background: transparent; border: 0; padding: 0; cursor: pointer;
}
#tour-sections .tour-ev-what:hover,
#page-clips-sections .tour-ev-what:hover,
#tour-multi-pages .tour-ev-what:hover,
#manrec-markers .tour-ev-what:hover { text-decoration: underline; text-underline-offset: 2px; text-decoration-color: rgba(14,14,12,.3); }
#tour-sections .tour-ev-what.is-prompt,
#page-clips-sections .tour-ev-what.is-prompt,
#tour-multi-pages .tour-ev-what.is-prompt,
#manrec-markers .tour-ev-what.is-prompt { color: var(--ink50); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: rgba(14,14,12,.3); }
#tour-sections .tour-ev-quiet,
#page-clips-sections .tour-ev-quiet,
#tour-multi-pages .tour-ev-quiet,
#manrec-markers .tour-ev-quiet { font-size: 11px; color: var(--ink50); }
/* The labelled time chip — the whole point of the column. One fixed pill:
   quiet mono label left, value right, the same in the head and in every row. */
#tour-sections .tour-tchip,
#page-clips-sections .tour-tchip,
#tour-multi-pages .tour-tchip,
#manrec-markers .tour-tchip {
  display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px;
  height: 22px; padding: 0 6px 0 7px; box-sizing: border-box;
  border: 1px solid var(--bmed); border-radius: 7px; cursor: text;
}
#tour-sections .tour-tchip:hover,
#page-clips-sections .tour-tchip:hover,
#tour-multi-pages .tour-tchip:hover,
#manrec-markers .tour-tchip:hover { border-color: var(--ink50); }
#tour-sections .tour-tchip:focus-within,
#page-clips-sections .tour-tchip:focus-within,
#tour-multi-pages .tour-tchip:focus-within,
#manrec-markers .tour-tchip:focus-within { border-color: var(--ink); background: var(--paper); }
#tour-sections .tour-tchip i,
#page-clips-sections .tour-tchip i,
#tour-multi-pages .tour-tchip i,
#manrec-markers .tour-tchip i { font-style: normal; font-family: var(--mono); font-size: 8px; letter-spacing: .08em; color: rgba(14,14,12,.4); margin-right: auto; }
#tour-sections .tour-tchip em,
#page-clips-sections .tour-tchip em,
#tour-multi-pages .tour-tchip em,
#manrec-markers .tour-tchip em { font-style: normal; font-family: var(--mono); font-size: 10px; color: var(--ink50); }
#tour-sections .tour-tchip input,
#page-clips-sections .tour-tchip input,
#tour-multi-pages .tour-tchip input,
#manrec-markers .tour-tchip input {
  width: 24px; height: 18px; padding: 0; border: 0; background: transparent;
  font-family: var(--mono); font-size: 10.5px; text-align: right; color: var(--ink65);
  -moz-appearance: textfield;
}
#tour-sections .tour-tchip input::-webkit-outer-spin-button,
#tour-sections .tour-tchip input::-webkit-inner-spin-button,
#page-clips-sections .tour-tchip input::-webkit-outer-spin-button,
#page-clips-sections .tour-tchip input::-webkit-inner-spin-button,
#tour-multi-pages .tour-tchip input::-webkit-outer-spin-button,
#tour-multi-pages .tour-tchip input::-webkit-inner-spin-button,
#manrec-markers .tour-tchip input::-webkit-outer-spin-button,
#manrec-markers .tour-tchip input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#tour-sections .tour-tchip input:focus,
#page-clips-sections .tour-tchip input:focus,
#tour-multi-pages .tour-tchip input:focus,
#manrec-markers .tour-tchip input:focus { color: var(--ink); outline: none; }
#tour-sections .tour-tchip.is-zero,
#page-clips-sections .tour-tchip.is-zero,
#tour-multi-pages .tour-tchip.is-zero,
#manrec-markers .tour-tchip.is-zero { border-style: dashed; }
#tour-sections .tour-tchip.is-zero input, #tour-sections .tour-tchip.is-zero em, #page-clips-sections .tour-tchip.is-zero input, #page-clips-sections .tour-tchip.is-zero em, #tour-multi-pages .tour-tchip.is-zero input, #tour-multi-pages .tour-tchip.is-zero em, #manrec-markers .tour-tchip.is-zero input, #manrec-markers .tour-tchip.is-zero em { color: rgba(14,14,12,.4); }
#tour-sections .tour-tchip.is-none,
#page-clips-sections .tour-tchip.is-none,
#tour-multi-pages .tour-tchip.is-none,
#manrec-markers .tour-tchip.is-none { border-color: transparent; justify-content: center; font-family: var(--mono); font-size: 10.5px; color: rgba(14,14,12,.4); cursor: default; }
/* Quiet adders on their own line; the mono total on ITS own line, right under
   the chip column it sums (the mockup's .is-add / .is-sum rows — together on
   one row they collided at rail width). */
#tour-sections .tour-ev-add,
#page-clips-sections .tour-ev-add,
#tour-multi-pages .tour-ev-add,
#manrec-markers .tour-ev-add { font: inherit; font-size: 11px; color: var(--ink50); background: transparent; border: 0; padding: 0 12px 0 0; cursor: pointer; white-space: nowrap; }
#tour-sections .tour-ev-add:hover,
#page-clips-sections .tour-ev-add:hover,
#tour-multi-pages .tour-ev-add:hover,
#manrec-markers .tour-ev-add:hover { color: var(--ink); }
#tour-sections .tour-evrow.is-addrow,
#page-clips-sections .tour-evrow.is-addrow,
#tour-multi-pages .tour-evrow.is-addrow,
#manrec-markers .tour-evrow.is-addrow { min-height: 28px; }
#tour-sections .tour-evrow.is-sumrow,
#page-clips-sections .tour-evrow.is-sumrow,
#tour-multi-pages .tour-evrow.is-sumrow,
#manrec-markers .tour-evrow.is-sumrow { min-height: 22px; border-top: 0; padding-top: 0; }
#tour-sections .tour-ev-sum,
#page-clips-sections .tour-ev-sum,
#tour-multi-pages .tour-ev-sum,
#manrec-markers .tour-ev-sum { font-family: var(--mono); font-size: 8.5px; letter-spacing: .07em; text-transform: uppercase; color: rgba(14,14,12,.4); text-align: right; white-space: nowrap; }
#tour-sections .tour-ev-sum b,
#page-clips-sections .tour-ev-sum b,
#tour-multi-pages .tour-ev-sum b,
#manrec-markers .tour-ev-sum b { font-weight: 400; color: var(--ink65); }
/* The row number IS the drag grip when there's an order to change. */
#tour-sections .tour-ev-n.act-grip,
#page-clips-sections .tour-ev-n.act-grip,
#tour-multi-pages .tour-ev-n.act-grip,
#manrec-markers .tour-ev-n.act-grip { padding: 0; justify-content: center; font-family: var(--mono); font-size: 9.5px; color: rgba(14,14,12,.4); }
#tour-sections .tour-ev-n.act-grip:hover,
#page-clips-sections .tour-ev-n.act-grip:hover,
#tour-multi-pages .tour-ev-n.act-grip:hover,
#manrec-markers .tour-ev-n.act-grip:hover { color: var(--ink); }
#tour-sections .tour-evrow.is-dragging,
#page-clips-sections .tour-evrow.is-dragging,
#tour-multi-pages .tour-evrow.is-dragging,
#manrec-markers .tour-evrow.is-dragging { opacity: 0.45; }
#tour-sections .tour-evrow.is-drop-target,
#page-clips-sections .tour-evrow.is-drop-target,
#tour-multi-pages .tour-evrow.is-drop-target,
#manrec-markers .tour-evrow.is-drop-target { box-shadow: 0 0 0 1.5px var(--ink) inset; }
/* Tools show on their row's hover — the resting card is sentences and times. */
#tour-sections .tour-ev-tools,
#page-clips-sections .tour-ev-tools,
#tour-multi-pages .tour-ev-tools,
#manrec-markers .tour-ev-tools { opacity: 0; transition: opacity .12s ease; }
#tour-sections .tour-evrow:hover .tour-ev-tools,
#tour-sections .tour-ev-tools:focus-within,
#page-clips-sections .tour-evrow:hover .tour-ev-tools,
#page-clips-sections .tour-ev-tools:focus-within,
#tour-multi-pages .tour-evrow:hover .tour-ev-tools,
#tour-multi-pages .tour-ev-tools:focus-within,
#manrec-markers .tour-evrow:hover .tour-ev-tools,
#manrec-markers .tour-ev-tools:focus-within { opacity: 1; }
/* The warnings keep their classes; on the grid they span the card. */
#tour-sections .tour-interaction-warning,
#tour-sections .tour-interaction-note,
#page-clips-sections .tour-interaction-warning,
#page-clips-sections .tour-interaction-note,
#tour-multi-pages .tour-interaction-warning,
#tour-multi-pages .tour-interaction-note,
#manrec-markers .tour-interaction-warning,
#manrec-markers .tour-interaction-note { margin: 2px 0 6px; }
/* When the card is collapsed there's no body below the head, so the
   underline + spacing would leave the row sitting visually high.
   :last-child strips them so the head centres cleanly in the card. */
.tour-section-head:last-child {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
}
.tour-section-num {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.04em; /* match the options-dropdown mono everywhere (Mark) */
  text-transform: uppercase;
  color: var(--ink50);
  flex: none;
  /* Sits in the card head before the stop title — a little breathing room. */
  margin-right: 6px;
}
.tour-section-label {
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 400;
  color: var(--ink);
  margin-left: 14px;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Editable title shown in the head when a stop/marker/clip is being
   edited. A light editable title, not a filled box: reads as the title
   text, with only a faint fill on hover/focus to signal it's editable.
   margin-left + padding-left sum to the static label's 14px so the
   text doesn't jump when editing starts. */
.tour-section-label-input {
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 400;
  color: var(--ink);
  margin-left: 8px;
  margin-right: 12px;
  flex: 1;
  min-width: 0;
  background: transparent;
  border: 0;
  border-radius: 6px;
  padding: 2px 6px;
  outline: none;
  transition: background .12s ease;
}
.tour-section-label-input:hover,
.tour-section-label-input:focus { background: var(--ink08); }
.tour-section-label-input::placeholder {
  color: var(--ink50);
}

/* ---------- Journey layout: shared rail + filled card ----------
   Single Page Tour, Multi Page Tour and Area Capture share the same
   visual shell: a narrow number rail on the left and a filled pill
   card on the right. The tours add a connector line in the rail so
   sections read as a sequence Capt moves through; Area Capture omits
   the connector because each crop region is independent. Scoped to
   the three containers so other section lists are unaffected. */
#tour-sections,
#tour-multi-pages .tour-group-sections,
#iso-sections,
#page-clips-sections,
#manrec-markers .tour-group-sections ,
#page-clips-sections{
  /* Tighter sequence: cards sit closer with a short connector that FILLS the
     gap (was 20px with a 20px line — the line now matches at 10px). Was over-
     specific (#id) so it silently overrode the .tour-sections base (Mark). */
  gap: 10px;
}
#tour-sections > .tour-section,
#tour-multi-pages .tour-group-sections > .tour-section,
#iso-sections > .tour-section,
#page-clips-sections > .tour-section,
#manrec-markers .tour-group-sections > .tour-section ,
#page-clips-sections > .tour-section{
  /* Full-width card, no left number rail — the order number now lives
     inside the card head, so cards (and the Add stop button) all align to
     the same left edge. */
  display: block;
  position: relative;
  border: 0;
  padding: 0;
}
/* Sequence connector — a short centred line in the 20px gap between cards
   so stops read as a top-down sequence running down the middle. Page Tour,
   Site Flow and Page Clips only; Area Capture's crops are independent. */
#tour-sections > .tour-section:not(:last-child)::after,
#tour-multi-pages .tour-group-sections > .tour-section:not(:last-child)::after,
#page-clips-sections > .tour-section:not(:last-child)::after,
#page-clips-sections > .tour-section:not(:last-child)::after,
#manrec-markers .tour-group-sections > .tour-section:not(:last-child)::after{
  content: '';
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: -10px;
  height: 10px;
  width: 1px;
  background: var(--bmed);
  z-index: 0;
}
/* Page Tour: the chain line runs through every gap. The + insert
   (see .tour-stop-insert) carries its own guide line, so suppress the
   passive connector only on the card directly above an insert —
   everywhere else the connector threads frame to frame. The last
   .tour-section isn't :last-child when a trailing + follows it, so
   the :has() guard covers that case too. */
#tour-sections > .tour-section:has(+ .tour-stop-insert)::after,
#tour-sections > .tour-section:has(+ .tour-travel)::after,
#page-clips-sections > .tour-section:has(+ .tour-stop-insert)::after,
#page-clips-sections > .tour-section:has(+ .tour-travel)::after,
#tour-multi-pages > .tour-section:has(+ .tour-stop-insert)::after,
#tour-multi-pages > .tour-section:has(+ .tour-travel)::after,
#manrec-markers > .tour-section:has(+ .tour-stop-insert)::after,
#manrec-markers > .tour-section:has(+ .tour-travel)::after { content: none; }

/* Travel divider — the gap between frames carries a tiny label naming
   the motion ("scrolls · medium"). The chain line is drawn in two
   halves (::before above, ::after below) with a break around the
   label, so no background patch is needed. */
#tour-sections .tour-travel,
#page-clips-sections .tour-travel,
#tour-multi-pages .tour-travel,
#manrec-markers .tour-travel {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 16px;
}
#tour-sections .tour-travel::before,
#tour-sections .tour-travel::after,
#page-clips-sections .tour-travel::before,
#page-clips-sections .tour-travel::after,
#tour-multi-pages .tour-travel::before,
#tour-multi-pages .tour-travel::after,
#manrec-markers .tour-travel::before,
#manrec-markers .tour-travel::after {
  content: '';
  position: absolute;
  left: 50%;
  width: 1px;
  background: var(--bmed);
  transform: translateX(-50%);
}
#tour-sections .tour-travel::before,
#page-clips-sections .tour-travel::before,
#tour-multi-pages .tour-travel::before,
#manrec-markers .tour-travel::before { top: -10px; bottom: calc(50% + 9px); }
#tour-sections .tour-travel::after,
#page-clips-sections .tour-travel::after,
#tour-multi-pages .tour-travel::after,
#manrec-markers .tour-travel::after  { top: calc(50% + 9px); bottom: -10px; }
#tour-sections .tour-travel-label,
#page-clips-sections .tour-travel-label,
#tour-multi-pages .tour-travel-label,
#manrec-markers .tour-travel-label {
  position: relative;
  z-index: 1;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink50);
  white-space: nowrap;
  /* Pivot the label on the rail: the · is the centre column, sitting
     exactly on the line; the words flank it symmetrically. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  width: 100%;
}
#tour-sections .tour-travel-side.is-left,
#page-clips-sections .tour-travel-side.is-left,
#tour-multi-pages .tour-travel-side.is-left,
#manrec-markers .tour-travel-side.is-left  { text-align: right; padding-right: 7px; }
#tour-sections .tour-travel-side.is-right,
#page-clips-sections .tour-travel-side.is-right,
#tour-multi-pages .tour-travel-side.is-right,
#manrec-markers .tour-travel-side.is-right { text-align: left;  padding-left: 7px; }
#tour-sections .tour-travel-dot,
#page-clips-sections .tour-travel-dot,
#tour-multi-pages .tour-travel-dot,
#manrec-markers .tour-travel-dot { text-align: center; }

/* + affordance between stops. A small round button on a centred guide
   line; clicking it drops an in-between stop at that position. */
.tour-stop-insert {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
}
.tour-stop-insert::before {
  content: '';
  position: absolute;
  top: -14px;
  bottom: -14px;
  left: 50%;
  width: 1px;
  background: var(--bmed);
  transform: translateX(-50%);
  z-index: 0;
}
/* Trailing + (no card below it) — the guide line runs only ABOVE the
   icon and ends on it, rather than dangling into empty space. */
.tour-stop-insert.is-trailing::before { bottom: 50%; }
/* The insert affordance is now a dashed "+ Add pause" CARD (consistent with
   Page Clips' "+ Add clip"), not a circular + on the spine — so drop the
   vertical guide line (it would cross the card) and let the button fill the
   row (Mark). The dashed-card look comes from .ghost-btn.is-add (full-width in
   the rail). */
.tour-stop-insert::before { display: none; }
.tour-stop-insert { min-height: 0; }
#previewPanel.is-tour-sequence .tour-stop-insert { display: block; }
/* Matches Site Flow's .tour-flow-end-plus exactly. The + is drawn as a
   geometric ::before/::after cross (font-size:0) so it sits dead-centre
   regardless of font metrics. */
.tour-stop-insert-btn {
  position: relative;
  z-index: 1;
  width: 34px;
  height: 34px;
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  border-radius: 100px;
  border: 1px solid var(--bmed);
  background: var(--card-fill);
  color: var(--ink65);
  font-size: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.tour-stop-insert-btn::before,
.tour-stop-insert-btn::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  background: currentColor;
}
.tour-stop-insert-btn::before { width: 12px; height: 1.5px; transform: translate(-50%, -50%); }
.tour-stop-insert-btn::after  { width: 1.5px; height: 12px; transform: translate(-50%, -50%); }
/* Hover tint stacked over an OPAQUE base — the old translucent ink08
   fill let the guide line show through the circle on hover. */
.tour-stop-insert-btn:hover {
  background: linear-gradient(var(--ink08), var(--ink08)), var(--card-fill);
  color: var(--ink);
}
#tour-sections .tour-section-card,
#tour-multi-pages .tour-section-card,
#iso-sections .tour-section-card,
#page-clips-sections .tour-section-card,
#manrec-markers .tour-section-card ,
#page-clips-sections .tour-section-card{
  /* Filled stop — white card so each stop reads as a clean bead on the
     connector string (sits on the panel/page-card grey). */
  background: var(--card-fill);
  border: 0;
  border-radius: 30px;
  padding: 18px 24px;
  min-width: 0;
}
/* Body divider — when the card is filled instead of bordered, the
   ink08 hairline reads too softly against the fill. Bump to a slightly
   stronger value so the head/body break remains legible. */
#tour-sections .tour-section-card .tour-section-body,
#tour-sections .tour-section-card .tour-section-interactions,
#tour-multi-pages .tour-section-card .tour-section-body,
#tour-multi-pages .tour-section-card .tour-section-interactions,
#iso-sections .tour-section-card .tour-section-body,
#iso-sections .tour-section-card .tour-section-interactions,
#page-clips-sections .tour-section-card .tour-section-body,
#page-clips-sections .tour-section-card .tour-section-interactions,
#manrec-markers .tour-section-card .tour-section-body,
#manrec-markers .tour-section-card .tour-section-interactions ,
#page-clips-sections .tour-section-card .tour-section-body,
#page-clips-sections .tour-section-card .tour-section-interactions{
  border-top-color: var(--ink18);
}
/* Page Tour storyboard: Click/Hover action cards ride an indented
   sub-rail UNDER their frame card (Start / Pause / End), so each
   action reads as happening within that frame's view. Layout only —
   the mini cards reuse the standard filled-card treatment above. */
#tour-sections .tour-action-sub,
#tour-multi-pages .tour-action-sub,
#page-clips-sections .tour-action-sub,
#iso-sections .tour-action-sub,
#page-clips-sections .tour-action-sub,
#manrec-markers .tour-action-sub{
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
  margin-left: 56px;
}
#tour-sections .tour-action-sub .tour-section-card,
#tour-multi-pages .tour-action-sub .tour-section-card,
#page-clips-sections .tour-action-sub .tour-section-card,
#iso-sections .tour-action-sub .tour-section-card,
#page-clips-sections .tour-action-sub .tour-section-card,
#manrec-markers .tour-action-sub .tour-section-card{
  padding: 12px 20px;
}
/* Page Clips: the action cards now live INSIDE the clip card so the clip
   and its actions read as one unit (Mark), not a sub-rail floating below.
   Drop the outside 56px indent; the clip card's own padding provides the
   inset. No divider line — Mark wants the card clean. */
#page-clips-sections .tour-section > .tour-section-card > .tour-action-sub {
  margin-left: 0;
  margin-top: 10px;
}
/* Page Clips: clean card — no head/body divider and no between-clip
   connector line (Mark: "2 random lines… remove these" + "remove the
   connector line between the clips"). */
#page-clips-sections .tour-section-card .tour-section-body { border-top: 0; }
#page-clips-sections > .tour-section:not(:last-child)::after { content: none; }
#tour-sections .tour-action-sub .tour-action-card .tour-interaction-row,
#tour-multi-pages .tour-action-sub .tour-action-card .tour-interaction-row,
#page-clips-sections .tour-action-sub .tour-action-card .tour-interaction-row,
#iso-sections .tour-action-sub .tour-action-card .tour-interaction-row,
#page-clips-sections .tour-action-sub .tour-action-card .tour-interaction-row,
#manrec-markers .tour-action-sub .tour-action-card .tour-interaction-row{
  padding: 0;
}
/* Expanded frame body — uniform two-column rows: a fixed mono label
   column (POSITION / FRAMING / HOLD / ACTIONS) with the control beside
   it, so every setting reads on the same grid. flex-wrap + min-width
   lets the control drop below its label on narrow widths instead of
   squeezing. */
.tour-field-row {
  display: flex;
  /* Labels centre against their control — single-line rows (Hold,
     Actions) and the taller Position readout both read aligned. */
  align-items: center;
  gap: 6px 14px;
  flex-wrap: wrap;
  margin-top: 16px;
}
.tour-section-body > .tour-field-row:first-child { margin-top: 0; }
/* CARD FONT SYSTEM (Mark: "mono is not for titles, only meta").
   Titles/field labels + inline qualifiers + values = SANS.
   Mono is reserved for META: page numbering, URLs/paths, selectors. */
.tour-field-label {
  font-family: var(--sans);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink65);
  flex: 0 0 64px;
}
.tour-field-control {
  flex: 1;
  min-width: 260px;
}
/* Controls reused inside the rows carry their own top margins from
   their standalone life — neutralise them on the grid. */
.tour-field-row .stop-timing,
.tour-field-row .tour-actions-add { margin-top: 0; }
.tour-field-control .position-methods { margin-top: 0; }
/* Actions row — the control carries its own flex now that it no
   longer rides .stop-timing (which is what the buttons' gap came
   from). */
#tour-sections .tour-actions-add,
#tour-multi-pages .tour-actions-add,
#page-clips-sections .tour-actions-add,
#iso-sections .tour-actions-add,
#page-clips-sections .tour-actions-add,
#manrec-markers .tour-actions-add{
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Footer row under the Page Tour storyboard — route readout left,
   quiet utilities right. One balanced line; wraps on narrow widths. */
.tour-list-footer {
  display: flex;
  align-items: center;
  gap: 8px 14px;
  flex-wrap: wrap;
  margin-top: 18px;
}
.tour-list-footer .page-range-summary {
  flex: 1;
  min-width: 220px;
  margin: 0;
}

/* "n actions" chip in a collapsed frame head — folds/unfolds the
   action sub-rail. Reads like the summary text, behaves like a quiet
   button; the chevron flips sideways when folded. */
.tour-actions-toggle {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink50);
  background: transparent;
  border: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 6px;
  border-radius: 6px;
  cursor: pointer;
  flex: none;
  margin-right: 10px;
  transition: color .12s ease, background .12s ease;
}
.tour-actions-toggle:hover { color: var(--ink); background: var(--ink08); }
.tour-actions-toggle svg { transition: transform .12s ease; }
.tour-actions-toggle.is-folded svg { transform: rotate(-90deg); }
/* Inline hold strip in a collapsed frame head — same controls as the
   expanded body's .stop-timing, minus the top gap, sitting compactly
   where the static time summary used to read. */
#tour-sections .tour-section-head .stop-timing-inline,
#tour-multi-pages .tour-section-head .stop-timing-inline,
#page-clips-sections .tour-section-head .stop-timing-inline,
#page-clips-sections .tour-section-head .stop-timing-inline,
#manrec-markers .tour-section-head .stop-timing-inline{
  margin-top: 0;
  flex: none;
  flex-wrap: nowrap;
  margin-right: 14px;
}
/* Number, kept quieter than the workflow step numerals (28px ink) so
   the user reads "01 / 02 / 03" as section indices rather than step
   labels. Small bg behind so the connector line passes cleanly. */
#tour-sections .tour-section-num,
#tour-multi-pages .tour-section-num,
#iso-sections .tour-section-num,
#page-clips-sections .tour-section-num,
#manrec-markers .tour-section-num ,
#page-clips-sections .tour-section-num{
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em; /* match the options-dropdown mono (Mark) — was 0.12em */
  text-transform: none;
  color: var(--ink50);
  /* No background patch — the number now sits inside the card head, so it
     should read as part of the card, not a separate badge. (The old
     var(--bg) patch was masking the rail connector, which is gone.) */
  background: transparent;
  padding: 0;
}
/* Two-line stacked variant — "Stop" / "01" sits in the rail without
   crowding the card. Used in Site Flow page cards where the prefix
   keeps the vocabulary explicit. */
.tour-section-num-stack {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  line-height: 1.05;
}
.tour-section-num-stack .tour-section-num-prefix,
.tour-section-num-stack .tour-section-num-value {
  display: block;
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 0.04em; /* match the options-dropdown mono (Mark) — was 0.1em */
  color: inherit;
}
.tour-section-num-stack .tour-section-num-prefix {
  text-transform: none;
  opacity: 0.8;
}
/* Label sits flush left in the card head — no margin-left override
   since the number is no longer the first sibling. */
#tour-sections .tour-section-label,
#tour-multi-pages .tour-section-label,
#iso-sections .tour-section-label,
#page-clips-sections .tour-section-label,
#tour-sections .tour-section-label-input,
#tour-multi-pages .tour-section-label-input,
#iso-sections .tour-section-label-input,
#page-clips-sections .tour-section-label-input,
#manrec-markers .tour-section-label,
#manrec-markers .tour-section-label-input ,
#page-clips-sections .tour-section-label,
#page-clips-sections .tour-section-label-input{
  margin-left: 0;
}
/* Page Clips: dim a skipped card so the user can tell at a glance
   which clips will run on the next capture vs which will be skipped. */
#page-clips-sections .tour-section-card.is-skipped {
  opacity: 0.55;
}
#page-clips-sections .tour-section-card.is-skipped:hover {
  opacity: 0.8;
}
/* Per-clip Included / Skipped checkbox + any future per-stop
   checkboxes — override Chrome's default blue accent with the
   app's dark ink so the checkmark matches the rest of the UI. */
#page-clips-sections input[type="checkbox"] {
  accent-color: var(--ink);
  cursor: pointer;
}
/* Per-page section list — flex column. Gap MUST match the connector
   geometry (the ::after line is 10px tall, bottom:-10px) or the line
   stops short of the next card (Mark: "connector doesn't connect"). */
#tour-multi-pages .tour-group-sections,
#manrec-markers .tour-group-sections {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
}
/* Manual Recording markers have NO connector line (just stacked cards), so
   the 20px sequence gap reads as too much air — tighten it (Mark). MUST come
   after the shared rule above or it loses (same specificity). */
#manrec-markers .tour-group-sections { gap: 4px; }
.tour-section-summary {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink50);
  margin-right: 14px;
  flex: none;
}
.tour-section-edit {
  flex: none;
  margin-right: 6px;
}
/* Edit / Done is the key affordance for opening a stop's settings, so it
   stays a solid (filled) secondary button rather than a quiet text one. */
.tour-edit-btn {
  flex: none;
  margin-right: 6px;
  font-size: 12px;
  letter-spacing: 0.01em;
}
.tour-section-remove {
  margin-left: auto;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink50);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1;
  padding: 0;
  /* The × glyph sits below its line centre, so flex-centring leaves it
     looking low next to the row's text. Lift it onto the optical line. */
  transform: translateY(-2px);
  border: 0;
  background: transparent;
  border-radius: 50%;
  transition: color .12s, background .12s;
  flex: none;
}
.tour-section-remove:hover {
  color: var(--ink);
  background: var(--pill);
}

/* Expanded section editing area */
.tour-section-body {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--ink08);
}
/* Stop/clip/area cards: no internal dividers — whitespace groups the
   parts instead. Keep the top gap, drop the hairlines. */
#tour-sections .tour-section-body,
#tour-sections .tour-section-interactions,
#tour-multi-pages .tour-section-body,
#tour-multi-pages .tour-section-interactions,
#page-clips-sections .tour-section-body,
#page-clips-sections .tour-section-interactions,
#iso-sections .tour-section-body,
#iso-sections .tour-section-interactions,
#manrec-markers .tour-section-body,
#manrec-markers .tour-section-interactions ,
#page-clips-sections .tour-section-body,
#page-clips-sections .tour-section-interactions{
  border-top: 0;
  padding-top: 0;
}
/* More breathing room between the parts now that the dividers are gone. */
#tour-sections .tour-section-body,
#tour-multi-pages .tour-section-body,
#page-clips-sections .tour-section-body,
#iso-sections .tour-section-body,
#manrec-markers .tour-section-body ,
#page-clips-sections .tour-section-body{ margin-top: 18px; }
#tour-sections .tour-section-interactions,
#tour-multi-pages .tour-section-interactions,
#page-clips-sections .tour-section-interactions,
#iso-sections .tour-section-interactions,
#page-clips-sections .tour-section-interactions,
#manrec-markers .tour-section-interactions{ margin-top: 24px; }
/* Inline stop timing — folds Hold-before and Hold-after into one quiet
   line, prefixed with "Hold", e.g. "Hold 2.5 s before · 1 s after". */
.stop-timing { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 22px; }
.stop-timing-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink65);
  margin-right: 4px;
}
.stop-timing .num-inp-sm {
  width: 56px;
  padding: 6px 8px;
  text-align: center;
}
.stop-timing-unit { font-family: var(--sans); font-size: 11px; color: var(--ink50); }
.stop-timing-sep { color: var(--ink32); margin: 0 4px; }

/* Compact interaction list inside a section card */
.tour-interaction-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}
.tour-interaction-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  font-size: 12px;
  color: var(--ink65);
}
/* Soft advisory shown beneath an interaction row when the target
   sits outside its section's focus area. Same restrained treatment
   as the Preview's motion warning — left rule in ink, no chromatic
   accent. */
.tour-interaction-warning {
  margin: 2px 0 8px 28px;
  padding: 6px 0 6px 10px;
  border-left: 2px solid var(--ink);
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.tour-interaction-num {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink50);
  letter-spacing: 0.1em;
  flex: none;
  width: 22px;
}
.tour-interaction-type {
  font-family: var(--mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink);
  flex: none;
  width: 52px;
}
.tour-interaction-target {
  flex: 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink50);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tour-interaction-target-empty {
  color: var(--ink32);
}
.tour-interaction-actions {
  display: flex;
  gap: 6px;
  flex: none;
}
/* ══ HIDE ELEMENTS BEGIN ══════════════════════════════════════════════════
   ONE LINE PER ELEMENT: number · name over meta · eye · remove.

   The row does NOT wrap. It used to, and that was the bug Mark hit: with
   `flex: 1 1 auto` the name column's hypothetical size is its MAX-content
   width, and a wrapping flex line is filled from hypothetical sizes BEFORE
   shrinking is applied — so a 60-character selector inside the name pushed
   the eye onto its own line, then the pills onto a third, and one element
   ate four lines of a 250px rail. `flex: 1 1 0` fixes it at the root: the
   name column asks for nothing, takes what is left, and the row can no
   longer be made to wrap by its own contents. */
.clean-hide-row { flex-wrap: nowrap; align-items: flex-start; gap: 8px; }
.clean-hide-row .tour-interaction-num { padding-top: 2px; }
.clean-hide-name {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.clean-hide-title {
  font-family: var(--sans);   /* a VALUE, not meta (house rule) */
  font-size: 12.5px;
  color: var(--ink);
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* selector, then mode. It WRAPS, and that is the safety valve: only selectors
   that are real identifiers reach this line (renderCleanPageList drops the
   generated chains), so the right answer when the pair doesn't fit is to let
   the mode drop to its own line, never to eat a name the user can still read.
   Nothing here is allowed to shrink for the same reason. */
.clean-hide-meta {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 1px 8px;
  min-width: 0;
}
/* The selector IS metadata — mono and quiet. */
.clean-hide-sel {
  flex: 0 1 auto;
  min-width: 0;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink32);
  letter-spacing: 0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.clean-hide-dot { display: none; }   /* the gap separates them; a dot as well is one mark too many */
/* The mode is a chosen VALUE, so it is sans and darker than the selector it
   sits beside — that contrast is what says "this bit is yours to change".
   The dotted underline is the affordance; a border or a fill would put it
   back in competition with Capture, which is what the pills were doing. */
.clean-hide-mode {
  flex: none;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--sans);
  font-size: 11px;
  line-height: 1.4;
  color: var(--ink50);
  cursor: pointer;
  white-space: nowrap;
  border-bottom: 1px dotted var(--ink32);
  transition: color .12s ease, border-color .12s ease;
}
.clean-hide-mode:hover { color: var(--ink); border-bottom-color: var(--ink); }
.clean-hide-eye {
  flex: none;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink32);
  cursor: pointer;
  transition: color .12s ease, background-color .12s ease;
}
.clean-hide-eye svg { width: 15px; height: 15px; }
.clean-hide-eye:hover { color: var(--ink); background: var(--ink08); }
.clean-hide-eye[aria-pressed="true"] { color: var(--ink); background: var(--ink08); }
/* Pointing at a row brings its element back on the preview, outlined in honey.
   The row wears the SAME honey wash, so the two surfaces read as one gesture.
   No padding or margin here on purpose: #canvas-settings clips at overflow
   hidden with about 2px of slack, so a left rule drawn outside the row gets
   sliced off, and padding added on peek would shunt the row sideways under the
   cursor that caused it. The wash bleeds outward through the shadow spread
   instead, which costs no layout at all. */
.clean-hide-row.is-peek {
  background: rgba(247, 206, 70, .16);
  box-shadow: 0 0 0 2px rgba(247, 206, 70, .16);
  border-radius: 6px;
}
.clean-hide-row.is-peek .clean-hide-title { color: var(--ink); }
.clean-hide-row.is-peek .clean-hide-sel { color: var(--ink50); }
/* The remove × is a row control now that the row is one line — same quiet
   weight as the eye beside it, so the pair reads as one cluster. */
.clean-hide-row .tour-section-remove { flex: none; align-self: flex-start; }
.clean-hide-empty {
  margin: 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink50);
  max-width: 42ch;
}
/* ══ HIDE ELEMENTS END ════════════════════════════════════════════════════ */
/* One-line editable action row: number · Click/Hover · target
   (result-plus-change) · wait dropdown · remove. */
.tour-interaction-target-wrap {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.tour-interaction-target-name {
  font-family: var(--sans); /* a VALUE, not meta — matches .tf-name (Mark) */
  font-size: 12.5px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.tour-interaction-sep { color: var(--ink32); flex: none; }
.wait-inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  font-family: var(--sans); /* qualifier text, not meta — sans (Mark) */
  font-size: 11px;
  color: var(--ink50);
}
/* The wait stepper is the STANDARD .num-stepper — no custom shape or
   width (Mark: 'make exactly the same' as the Hold steppers). */

/* Page Clips action cell — stacks the main row (number / click-hover /
   target / actions) on top of a Hold-after strip. Pill-selects need a
   full-width row to lay out horizontally; jammed into the same flex
   row as the target input they wrap into a vertical column. */
.tour-interaction-cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 0;
  border-bottom: 1px solid var(--ink08);
}
.tour-interaction-cell:last-child {
  border-bottom: 0;
}
.tour-interaction-hold {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-left: 32px;
}
.tour-interaction-hold-label {
  font-family: var(--mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink50);
  flex: none;
}

/* Interactions area inside a section card */
.tour-section-interactions {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--ink08);
}
.tour-section-interactions-label {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
  margin-bottom: 8px;
}

/* The library has no subtitle, so give the title room before the
   folders / filter bar. */
[data-panel="history-all"] .title { margin-bottom: 30px; }

/* ---------- History grid + cards ---------- */
.history-filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
  margin-bottom: 24px;
}
.history-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.history-select {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--bmed);
  border-radius: 8px;
  padding: 6px 10px;
  cursor: pointer;
  max-width: 180px;
}
.history-select:hover { border-color: var(--ink); }
/* No blue browser focus ring — match the app's quiet ink-border feedback. */
.history-select:focus,
.history-select:focus-visible { outline: none; border-color: var(--ink); }

/* Custom dropdown for the library toolbars (Mark, 16 July 2026) — replaces the
   native selects, whose OS popups indent options + tick the current one. All
   items left-aligned, NO tick; the current item sits on a quiet grey wash. */
.lib-dd-wrap { position: relative; display: inline-flex; }
.lib-dd { display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--mono); font-size: 11px; color: var(--ink);
  background: transparent; border: 1px solid var(--bmed); border-radius: 8px;
  padding: 6px 10px; cursor: pointer; max-width: 180px; white-space: nowrap; }
.lib-dd:hover { border-color: var(--ink); }
.lib-dd-chev { flex: none; color: var(--ink32); }
.lib-dd-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 80;
  min-width: 160px; max-height: 60vh; overflow: auto; background: var(--paper);
  border: 1px solid var(--rule-faint); border-radius: 10px; padding: 5px;
  box-shadow: 0 10px 28px rgba(14, 14, 12, .16);
  display: flex; flex-direction: column; gap: 1px; }
.lib-dd-menu[hidden] { display: none; }
.lib-dd-sec { font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em;
  text-transform: none; color: var(--ink-mute); padding: 8px 10px 3px; font-weight: 500;}
.lib-dd-item { display: block; width: 100%; text-align: left;
  font-family: var(--sans); font-size: 12.5px; color: var(--ink);
  background: none; border: 0; border-radius: 7px; padding: 6px 10px;
  cursor: pointer; white-space: nowrap; }
.lib-dd-item:hover { background: var(--btn2bg); }
.lib-dd-item.is-cur { background: var(--btn2bg); }
/* ── Library control row: Date/Site · device chips · Stills/Videos · sort ── */
.lib-seg { display: inline-flex; border: 1px solid var(--bmed); border-radius: 8px; overflow: hidden; }
.lib-seg-btn {
  font-family: var(--sans); font-size: 12px; color: var(--ink-soft);
  background: transparent; border: 0; border-left: 1px solid var(--bmed);
  padding: 6px 11px; cursor: pointer; white-space: nowrap;
  transition: background .12s, color .12s;
}
.lib-seg-btn:first-child { border-left: 0; }
.lib-seg-btn:hover { color: var(--ink); }
.lib-seg-btn.is-on { background: var(--ink); color: var(--paper); }
/* (Mode / type filters use a dropdown — .history-select — not pills, since the
   list of modes gets long. Kept as a select for a compact, scalable control.) */
.lib-dev-filter { display: inline-flex; gap: 4px; }
.lib-dev {
  width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--bmed); border-radius: 8px; background: transparent; color: var(--ink-soft);
  cursor: pointer; transition: background .12s, color .12s, border-color .12s;
}
.lib-dev svg { width: 16px; height: 16px; }
.lib-dev:hover { color: var(--ink); border-color: var(--ink); }
.lib-dev.is-on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
/* No captures of this device in the current view — shown for consistency but inert. */
.lib-dev.is-empty { opacity: .3; cursor: default; }
.lib-dev.is-empty:hover { color: var(--ink-soft); border-color: var(--bmed); }
/* Favourites-only filter — a star chip matching the device chips. */
.lib-fav-filter {
  width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--bmed); border-radius: 8px; background: transparent; color: var(--ink-soft);
  cursor: pointer; transition: background .12s, color .12s, border-color .12s;
}
.lib-fav-filter svg { width: 15px; height: 15px; }
.lib-fav-filter:hover { color: var(--ink); border-color: var(--ink); }
.lib-fav-filter.is-on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
/* Active-filter chips — removable pills showing what's currently narrowing the view. */
.lib-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: -8px 0 18px; }
.lib-chip {
  display: inline-flex; align-items: center; gap: 5px; font-family: var(--sans); font-size: 12px;
  color: var(--ink); background: var(--ink08); border: 0; border-radius: 100px;
  padding: 4px 7px 4px 11px; cursor: pointer; transition: background .12s;
}
.lib-chip:hover { background: rgba(14, 14, 12, .12); }
.lib-chip-x { display: inline-flex; align-items: center; justify-content: center; width: 15px; height: 15px;
  border-radius: 50%; font-size: 13px; line-height: 1; color: var(--ink-soft); }
.lib-chip:hover .lib-chip-x { color: var(--ink); }

/* ---------- Library two-row header ----------
   Utility row (breadcrumb + exit) over a title row (name + count + filters).
   One left edge; the exit reads a touch more present than the breadcrumb. */
.lib-head-util {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-bottom: 14px;
  /* Breadcrumb on the left, the search box right-aligned — the search rides this
     row (not the title row) so it stays clear of the site name below. (Mark) */
}
.lib-breadcrumb { display: flex; align-items: center; gap: 7px; min-width: 0; }
.lib-crumb-link {
  border: 0; background: transparent; padding: 0; cursor: pointer;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink50);
}
.lib-crumb-link:hover { color: var(--ink); }
.lib-crumb-sep { font-family: var(--mono); font-size: 11px; color: var(--ink32); }
.lib-crumb-here {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
/* Library section tabs — replace the old two-item sidebar rail now the library
   is full-width. Segmented pill, same voice as the app's other toggles (ink
   fill on the active tab). The folder trail (sep + here) rides alongside when
   you drill into a site. (Mark) */
.lib-head-nav { display: flex; align-items: center; gap: 9px; min-width: 0; }
/* Library section tabs as quiet TEXT LINKS (Mark: the segmented capsule read
   clumsy once there were three). Active = ink with a hairline underline;
   scales to any number of sections. */
.lib-tabs {
  display: inline-flex; align-items: center; gap: 18px; flex: none;
}
.lib-tab {
  border: 0; background: transparent; border-radius: 0;
  font-family: var(--sans); font-size: 13px; font-weight: 500; letter-spacing: .005em;
  color: var(--ink50); padding: 4px 0; cursor: pointer;
  transition: color .15s ease;
}
.lib-tab:hover { color: var(--ink); }
/* Active = ink alone, no underline (Mark: underlines appear nowhere else in
   the app). Weight stays equal so the row doesn't shift on switch. */
.lib-tab.is-active { background: transparent; color: var(--ink); }
.lib-exit {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  border: 1px solid var(--bmed); background: var(--paper); border-radius: 8px;
  padding: 6px 11px; cursor: pointer; font-size: 12.5px; color: var(--ink-soft);
}
.lib-exit:hover { border-color: var(--ink); color: var(--ink); }
.lib-exit svg { flex: none; }
/* Library search — sits where "Back to capture" was (top-right of the util
   row). Mirrors the add-to-layout picker's search. (Mark) */
.lib-search {
  flex: none; width: 220px; max-width: 42%; height: 32px;
  border: 1px solid var(--bmed); background: var(--paper); border-radius: 8px;
  padding: 0 11px; font-size: 12.5px; color: var(--ink); font-family: var(--sans);
  outline: none;
}
.lib-search::placeholder { color: var(--ink50); }
.lib-search:focus { border-color: var(--ink); }
/* Focus is shown by the border darkening (like .text-input) — suppress the
   global focus-visible outline ring so it doesn't read as a double border. */
.lib-search:focus-visible { outline: none; }
/* The Import tile lives in the folder grid's Uploads section and borrows the
   .libu-folder / .libu-new shapes, so it needs no size rules of its own — only
   the disabled state while an import runs. (A header button was tried first and
   floated in dead space between the tabs and the search: .lib-head-util is
   space-between, which pins the first child left and the last right and leaves
   anything in between adrift.) */
[data-lib-import][disabled] { opacity: 0.6; cursor: default; }
/* Dropping image files anywhere on the grid imports them. The cue is a dashed
   inset rather than a full-bleed overlay, so the captures underneath stay
   readable and it is obvious the drop lands in THIS grid. */
.history-grid.is-filedrop {
  outline: 2px dashed var(--ink50); outline-offset: -6px; border-radius: 10px;
}
.lib-head-title {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-bottom: 22px;
}
.lib-head-name { display: flex; align-items: baseline; gap: 9px; min-width: 0; }
/* Back to All captures — a quiet round icon button beside the folder title,
   only present when drilled in (setLibHeader toggles it). */
.lib-back {
  flex: none; width: 30px; height: 30px; padding: 0; border: 0; border-radius: 100px;
  background: transparent; color: var(--ink50); cursor: pointer;
  display: grid; place-items: center; align-self: center;
  transition: background .12s ease, color .12s ease;
}
.lib-back:hover { background: var(--ink08); color: var(--ink); }
.lib-back svg { width: 17px; height: 17px; }
/* display:grid above would defeat the [hidden] attribute (UA display:none),
   so restate it — same fix the g2 detail overlay needed. */
.lib-back[hidden] { display: none; }
.lib-title {
  font-family: var(--sans); font-size: 30px; font-weight: 500; line-height: 1.12;
  letter-spacing: -0.015em; color: var(--ink);
  margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lib-title-count { font-family: var(--mono); font-size: 12px; color: var(--ink-mute); flex: none; }
.lib-head-title .history-filters { margin: 0; flex: none; }

/* Top-bar Library icon ⇄ close while the library is open. */
.lib-ico-close { display: none; }
#topnav-library.is-close .lib-ico-grid { display: none; }
#topnav-library.is-close .lib-ico-close { display: block; }
/* Guide / Settings top-bar buttons swap their icon for an X while open — same
   close affordance as the Library icon, so every top-right destination has one
   obvious way out (Mark). */
.topbar-nav-btn .tn-ico-close { display: none; }
.topbar-nav-btn.is-close .tn-ico-main { display: none; }
.topbar-nav-btn.is-close .tn-ico-close { display: block; }

/* Library is now a vertical stack of per-site groups, each with its own
   responsive thumbnail grid. */
.history-grid { display: block; }

.lib-note {
  padding: 10px 13px;
  margin-bottom: 16px;
  border: 1px solid var(--bmed);
  border-radius: 9px;
  background: var(--bg2);
  color: var(--ink);
  font-size: 12px;
  line-height: 1.5;
}
/* The same message, floated, for everywhere that isn't the library. libNote()
   used to give up when #history-grid was absent, so a capture that failed to
   open from the home rail failed in total silence — no file, no reason, nothing
   (Mark, 9 Aug 2026: "they are not opening ... why?"). */
#loey-note {
  position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 8px);
  z-index: 9000; max-width: 460px;
  padding: 11px 15px; border-radius: 10px;
  background: #24231D; color: #F7F5EF; border: 1px solid rgba(255, 255, 255, .12);
  /* Stated, as it was when this pill lived on <body>, outside .app. It stands in
     .app now (21 Sep 2026), so the words in a timeline's row take the theme's ink. */
  font-family: var(--sans);
  font-size: 12.5px; line-height: 1.5; text-align: center;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .22);
  opacity: 0; pointer-events: none; transition: opacity .16s ease, transform .16s ease;
}
#loey-note.is-show { opacity: 1; transform: translate(-50%, 0); }
/* IN THE TIMELINE'S ROW (21 Sep 2026, Mark: "maybe in the gap in the timeline under the preview?", then "ok do B"): the words alone,
   no pill, in the row's own muted ink, centred under the preview and cut short before the controls either side (loeyNotePlace
   gives the room); an Undo is a word after them, in full ink. */
#loey-note.is-row, #loey-note.is-row.is-show { transform: translate(-50%, -50%); }
#loey-note.is-row {
  display: flex; align-items: center; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none;
  color: var(--ink65); white-space: nowrap; transition: opacity .16s ease;
}
#loey-note.is-row .loey-note-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
#loey-note.is-row .loey-note-act { flex: none; margin-left: 12px; padding: 0; background: none; border: 0; color: var(--ink); font-weight: 500; }
#loey-note.is-row .loey-note-act:hover { background: none; text-decoration: underline; text-underline-offset: 3px; }
/* #317. The action on a message — in practice always Undo. The pill itself is
   pointer-events:none so it never blocks the app underneath it; the button has
   to switch that back on for itself alone, or it is a control you can see and
   cannot press. Inline rather than on its own line: the sentence and the way
   out of it are one thought. */
#loey-note .loey-note-act, .lib-note .loey-note-act {
  display: inline-flex; align-items: center;
  margin-left: 10px; padding: 3px 9px;
  font-family: var(--sans); font-size: 12px; line-height: 1.4; letter-spacing: .01em;
  border-radius: 6px; cursor: pointer; pointer-events: auto;
}
#loey-note .loey-note-act {
  background: rgba(247, 245, 239, .14); color: #F7F5EF;
  border: 1px solid rgba(247, 245, 239, .26);
}
#loey-note .loey-note-act:hover { background: rgba(247, 245, 239, .24); }
.lib-note .loey-note-act {
  background: var(--btn2bg); color: var(--ink); border: 0;
}
.lib-note .loey-note-act:hover { background: var(--btn2bg-hover); }
.loey-note-act[disabled] { opacity: .5; cursor: default; }
.lib-group { margin-bottom: 32px; }
.lib-group:last-child { margin-bottom: 0; }
.lib-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 14px;
}
.lib-fav {
  width: 18px;
  height: 18px;
  border-radius: 5px;
  object-fit: cover;
  flex: none;
  background: var(--bg2);
}
.lib-fav-mono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  color: var(--ink);
  text-transform: uppercase;
}
.lib-group-host {
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--ink);
}
.lib-group-count {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink50);
}
.lib-group-clear {
  margin-left: 8px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink50);
  background: transparent;
  border: 0;
  padding: 2px 4px;
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s ease, color .12s ease;
}
.lib-group-head:hover .lib-group-clear { opacity: 1; }
.lib-group-clear:hover { color: var(--ink); }

/* Per-site clear — ONE always-visible button at the BOTTOM of a site's
   captures, labelled with the site name so it's obvious it clears only that
   site (Mark). Replaces the hover-hidden header button + the global one. */
.lib-site-clear-row {
  display: flex;
  justify-content: center;
  /* The Uploads shelf puts an Import button in here beside the clear, so the
     row has to space two children rather than centre one. */
  gap: 10px;
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--ink08);
}
.lib-site-clear { color: var(--ink50); }

/* ── Folder grid (library home) ── */
.lib-folder-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(max(200px, (100% - 56px) / 5), 1fr));
  gap: 14px;
}
.lib-folder {
  display: flex;
  flex-direction: column;
  text-align: left;
  padding: 0;
  border: 1px solid var(--bmed);
  border-radius: 10px;   /* one card radius (6 Aug 2026) */
  overflow: hidden;
  background: var(--card-fill);
  cursor: pointer;
  transition: border-color .12s;
}
.lib-folder:hover { border-color: var(--ink); }
.lib-folder-thumb {
  aspect-ratio: 4 / 3;
  background: var(--bg2);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.lib-folder-thumb img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
.lib-folder-thumb .lib-thumb-ph { font-size: 22px; }
.lib-folder-info { display: flex; align-items: center; gap: 8px; padding: 10px 12px; }
.lib-folder-name {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lib-folder-count { font-family: var(--mono); font-size: 10px; color: var(--ink50); }

/* Breadcrumb (inside a folder) */
.lib-crumb { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; }
.lib-back {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink);
  background: transparent;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
}
.lib-back:hover { text-decoration: underline; }
.lib-crumb-sep { color: var(--ink32); }
.lib-crumb-name {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}

/* Favourite star (card) + favourites marker */
.lib-fav-star {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 2;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: rgba(16, 16, 14, 0.55);
  color: #FBFAF8;
  opacity: 0;
  transition: opacity .12s ease, background .12s ease;
}
.lib-fav-star svg { fill: none; stroke: currentColor; stroke-width: 1.3; }
.lib-card:hover .lib-fav-star { opacity: 1; }
.lib-fav-star:hover { background: rgba(16, 16, 14, 0.8); }
.lib-fav-star.on { opacity: 1; }
.lib-fav-star.on svg { fill: #F7CE46; stroke: #F7CE46; }
.lib-fav-on { color: #F7CE46; background: transparent; }
.lib-fav-on svg { fill: currentColor; stroke: none; }
.lib-grid {
  display: grid;
  /* Cap at 5 columns: each track is at least 1/5 of the row (minus the
     4 gaps), so a wide laptop screen lands on 5 wider cards instead of 6
     narrow ones that truncate the text — but still drops to fewer
     columns on narrower windows via the 210px floor. */
  grid-template-columns: repeat(auto-fill, minmax(max(210px, (100% - 56px) / 5), 1fr));
  gap: 14px;
}
/* Grouping sections inside a site folder (group by Page / Type / Date). */
.lib-sec { margin-top: 26px; }
.lib-sec:first-of-type { margin-top: 14px; }
.lib-sec-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 14px; }
.lib-sec-title {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink);
}
.lib-sec-count { font-family: var(--mono); font-size: 11px; color: var(--ink50); }
/* ── WHY A LONG GRID SCROLLS SMOOTHLY (#307, 17 Aug 2026) ─────────────────
   Mark: "scrolling in the library picker was really juddery for a bit then went
   ok. any reason?"

   Yes, and the "then went ok" is the tell. Every card in these grids is built
   at once, each with its own <img> whose src is a stored data URL. Nothing is
   fetched — the bytes are already in memory — but each one still has to be
   DECODED, and a decode that happens during paint blocks the frame. Scroll a
   fresh grid and you pay for every thumbnail that comes into view; scroll it
   again and the decoder cache answers instantly, which is why it settles.

   Three things, and they attack different halves of it. The images carry
   decoding="async" and loading="lazy" (see app.js), so a decode can never
   block a frame and offscreen ones do not start. This rule is the third and
   the strongest: content-visibility lets the browser skip layout, style and
   paint entirely for cards that are not on screen. contain-intrinsic-size is
   not optional with it — without a size to stand in with, skipped cards
   collapse to nothing and the scrollbar jumps around as you travel. The value
   is a whole card: a square thumbnail plus the three lines of body text. */
.lib-card {
  content-visibility: auto;
  contain-intrinsic-size: auto 260px;
}
.lib-card {
  border: 1px solid var(--bmed);
  /* 10px — one card radius across the app (Mark, 6 Aug 2026): library cards,
     library folders, the latest-capture card, the recent-capture cards, the
     home mode cards and the Layouts template cards all sit at 10 now. Pills,
     swatches and anything already under 10 keep their own value. */
  border-radius: 10px;
  overflow: hidden;
  background: var(--card-fill);
  display: flex;
  flex-direction: column;
  transition: border-color .12s;
}
.lib-card:hover { border-color: var(--ink); }
.lib-thumb {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--bg2);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: pointer;
}
.lib-thumb-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  display: block;
}
/* Tall scroll: fill the square from the top, fade out the cut bottom. */
.lib-thumb.is-tall .lib-thumb-img.is-tall {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: cover;
  object-position: top;
}
.lib-thumb-fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 22%;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, var(--bg2) 100%);
}

/* ── Library bulk select ── */
.lib-select-toggle {
  margin-left: auto;                 /* sit at the right end of the control row */
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  border-radius: 8px;
  padding: 5px 12px;
  cursor: pointer;
  white-space: nowrap;
}
.lib-select-toggle:hover { border-color: var(--ink32); }
.lib-select-toggle.is-on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
/* Style + Delete are ACTIONS, not filters (Mark, 16 July 2026): a thin divider
   separates them from the filter controls, and they carry small icons. The
   divider takes over the right-push, so the buttons drop their auto margin. */
.lib-act-div { margin-left: auto; width: 1px; height: 18px; background: var(--border); flex: none; align-self: center; }
.lib-act-div ~ .lib-select-toggle { margin-left: 0; }
.lib-act-btn { display: inline-flex; align-items: center; gap: 6px; }
.lib-act-btn svg { flex: none; color: var(--ink50); }
.lib-act-btn:hover svg { color: var(--ink); }
.lib-select-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 14px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg2, var(--paper));
}
.lib-select-bar[hidden] { display: none; }
.lib-sel-count { font-family: var(--sans); font-size: 12.5px; font-weight: 500; color: var(--ink); }
.lib-sel-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
/* Select mode: hide the hover affordances (favourite star, ··· menu,
   recapture) so they don't clash with the checkbox in the corner or intercept
   a tap — the whole card is the target while selecting. */
.lib-card.is-selectable .lib-fav-star,
.lib-card.is-selectable .lib-thumb-actions { display: none !important; }
.lib-card.is-selectable,
.lib-card.is-selectable .lib-thumb { cursor: pointer; }
/* Whole-card selection: a clean ink ring hugging the entire card. */
.lib-card.is-selectable.is-selected {
  border-color: var(--ink);
  box-shadow: 0 0 0 1px var(--ink);
}
/* Selection check — a soft white circle (no hard border); fills ink with a
   white tick when the card is selected. */
.lib-check {
  position: absolute;
  top: 8px;
  left: 8px;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 4;
  pointer-events: none;
}
.lib-card.is-selected .lib-check { background: var(--ink); }
.lib-check::after {
  content: "";
  width: 5px;
  height: 9px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translateY(-1px);
  opacity: 0;
  transition: opacity .1s ease;
}
.lib-card.is-selected .lib-check::after { opacity: 1; }

/* Latest page capture thumbnail in the Page Capture preview column. Long
   captures show the top and fade out, mirroring the All Captures tall thumbs. */
.page-latest-thumb {
  position: relative;
  margin-top: 10px;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--bmed);
  background: var(--card-fill);
  line-height: 0;
}
.page-latest-thumb img { width: 100%; display: block; }
.page-latest-thumb.is-tall { max-height: 280px; }
.page-latest-fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 42%;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, var(--bg2) 100%);
}
/* Smart-auto offer: shown under an at-risk Freeze capture (below-fold lazy
   images likely blank). A soft info note, not an error — the capture saved,
   this just offers a more complete one. */
.page-lazy-offer {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid var(--bmed);
  border-radius: 10px;
  background: var(--ink08);
}
.page-lazy-offer-text {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-soft);
}
.lib-thumb-ph {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  color: var(--ink32);
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  padding: 8px;
}
.lib-thumb-video { color: var(--ink50); }
/* Play badge over a video poster — centred, subtle, so a video card reads
   as video at a glance while still showing its real first frame. */
.lib-thumb-playbadge {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(16,16,14,0.55);
  color: #fff;
  pointer-events: none;
  backdrop-filter: blur(1px);
}
.lib-thumb-playbadge svg { width: 18px; height: 18px; margin-left: 1px; }
/* Open + Delete icons overlaid on the thumbnail corner — keeps them off
   the text, which gets the full card width. Fade in on card hover. */
.lib-thumb-actions {
  position: absolute;
  top: 6px;
  right: 6px;
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity .12s ease;
  z-index: 2;
}
.lib-card:hover .lib-thumb-actions { opacity: 1; }
.lib-thumb-actions .lib-thumb-btn {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: rgba(16, 16, 14, 0.82);
  color: #FBFAF8;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.lib-thumb-actions .lib-thumb-btn:hover { background: rgba(16, 16, 14, 1); }
/* Keep the hover actions visible while a card's ··· menu is open. */
.lib-card:has(.lib-menu:not([hidden])) .lib-thumb-actions { opacity: 1; }
.lib-menu-wrap { position: relative; }
.lib-menu {
  position: absolute;
  top: calc(100% + 5px);
  right: 0;
  min-width: 144px;
  background: rgba(16, 16, 14, 0.95);
  border: 0.5px solid rgba(251, 250, 248, 0.14);
  border-radius: 9px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
  padding: 4px;
  display: flex;
  flex-direction: column;
  z-index: 6;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.lib-menu[hidden] { display: none; }
.lib-menu button {
  text-align: left;
  background: transparent;
  border: 0;
  padding: 7px 10px;
  font: inherit;
  font-size: 12px;
  color: #FBFAF8;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
}
.lib-menu button:hover { background: rgba(251, 250, 248, 0.12); }
/* Saved-setup recipe block hosts the hover actions and is itself the
   click target for Edit. */
.lib-setup-recipe { position: relative; cursor: pointer; }
.lib-card-body { padding: 11px 12px 12px; }
/* Type eyebrow (mono small-caps) · Website name (lead) · Page · meta. */
.lib-card-type {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink50);
  margin-bottom: 5px;
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.lib-card-type-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Freeze / Scroll render-mode badge on full-page still cards (Mark). */
.lib-card-mode { flex: none; font-size: 8.5px; letter-spacing: 0.06em; padding: 1px 5px; border-radius: 4px;
  border: 1px solid var(--ink18); color: var(--ink-soft); background: var(--ink08); }
/* The variants chip — the render-mode chip's shape, but a BUTTON: it opens
   the original's styled copies inline (15 Aug 2026). */
/* ROUNDS 16–17: the styled-versions opener. Round 16's type-line row
   truncated the type text (Mark: "we cant have things truncated") — so it
   wears the SET BADGE's own corner grammar instead: a dark count pill on the
   thumbnail, bottom right (hover actions own the top), the
   overlapping-squares glyph saying what the count is. Same .lib-set-badge
   anatomy, one glyph heavier. */
.lib-var-badge {
  position: absolute;
  bottom: 8px;
  right: 8px;
  min-width: 20px;
  height: 20px;
  padding: 0 7px;
  border: 0;
  border-radius: 100px;
  background: rgba(16,16,14,0.92);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
  z-index: 2;
}
.lib-var-badge svg { flex: none; display: block; }
.lib-var-badge:hover { background: rgba(16,16,14,1); }
.lib-var-badge.is-on { background: var(--paper); color: var(--ink); box-shadow: 0 0 0 1px var(--ink18); }
/* Quiet device glyph at the type line's right edge — the device readable
   while scanning, without a chip's weight (Mark). */
.lib-card-dev { margin-left: auto; flex: none; display: inline-flex; color: var(--ink50); }
.lib-card-dev svg { width: 18px; height: 18px; display: block; }   /* 12px read as a speck (21 Sep 2026, Mark: "make the device icon on the library/picker cards a bit bigger"); shell.css says the same under the shell */
.lib-card-title {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lib-card-page {
  font-size: 12px;
  color: var(--ink65);
  line-height: 1.3;
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Compressed caption: page name as the lead, then a quiet mono Type · time
   line. The brand is dropped (the breadcrumb carries it); the device frame
   carries the device. */
.lib-card-page-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lib-card-page-name + .lib-card-meta { margin-top: 5px; }
/* Site favicon on the card's site / page-name line. Inline so the name still
   truncates with an ellipsis after it. */
.lib-card-favicon { width: 14px; height: 14px; border-radius: 3px; margin-right: 6px; vertical-align: -2px; object-fit: contain; }
.lib-card-meta {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink50);
  margin-top: 7px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Title row: titles on the left, Open + Delete icons on the right. */
.lib-card-titlerow {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}
.lib-card-titles { flex: 1; min-width: 0; }
.lib-card-icons {
  flex: none;
  display: flex;
  gap: 2px;
  /* Pull the icons up to sit level with the title line. */
  margin: -2px -4px 0 0;
}
.lib-set-toggle {
  flex: none;
  align-self: flex-start;
  white-space: nowrap;
}
/* Recapture / Edit — quiet text actions, shown only when the entry
   carries a recipe. */
.lib-card-recap {
  display: flex;
  gap: 12px;
  margin-top: 9px;
}
.lib-card-recap button {
  /* was: var(--mono) / 10px / 0.06em / uppercase - see BUTTONS: NO MONO */
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink50);
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color .12s ease;
}
.lib-card-recap button:hover { color: var(--ink); }
/* Saved-setup card — the recipe preview replaces the thumbnail. */
.lib-setup-recipe {
  background: var(--bg2);
  border-bottom: 1px solid var(--bmed);
  padding: 16px 14px;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  min-height: 86px;
}
.lib-setup-recipe-text { flex: 1; min-width: 0; }
/* Recipe schematic — a hairline diagram of what the setup will DO (stops on
   a rail, connected pages, clip frames), drawn from its real numbers. Fills
   the zone a capture card fills with a thumbnail, without shouting. */
.lib-setup-glyph { flex: none; display: flex; color: var(--ink32); }
.lib-setup-glyph svg { width: 48px; height: 40px; display: block; }
.lib-setup-recipe-type {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink50);
}
.lib-setup-recipe-text {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.3;
}
.lib-setup-actions { flex-wrap: wrap; margin-top: 10px; }
/* Capture is the primary action; Edit next; Move/Delete recede. */
.lib-setup-actions button:nth-child(1) { color: var(--ink); }
.lib-setup-actions button:nth-child(n+3) { opacity: 0.7; }
/* Grouped set card — stacked member thumbnails + count badge. */
.lib-thumb-set { background: var(--bg2); }
.lib-set-stack {
  position: relative;
  width: 66%;
  height: 66%;
  /* The members fan down-right (8px each), so the cluster's centre sits
     +8px from the box centre — nudge the whole stack back up-left so it
     reads as centred in the square. */
  transform: translate(-8px, -8px);
}
.lib-set-thumb {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--paper);
  box-shadow: 0 2px 8px rgba(0,0,0,0.14);
  transform: translate(calc(var(--i) * 8px), calc(var(--i) * 8px));
  background: var(--card-fill);
}
/* Expanded set: no boxed tray — the set card + its members just take
   the darker hover-style border so they read as one group inline. */
.lib-card-set.is-open,
.lib-card-member { border-color: var(--ink); }
.lib-set-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 100px;
  background: rgba(16,16,14,0.92);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Saved setups cards are text-only and carry three action buttons, so
   they need more horizontal space than the thumbnail-led history grid. */
.sets-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}
.hcard {
  border: 1px solid var(--bmed);
  overflow: hidden;
  cursor: pointer;
  background: var(--bg);
  text-align: left;
  padding: 0;
  display: flex;
  flex-direction: column;
  transition: border-color .12s;
}
.hcard:hover { border-color: var(--ink); }
.hcard-thumb {
  width: 100%;
  aspect-ratio: 16 / 10;
  background: var(--bg2);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink32);
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.hcard-meta {
  padding: 10px 12px 12px;
}
.hcard-name {
  font-size: 11px;
  font-weight: 400;
  color: var(--ink);
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hcard-detail {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink50);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.history-clear-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 32px;
}
.history-clear-confirm {
  font-size: 11px;
  color: var(--ink50);
  display: none;
  align-items: center;
  gap: 10px;
}

/* ---------- Empty state (History) ---------- */
.empty-state {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink50);
  line-height: 1.85;
  padding: 24px 0;
  max-width: 460px;
}
/* All captures → back to capture modes (Mark). A quiet link above the title. */
.lib-back {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0 0 12px -6px; padding: 5px 9px 5px 6px; border: 0; border-radius: 8px;
  background: transparent; color: var(--ink50); cursor: pointer;
  font-family: var(--sans); font-size: 12.5px;
}
.lib-back:hover { background: var(--ink08); color: var(--ink); }
.lib-back svg { flex: none; }

/* ---------- Collapse-on-defaults option rows ---------- */
.option-row {
  border-bottom: 1px solid var(--ink08);
}
.option-row:last-child {
  border-bottom: 0;
}
/* Mockup fit captures the exact device rectangle, so the styling controls
   don't apply — hide them while it's on. The class lives on <html> (NOT the
   panel) because Canvas mode relocates these rows out of the panel into
   #canvas-settings accordions — a panel-scoped rule would miss them there.
   <html> is an ancestor in both layouts. display:none !important so nothing
   can re-show them. (Mark — kept not hiding) */
html.is-mockupfit .option-row[data-key="viewportPadding"],
html.is-mockupfit .option-row[data-key="viewportBackground"],
html.is-mockupfit .option-row[data-key="viewportBorder"],
html.is-mockupfit .option-row[data-key="viewportShadow"],
html.is-mockupfit .option-row[data-key="viewportBorderRadius"],
html.is-mockupfit .option-row[data-key="viewportAspect"],
html.is-mockupfit .stills-finish-reset[data-reset-mode="viewport"] {
  display: none !important;
}
.option-summary {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 14px 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  color: var(--ink);
  transition: opacity .12s;
}
.option-summary:hover { opacity: 0.78; }
.option-label {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink);
  /* Keep the label + its info-tip on one line — the inline padding
     slider was squeezing "Padding (i)" so the tip wrapped below (Mark). */
  white-space: nowrap;
}
.option-value {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink50);
}
.option-chevron {
  width: 10px;
  height: 10px;
  margin-left: 12px;
  color: var(--ink32);
  transition: transform .15s ease;
}
.option-row.is-open .option-chevron { transform: rotate(90deg); }
.option-expand {
  padding: 4px 0 18px;
}
.option-expand[hidden] { display: none; }

/* Motion "More options" — wraps the secondary motion settings so the
   step defaults to just Speed + Easing. Reuses the option-row toggle;
   the summary reads quietly so it doesn't compete with the two core
   controls above it. */
.motion-more { border-bottom: 0; }
.motion-more > .option-summary .option-label { color: var(--ink50); }
.motion-more .option-expand .option-row:last-child { border-bottom: 0; }

/* ---------- Swatch grid (Background colour picker) ----------
   Replaces the three-pill Transparent / White / Black selector with
   a row of circular swatches plus a Custom (+) swatch that opens a
   popover. First introduction of user-content colour in the Capt UI
   chrome — the chrome itself stays monochrome; only the swatches
   themselves carry the user's chosen colour. */
.swatch-grid {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  position: relative;
}
.swatch {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--bmed);
  background: var(--bg2);
  padding: 0;
  cursor: pointer;
  position: relative;
  flex: none;
  transition: transform .12s ease, border-color .12s ease;
  /* The selected ring is painted with an outset box-shadow so the
     swatch itself stays at its colour without an inner outline that
     would change the visible swatch tone. */
}
.swatch:hover { border-color: var(--bstr); }
.swatch.is-selected {
  box-shadow: 0 0 0 2px var(--ink);
}
/* Transparent swatch — chequerboard texture so the user reads the
   absence of colour rather than mistaking it for a white tile. */
.swatch[data-swatch="transparent"] {
  background:
    linear-gradient(45deg, var(--ink08) 25%, transparent 25%) 0 0 / 8px 8px,
    linear-gradient(-45deg, var(--ink08) 25%, transparent 25%) 0 4px / 8px 8px,
    linear-gradient(45deg, transparent 75%, var(--ink08) 75%) 4px -4px / 8px 8px,
    linear-gradient(-45deg, transparent 75%, var(--ink08) 75%) -4px 0 / 8px 8px,
    var(--bg2);
}
/* Custom (+) swatch — shows the currently-bound custom colour as
   its fill when a custom hex is active, otherwise reads as an empty
   placeholder with a plus sign. */
.swatch[data-swatch="custom"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg2);
}
.swatch[data-swatch="custom"]::after {
  content: '+';
  font-family: var(--mono);
  font-size: 18px;
  color: var(--ink50);
  line-height: 1;
}
.swatch[data-swatch="custom"].has-color::after { content: none; }

/* Popover that drops in below the Custom swatch when clicked.
   Anchored to the swatch-grid, positioned absolutely. Stays within
   the option-expand region. */
.swatch-popover {
  position: absolute;
  top: 44px;
  left: 0;
  z-index: 5;
  background: var(--bg2);
  border: 1px solid var(--bmed);
  border-radius: 10px;
  padding: 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
  min-width: 220px;
}
.swatch-popover[hidden] { display: none; }
.swatch-popover .swatch-hex {
  flex: 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--bg);
  border: 1px solid var(--bmed);
  border-radius: 6px;
  padding: 8px 10px;
  color: var(--ink);
  outline: none;
}
.swatch-popover .swatch-hex:focus { border-color: var(--ink); }
.swatch-popover .swatch-native {
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--bmed);
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
  /* The native colour input has its own internal padding; the
     ::-webkit-color-swatch-wrapper reset trims it so the colour fills
     the visible square. */
}
.swatch-popover .swatch-native::-webkit-color-swatch-wrapper { padding: 0; }
.swatch-popover .swatch-native::-webkit-color-swatch { border: 0; border-radius: 4px; }

/* ---------- Picked target (Range capture name + selector reveal) ---------- */
/* Surface for the picked element's friendly name. Pairs with the Pick
   button on the right; the raw CSS selector sits underneath behind a
   small toggle so the panel doesn't lead with builder noise like
   `body > main > section > h1.elementor-heading-title`. */
.picked-target {
  display: flex;
  align-items: stretch;
  gap: 8px;
}
.picked-target-display {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  padding: 10px 12px 10px 14px;
  background: var(--bg2);
  border: 1px solid var(--bmed);
  /* Rounded to sit comfortably inside the pill cards — the old square
     box read stark against their 30px radius. */
  border-radius: 10px;
  transition: border-color .15s ease;
}
.picked-target-display:hover { border-color: var(--bstr); }
/* Viewport: Roam | Tile a page mode switch (right column). */
.viewport-cap-mode { display: inline-flex; margin: 0 0 12px; }
/* Page Capture Auto/Manual: hard hide for the inactive method's controls. Plain
   [hidden] loses to the elements' own class display rules, so use !important. */
.method-off { display: none !important; }
/* Black-pill "Open capture window" (Manual primary) — matches the topbar's
   relocated primary action: ink fill, light text, open-in-new-window icon. */
.open-window-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  background: var(--ink); color: var(--bg);
  border: 0; border-radius: 100px;
  padding: 8px 15px;
  font-family: var(--sans); font-size: 12.5px; font-weight: 500; letter-spacing: 0;
  cursor: pointer;
  transition: opacity .15s ease, transform .08s ease;
}
.open-window-btn:hover { opacity: 0.9; }
.open-window-btn:active { transform: translateY(1px); }
.open-window-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.open-window-btn svg { width: 13px; height: 13px; flex: none; }
/* Area capture: Page | Styled stage view switch. It lives in the stage toolbar
   (.lab-chrome) now, so style it as a segmented control that matches the device
   switcher beside it — one bordered rounded-rect, flush cells, ink fill on the
   active segment — instead of two big stray pills (Mark). */
.iso-preview-switch { display: inline-flex; }
.iso-preview-switch[hidden] { display: none; }
.lab-chrome .iso-preview-switch {
  position: relative;
  margin: 0 0 0 13px; /* gap from the device selector, room for the divider */
  padding: 0;
  gap: 0;
  flex: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg);
}
/* Hairline divider between the device selector (left) and Page | Styled. */
.lab-chrome .iso-preview-switch::before {
  content: '';
  position: absolute;
  left: -7px; top: 3px; bottom: 3px;
  width: 1px;
  background: var(--border);
}
.lab-chrome .iso-preview-switch .pill {
  border: 0;
  border-radius: 0;
  padding: 5px 10px;
  font-size: 12px;
  font-weight: 400;
  color: var(--ink50);
  background: transparent;
}
.lab-chrome .iso-preview-switch .pill:hover {
  color: var(--ink);
  background: var(--ink08);
  border-color: transparent;
}
.lab-chrome .iso-preview-switch .pill.is-selected {
  background: var(--ink);
  color: var(--bg);
  border-color: transparent;
}
/* Styled stage — a gallery of the chosen areas, each shown with the current
   styling applied so you can see the output before exporting. */
.iso-styled-stage { display: block; }
.iso-styled-stage[hidden] { display: none; }
.iso-styled-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 20px 18px;
  align-content: start;
  max-height: 72vh;
  overflow-y: auto;
  padding: 4px; /* room for the active-card ring so the scroll box doesn't clip it */
}
.iso-styled-card { margin: 0; min-width: 0; cursor: pointer; }
/* Single fitted preview (Mark): one selected area, scaled to fit the canvas and
   centred — no scrolling. The active area is chosen from the Areas rail. */
.iso-styled-single {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 12px; width: 100%; box-sizing: border-box; padding: 8px;
}
.iso-styled-single .iso-styled-card { cursor: default; max-width: 100%; }
/* No hairline frame around the fitted preview (Mark: "remove the black frame") —
   the styling (bg / border / shadow) is what should read, nothing else. */
.iso-styled-single .iso-styled-frame { max-width: 100%; box-shadow: none; }
.iso-styled-single .iso-styled-frame img { max-width: 100%; }
.iso-styled-cap {
  display: flex; align-items: center; gap: 7px;
  font-family: var(--sans); font-size: 12px; color: var(--ink-soft);
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Honest-preview note under the Crop stage caption — quieter than the
   caption itself: the thumb is an as-is grab, the saved capture primes
   lazy loads / scroll reveals first (Mark). */
.iso-styled-note {
  font-family: var(--sans); font-size: 11px; color: var(--ink50);
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-top: 2px;
}
/* Selected area ↔ its styled image share a highlight (Mark). A box-shadow ring
   on the FRAME (not an offset outline on the figure) — the offset outline got
   clipped by the gallery's scroll container and showed as stray corner marks. */
.iso-styled-card.is-active .iso-styled-frame { box-shadow: 0 0 0 2px var(--ink); }
.iso-section.is-active > .tour-section-card { border-color: var(--ink); }
/* Include/exclude checkbox on an area card (parity with page clips). An excluded
   area stays defined but dims and isn't captured. */
.iso-section-enable { flex: none; width: 15px; height: 15px; margin: 0 2px 0 0; accent-color: var(--ink); cursor: pointer; }
.iso-section.is-excluded > .tour-section-card { opacity: 0.55; }
.iso-styled-frame {
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 1px rgba(14, 14, 12, 0.07);
}
.iso-styled-frame.is-transparent {
  background-color: #ececea;
  background-image:
    linear-gradient(45deg, #d3d3d0 25%, transparent 25%),
    linear-gradient(-45deg, #d3d3d0 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #d3d3d0 75%),
    linear-gradient(-45deg, transparent 75%, #d3d3d0 75%);
  background-size: 14px 14px;
  background-position: 0 0, 0 7px, 7px -7px, -7px 0;
}
.iso-styled-card figcaption {
  display: flex; align-items: center; gap: 7px;
  margin-top: 9px;
  font-family: var(--sans); font-size: 12px; color: var(--ink-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.iso-styled-num { font-family: var(--mono); font-size: 10px; color: var(--ink50); flex: none; letter-spacing: 0.04em; }
/* The frame keeps the crop's reserved height while a grab is out, so the empty
   state is centred in it rather than sitting as a strip at the top. */
.iso-styled-frame:has(.iso-styled-empty) { display: flex; align-items: center; justify-content: center; }
.iso-styled-empty {
  display: flex; align-items: center; justify-content: center;
  width: 100%; min-height: 96px;
  font-family: var(--sans); font-size: 12px; color: var(--ink50);
  border: 1px dashed var(--bmed); border-radius: 8px; background: var(--paper);
}
.iso-styled-blank {
  padding: 48px 8px; text-align: center;
  font-family: var(--sans); font-size: 13px; color: var(--ink-soft);
}
/* Captured views (Walk mode) — a clean list in the right column, no heavy box. */
.viewport-combine-bar { display: block; margin: 16px 0 4px; }
.viewport-combine-bar[hidden] { display: none; }
.viewport-combine-bar .vcb-head { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.viewport-combine-bar .vcb-eyebrow { font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em; text-transform: none; color: var(--ink-soft); font-weight: 500;}
.viewport-combine-bar .vcb-actions { display: inline-flex; align-items: center; gap: 12px; margin-left: auto; }
.vcb-clear { border: 0; background: transparent; padding: 0; font-family: var(--sans); font-size: 12px; color: var(--ink50); cursor: pointer; }
.vcb-clear:hover { color: var(--ink); }
/* The captured views, listed top-to-bottom in stitch order. */
.viewport-combine-bar .vcb-list { display: flex; flex-direction: column; gap: 7px; max-height: 260px; overflow-y: auto; }
.vcb-thumb { position: relative; border: 1px solid var(--bmed); border-radius: 8px; overflow: hidden; background: var(--paper); }
.vcb-thumb img { display: block; width: 100%; height: 52px; object-fit: cover; object-position: top; }
.vcb-thumb-n { position: absolute; top: 6px; left: 6px; min-width: 17px; height: 17px; padding: 0 5px; display: inline-flex; align-items: center; justify-content: center; border-radius: 6px; background: rgba(14,14,12,0.66); color: #fff; font-family: var(--mono); font-size: 10px; font-weight: 500; }
.vcb-thumb-x { position: absolute; top: 5px; right: 5px; width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 6px; background: rgba(14,14,12,0.55); color: #fff; font-size: 13px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity .12s ease, background .12s ease; }
.vcb-thumb:hover .vcb-thumb-x { opacity: 1; }
.vcb-thumb-x:hover { background: rgba(14,14,12,0.85); }
/* Batch capture: Views / Regions tab switch above the cards. */
.iso-tabs { display: inline-flex; margin: 0 0 14px; }
.iso-tab-empty { padding: 16px 2px 6px; color: var(--ink50); font-family: var(--sans); font-size: 13px; }
/* Per-view "Combine" checkbox in a grabbed-view readout (stitch selection). */
.iso-combine-toggle { display: inline-flex; align-items: center; gap: 5px; margin-left: 10px; font-family: var(--sans); font-size: 11.5px; color: var(--ink65); cursor: pointer; white-space: nowrap; }
.iso-combine-toggle input { margin: 0; cursor: pointer; accent-color: var(--ink); }
.picked-target-display.is-empty {
  background: transparent;
}
.picked-target-name {
  flex: 1;
  min-width: 0;
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.picked-target-display.is-empty .picked-target-name {
  color: var(--ink32);
}

.picked-selector-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding: 4px 0;
  background: none;
  border: 0;
  color: var(--ink50);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color .12s ease;
}
.picked-selector-toggle:hover { color: var(--ink); }
.picked-selector-toggle .option-chevron {
  margin-left: 0;
}
.picked-selector-toggle.is-open .option-chevron {
  transform: rotate(90deg);
}
.picked-selector-expand {
  margin-top: 8px;
}
.picked-selector-expand[hidden] { display: none; }
.picked-selector-expand .text-input {
  font-family: var(--mono);
  font-size: 12px;
}

/* Manual Tour marker groups CSS was here before markers became an
   in-recording feature (no setup UI). Preserved in
   archive/markers-presetup.md alongside the JS that drove them. */

/* ---------- Directed Tour page groups (multi-page) ---------- */
/* Each URL group gets a header followed by its sections. Single-page
   tours render flat — no group wrapper — so the header only appears
   when the user has actually set up sections across multiple pages. */
.tour-group {
  margin-bottom: 18px;
}
.tour-group:last-child {
  margin-bottom: 0;
}
.tour-group-header {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--bweak);
}
.tour-group-num {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink50);
}
.tour-group-host {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-transform: uppercase;
}
.tour-group-current {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--ink-soft);
  text-transform: uppercase;
}
.tour-group-count {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--ink50);
}
/* When both the current-source pip and the count badge are present,
   keep them visually balanced — only one gets margin-left: auto so
   the row reads as: num · host · current · count. */
.tour-group-current + .tour-group-count {
  margin-left: 8px;
}

/* ─── Site Flow visual hierarchy ─────────────────────────────────────
   Page cards are soft rounded containers. Stop cards sit inside the
   page container as smaller filled blocks. Transition rows render as
   separate connector strips between page cards — when the transition
   is fully wired (target picked, next page auto-added). The structure
   tells the journey story top-down: page → stops → connector strip →
   next page → stops → next strip → final page.

   Scoped to #tour-multi-pages so Single Page Tour and Area Capture
   keep their existing simpler treatment. */

#tour-multi-pages .tour-flow-page, #manrec-markers .tour-flow-page {
  margin-bottom: 0;
}
#tour-multi-pages .tour-flow-page + .tour-flow-page, #manrec-markers .tour-flow-page + .tour-flow-page {
  margin-top: 0;
}

/* Page card — soft container. #F2F2F2 fill, no border. 18px radius,
   generous padding. The grey sits between the panel's paper
   (#FAFAFA) and the connector strip's darker grey, so the visual
   hierarchy reads panel → page → connector → next page purely
   through tone, no lines needed. */
#tour-multi-pages .tour-group, #manrec-markers .tour-group {
  background: #0e0e0c0f;
  border: 0;
  border-radius: 18px;
  padding: 22px 22px 20px;
  margin-bottom: 0;
}
/* Page header — keep the existing num/host/count typography, but
   pull the hairline border off (the page card's own border carries
   the separation). Tighten margin so the header sits closer to the
   first stop card. */
#tour-multi-pages .tour-group-header, #manrec-markers .tour-group-header {
  border-bottom: 0;
  padding-bottom: 0;
  margin-bottom: 14px;
}

/* Stop cards inside the page card. Override the cream pill treatment
   with a paper-white filled block: smaller radius (12px vs 30px),
   subtle border, tighter padding. Reads as a nested item rather than
   a free-floating bead. The connector line inside the rail still
   reinforces stop-to-stop sequence within the page. */
#tour-multi-pages .tour-section-card, #manrec-markers .tour-section-card {
  background: var(--paper);
  border: 1px solid var(--ink08);
  border-radius: 10px;
  padding: 14px 18px;
}
/* Per-context rail tweaks for Site Flow: no top-padding override
   now that the parent grid centers the rail vertically with the
   card. The connector line is positioned by the global rule with
   top:0/bottom:-20px and gets visually clipped behind the number
   badge's solid bg patch. */

/* Clear all action — quiet ghost link below the page list, right-
   aligned. Destructive but recoverable (confirms via modal), so it
   sits low in the visual hierarchy. */
.tour-multi-clear-all-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--ink08);
}
/* Number rail sits on the page card's grey background, not the
   panel's paper. The number's background patch must match the page
   card so the rail's connector line passes cleanly behind it. */
#tour-multi-pages .tour-section-num, #manrec-markers .tour-section-num {
  background: transparent;
}

/* Connector between page cards. The transition lives in its own
   surface OUTSIDE the page card whatever its state — empty
   affordance, partial URL fallback, or fully wired. The wrapper is
   a centered flex column carrying two short vertical hairlines and
   the connector box itself. Tone steps panel #FAFAFA → page #F2F2F2
   → connector #E0E0E0 so the journey reads top-down through three
   tones plus the connecting lines. */
/* Transition nested INSIDE its page card (Mark): drop the between-card
   connector lines and add a quiet divider so it reads as part of the page. */
#tour-multi-pages .tour-group-transition, #manrec-markers .tour-group-transition {
  margin-top: 8px;
}
#tour-multi-pages .tour-group-transition .tour-flow-connector, #manrec-markers .tour-group-transition .tour-flow-connector { margin: 0; padding: 0; }
#tour-multi-pages .tour-group-transition .tour-flow-connector-line, #manrec-markers .tour-group-transition .tour-flow-connector-line { display: none; }
#tour-multi-pages .tour-flow-connector, #manrec-markers .tour-flow-connector {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  margin: 0;
}
/* Vertical hairlines top and bottom of the connector box. 18px tall,
   1px wide, centered. Reinforces the downward flow visually since
   the page cards are borderless now. */
#tour-multi-pages .tour-flow-connector-line, #manrec-markers .tour-flow-connector-line {
  display: block;
  width: 1px;
  height: 18px;
  background: var(--bmed);
}
/* Transition pill — shape and button/text styling match the stop
   cards (30px radius, same padding, same ghost-btn sizing), but the
   FILL is a tone step darker than the page card so the visual flow
   reads panel #FAFAFA → page #F2F2F2 → transition #E0E0E0 → next
   page. Tone hierarchy carries the "bridge between pages" meaning
   without needing a different shape or border treatment. */
#tour-multi-pages .tour-flow-connector-pill, #manrec-markers .tour-flow-connector-pill {
  align-self: stretch;
  display: flex;
  align-items: center;
  gap: 14px;
  background: #E0E0E0;
  border: 0;
  border-radius: 30px;
  padding: 18px 24px;
  min-width: 0;
}
/* Vertical-card variant of the transition pill — single pill, no
   nested sub-cards. Sections separated by a hairline. Stripped to
   essentials: "Transition" label, optional setup steps, + Step
   button, divider, link click row. */
/* Slim, quiet connector — lighter than the page cards (it's a bridge,
   not a third config block). Faint fill, tight padding, the connector
   lines above/below run into it. Expands taller only when edited. */
#tour-multi-pages .tour-flow-transition-card, #manrec-markers .tour-flow-transition-card {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  background: rgba(14, 14, 12, 0.035);
  border-radius: 16px;
  padding: 7px 14px;
}
#tour-multi-pages .tour-flow-transition-card .tour-flow-connector-num, #manrec-markers .tour-flow-transition-card .tour-flow-connector-num {
  margin-bottom: 12px;
}
/* The ↳ bridge glyph before the summary. */
#tour-multi-pages .tour-flow-transition-arrow, #manrec-markers .tour-flow-transition-arrow {
  flex: none;
  color: var(--ink32);
  font-size: 14px;
  line-height: 1;
}
/* Collapsible transition head: title + summary + Edit/Done in a row.
   Margin below the head only when a body follows (expanded). */
#tour-multi-pages .tour-flow-transition-head, #manrec-markers .tour-flow-transition-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
#tour-multi-pages .tour-flow-transition-card > .tour-flow-transition-head:not(:last-child), #manrec-markers .tour-flow-transition-card > .tour-flow-transition-head:not(:last-child) {
  margin-bottom: 14px;
}
#tour-multi-pages .tour-flow-transition-summary, #manrec-markers .tour-flow-transition-summary {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#tour-multi-pages .tour-flow-add-step, #manrec-markers .tour-flow-add-step {
  align-self: flex-start;
  margin-top: 4px;
}
#tour-multi-pages .tour-flow-transition-divider, #manrec-markers .tour-flow-transition-divider {
  height: 1px;
  background: rgba(14, 14, 12, 0.12);
  margin: 14px 0;
}
#tour-multi-pages .tour-flow-transition-link-row, #manrec-markers .tour-flow-transition-link-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  min-height: 28px;
}

/* ── Site Flow transition as a prominent dark "spine" node ─────────
   The transition is the main event — the link the pages hang from —
   not a faint connector. Dark node, large link name, with the connector
   lines running into it as the spine. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node {
  position: relative; /* anchor for the absolutely-pinned "Back" × */
  align-self: stretch;
  /* Light bridge (Mark): the collapsed connector now matches the page
     cards instead of being a heavy dark cut, so the chain reads calm.
     Paper ground (#FAFAFA) + the same hairline the stop cards use. */
  background: var(--paper);
  border-radius: 14px;
  padding: 16px 18px;
  border: 1px solid var(--bmed);
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04);
  color: var(--ink);
  min-width: 0;
}
/* Expanded = editing → switch the node to the standard light card so
   editing happens on the normal component system. The dark treatment
   stays for the collapsed bridge only, where it carries the journey's
   filmstrip rhythm (light pages, dark cuts). */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded {
  display: flex; flex-direction: column;
  /* Blend into the flyout — the flyout IS the card, so the node carries no
     fill, border, or shadow of its own (Mark: it was reading as a second
     card inside the main card). */
  background: transparent;
  color: var(--ink);
  box-shadow: none;
  border: 0;
}
/* Identity anchor across the flip: the disc inverts to solid dark, so
   the transition's badge persists when the card goes light. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-disc, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-disc {
  color: var(--ink65);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-eyebrow, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-eyebrow { color: var(--ink50); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-link, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-link { color: var(--ink); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-edit, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-edit { background: var(--btn2bg); color: var(--ink65); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-edit:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-edit:hover { background: var(--btn2bg-hover); color: var(--ink); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-row + .tour-flow-spine-row, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-row + .tour-flow-spine-row { border-top-color: var(--ink08); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-role, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-role { color: var(--ink65); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-role-note, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-role-note { color: var(--ink50); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-flink, :is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-fdest, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-flink, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-fdest { color: var(--ink); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-flink.is-empty, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-flink.is-empty { color: var(--ink32); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-wval, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-wval { color: var(--ink50); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-scrollnote, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-scrollnote {
  border-left-color: var(--bmed);
  background: var(--ink08);
  color: var(--ink65);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-addreveal-warn, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-addreveal-warn { color: var(--ink65); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-info, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-info { color: var(--ink50); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-menuwarn-txt, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-menuwarn-txt { color: var(--ink65); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head { display: flex; align-items: center; gap: 16px; min-width: 0; }
/* "Back" / cancel-adding × — pinned to the spine card's top-right corner.
   It's taken out of flow (absolute) because the head wraps and the prompt
   body is full-width, which otherwise pushed the × onto its own empty row
   below the prompt — leaving a tall dead band and a stranded × (Mark).
   Light ink for the dark spine card. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head .tour-flow-add-cancel, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head .tour-flow-add-cancel {
  position: absolute;
  top: 11px;
  right: 13px;
  margin: 0;
  flex: none;
  color: rgba(251, 250, 248, 0.7);
}
/* Keep the prompt clear of the pinned ×. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head:has(.tour-flow-add-cancel), :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head:has(.tour-flow-add-cancel) {
  padding-right: 24px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head .tour-flow-add-cancel:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head .tour-flow-add-cancel:hover { color: #FBFAF8; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-disc, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-disc {
  /* Same icon voice as the stop chips (Mark): a bare 14px glyph in ink65,
     no ring — the 38px bordered disc was the odd one out on the rail. */
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink65);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-disc svg, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-disc svg { width: 14px; height: 14px; display: block; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-body, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-body {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: 6px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-eyebrow, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-eyebrow {
  /* Card TITLE ("Transition", "Link to Page 02") — sans, not mono (Mark). */
  font-family: var(--sans); font-size: 11.5px; font-weight: 500;
  letter-spacing: 0; text-transform: none; color: var(--ink50);
  display: inline-flex; align-items: center; gap: 5px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-eyebrow-arrow, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-eyebrow-arrow { flex: none; color: var(--ink32); margin-top: -1px; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-link, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-link {
  font-size: 15px; letter-spacing: -0.01em; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-link.is-empty, :is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-flink.is-empty, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-link.is-empty, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-flink.is-empty { color: #8A8A85; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-edit, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-edit {
  flex: none; align-self: center; padding: 7px 14px;
  font-family: var(--sans); font-size: 12px; color: var(--ink65);
  /* Same corner radius as .ghost-btn — the old 100px stadium made
     these read as a different component from every other button. */
  background: var(--btn2bg); border: 0; border-radius: 8px;
  cursor: pointer; transition: background .12s ease, color .12s ease;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-edit:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-edit:hover { background: var(--btn2bg-hover); color: var(--ink); }
/* Pick = the required next action. Same pill shape and size as
   Edit/Done beside it, inverted to solid light so it reads as the
   one thing to do — consistent shape, stronger weight. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-edit.is-pick, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-edit.is-pick {
  background: rgba(251, 250, 248, 0.92); color: #1A1A18; font-weight: 500;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-edit.is-pick:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-edit.is-pick:hover {
  background: #FFFFFF;
}
/* Expanded — same head row as collapsed, controls stack below it. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-expand, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-expand {
  margin-top: 14px; display: flex; flex-direction: column; gap: 0;
}
/* ── Flow-map framing for the expanded transition (Mark): source page
   node → controls panel → destination node, so the link reads as a real
   step between two pages. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map, :is(#manrec-markers, .tour-flyout) .tour-flow-map { display: flex; flex-direction: column; margin-top: 10px; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-node, :is(#manrec-markers, .tour-flyout) .tour-flow-map-node {
  border: 1px solid var(--bmed); border-radius: 9px; padding: 6px 11px; background: var(--ink08);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-node.is-dest, :is(#manrec-markers, .tour-flyout) .tour-flow-map-node.is-dest { background: transparent; border-style: dashed; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-eyebrow, :is(#manrec-markers, .tour-flyout) .tour-flow-map-eyebrow {
  display: block; font-family: var(--mono); font-size: 8.5px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink50);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-name, :is(#manrec-markers, .tour-flyout) .tour-flow-map-name {
  display: block; font-size: 12.5px; color: var(--ink); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-conn, :is(#manrec-markers, .tour-flyout) .tour-flow-map-conn { width: 2px; height: 8px; background: var(--bmed); margin: 3px auto; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-panel, :is(#manrec-markers, .tour-flyout) .tour-flow-map-panel {
  /* Inner box dropped (Mark round 2): stop cards have no inset frame
     around their field rows, and the transition editor now shares their
     layout — the extra border read as a different design language. */
  margin-top: 0; border: 0; border-radius: 0; padding: 0;
}
/* Panel rows: tighter, and don't force-wrap in the narrow flyout. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-panel .tour-flow-spine-row, :is(#manrec-markers, .tour-flyout) .tour-flow-map-panel .tour-flow-spine-row { padding: 9px 0; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-panel .tour-flow-spine-rowctl, :is(#manrec-markers, .tour-flyout) .tour-flow-map-panel .tour-flow-spine-rowctl { min-width: 0; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-map-panel .tour-interaction-row, :is(#manrec-markers, .tour-flyout) .tour-flow-map-panel .tour-interaction-row {
  gap: 8px; flex-wrap: wrap; justify-content: flex-start;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-wsep, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-wsep { color: var(--ink32); flex: none; }
/* ── Clean stacked-label fields inside the transition panel (Mark): a
   small mono label over a single control row, hairline between fields —
   robust in the narrow flyout, no left-column wrapping. */
:is(#tour-multi-pages, .tour-flyout) .tf-field, :is(#manrec-markers, .tour-flyout) .tf-field { padding: 7px 0; }
/* Between-field hairlines dropped — stop cards don't have them, and the
   transition editor now shares their exact layout grammar (Mark). */
/* FLYOUT: label-LEFT grid, same as the stop cards' field rows — the
   stacked label-above version read as a different design (Mark). The
   narrow rail keeps stacked labels, exactly like rail stop cards do. */
.tour-flyout .tf-field {
  display: grid;
  /* minmax(0, 1fr), not bare 1fr — a 1fr track still respects the
     content's min-content floor, which is what let the long link path
     force the card wide. minmax(0, …) removes the floor. */
  grid-template-columns: 64px minmax(0, 1fr);
  column-gap: 10px;
  align-items: start;
}
.tour-flyout .tf-field .tf-field-lbl { grid-column: 1; margin-bottom: 0; padding-top: 5px; }
.tour-flyout .tf-field .tf-ctl,
.tour-flyout .tf-field .tf-hint { grid-column: 2; }
:is(#tour-multi-pages, .tour-flyout) .tf-field-lbl, :is(#manrec-markers, .tour-flyout) .tf-field-lbl {
  /* Same sans title token as .tour-field-label — one label voice. */
  font-family: var(--sans); font-size: 11.5px; font-weight: 500;
  letter-spacing: 0; text-transform: none; color: var(--ink50); margin-bottom: 5px;
}
/* min-width: 0 is the load-bearing part — grid/flex items default to a
   min-content floor, so a long mono path (→ /collections/templates) held
   the whole card at "one character from breaking" width. Zeroing the
   floor lets the readout box take the available width and ellipsize the
   path instead (Mark). */
:is(#tour-multi-pages, .tour-flyout) .tf-ctl, :is(#manrec-markers, .tour-flyout) .tf-ctl { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
:is(#tour-multi-pages, .tour-flyout) .tf-ctl > .picked-target-display, :is(#manrec-markers, .tour-flyout) .tf-ctl > .picked-target-display { flex: 1 1 0; min-width: 0; }
:is(#tour-multi-pages, .tour-flyout) .tf-ctl + .tf-ctl, :is(#manrec-markers, .tour-flyout) .tf-ctl + .tf-ctl { margin-top: 6px; }
/* Compact Click/Hover segment on its own row (Mark: the default seg was
   oversized for this panel). */
/* Two-step reveal: quiet mono step numbers when the sequence has 2 rows. */
:is(#tour-multi-pages, .tour-flyout) .tf-step-n, :is(#manrec-markers, .tour-flyout) .tf-step-n {
  flex: none; font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.08em;
  color: var(--ink50); width: 12px; text-align: center;
}
:is(#tour-multi-pages, .tour-flyout) .tf-ctl-seg .pill-select.seg, :is(#manrec-markers, .tour-flyout) .tf-ctl-seg .pill-select.seg { flex: none; padding: 2px; gap: 2px; }
:is(#tour-multi-pages, .tour-flyout) .tf-ctl-seg .pill-select.seg .pill, :is(#manrec-markers, .tour-flyout) .tf-ctl-seg .pill-select.seg .pill { font-size: 11px; padding: 4px 13px; }
:is(#tour-multi-pages, .tour-flyout) .tf-name, :is(#manrec-markers, .tour-flyout) .tf-name {
  font-size: 13px; color: var(--ink); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:is(#tour-multi-pages, .tour-flyout) .tf-link, :is(#manrec-markers, .tour-flyout) .tf-link {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.02em; color: var(--ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:is(#tour-multi-pages, .tour-flyout) .tf-link.is-empty, :is(#manrec-markers, .tour-flyout) .tf-link.is-empty { color: var(--ink50); }
/* Tail-first truncation (Mark: the END of a path is the important bit).
   dir="rtl" moves the ellipsis to the START; the &lrm; guards in the
   markup keep the latin path rendering left-to-right inside. The arrow
   sits outside the truncating span so it can't be eaten. */
:is(#tour-multi-pages, .tour-flyout) .tf-link-tail, :is(#manrec-markers, .tour-flyout) .tf-link-tail { text-align: left; }
:is(#tour-multi-pages, .tour-flyout) .tf-link-arrow, :is(#manrec-markers, .tour-flyout) .tf-link-arrow { flex: none; font-family: var(--mono); font-size: 12px; color: var(--ink); }
:is(#tour-multi-pages, .tour-flyout) .tf-muted, :is(#manrec-markers, .tour-flyout) .tf-muted { font-size: 12px; color: var(--ink50); }
:is(#tour-multi-pages, .tour-flyout) .tf-hint, :is(#manrec-markers, .tour-flyout) .tf-hint { font-size: 11.5px; color: var(--ink50); line-height: 1.4; margin-bottom: 8px; }
:is(#tour-multi-pages, .tour-flyout) .tf-sp, :is(#manrec-markers, .tour-flyout) .tf-sp { flex: 1; min-width: 8px; }
/* Expanded node rows — the same label-column grid as the light cards
   (REVEAL / LINK / WAIT), separated by quiet hairlines instead of
   recessed boxes. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-row, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-row {
  display: flex;
  align-items: center;
  gap: 6px 14px;
  flex-wrap: wrap;
  padding: 12px 0;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-row + .tour-flow-spine-row, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-row + .tour-flow-spine-row {
  border-top: 1px solid rgba(251, 250, 248, 0.08);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-role, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-role {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: #8A8A85;
  flex: 0 0 64px;
  display: inline-flex; align-items: center; gap: 6px;
  flex-wrap: wrap;
}
/* Verification note tucked under the Reveal label in the role column —
   aligned with the grid instead of floating in the control area. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-role-note, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-role-note {
  flex-basis: 100%;
  font-size: 9px; letter-spacing: 0.08em;
  color: #C7C789;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-rowctl, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-rowctl {
  flex: 1; min-width: 240px;
  display: flex; flex-direction: column; gap: 8px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-rowctl.is-inline, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-rowctl.is-inline {
  flex-direction: row; align-items: center; gap: 10px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-reveal, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-reveal {
  border-left: 2px solid #3A3A38;
  background: rgba(255, 255, 255, 0.025);
  border-radius: 0 8px 8px 0;
  padding: 10px 12px; margin-bottom: 14px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-reveal-head, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-reveal-head {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: #8A8A85; margin-bottom: 8px;
  display: flex; align-items: center; gap: 8px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-tag, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-tag {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.1em;
  color: #B7B5AD; border: 0.5px solid #3A3A38; border-radius: 5px;
  padding: 2px 6px; text-transform: uppercase;
}
/* End-of-flow affordance — the same dashed "+ Add …" grammar as
   + Add pause and Page Clips' + Add clip (Mark: the old card + circular
   plus was big and unlike anything else). Full-width in the rail. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-end-add, :is(#manrec-markers, .tour-flyout) .tour-flow-end-add {
  align-self: stretch;
  width: 100%;
  display: flex; align-items: center; justify-content: center;
}
#tour-multi-pages .tour-flow-end-note, #manrec-markers .tour-flow-end-note { font-size: 13px; color: var(--ink-mute, #8A8A85); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-tag-auto, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-tag-auto { color: #C7C789; border-color: rgba(199, 199, 137, 0.4); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-reveal-auto, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-reveal-auto { font-size: 12.5px; color: #A4A29B; margin-bottom: 10px; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-addreveal, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-addreveal { margin-bottom: 14px; }
/* In-menu prompt — amber-flagged note under the collapsed spine head,
   guiding the user to add a reveal when the link is inside a dropdown. */
/* Reveal prompt (Mark): on-brand neutral card — a white inset with a
   hairline, readable ink copy, the bent-arrow reveal glyph, and a solid
   dark primary action. Replaces the old pale-yellow-on-yellow box that
   couldn't be read. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-menuwarn, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-menuwarn {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 12px 0 2px; padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--bmed);
  background: var(--card-fill);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-menuwarn-icon, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-menuwarn-icon {
  flex: none; color: var(--ink50);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-menuwarn-txt, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-menuwarn-txt {
  flex: 1; min-width: 150px;
  font-family: var(--sans); font-size: 11.5px; line-height: 1.45;
  color: var(--ink65);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-menuwarn-btn, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-menuwarn-btn {
  flex: none;
  font-family: var(--sans); font-size: 12px; font-weight: 500;
  color: #FBFAF8; background: var(--ink);
  border: 0; border-radius: 8px; padding: 7px 13px; cursor: pointer;
  transition: background .12s ease;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-menuwarn-btn:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-menuwarn-btn:hover { background: #34322D; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-addreveal-warn, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-addreveal-warn {
  display: block; margin-bottom: 9px;
  font-family: var(--sans); font-size: 12px; line-height: 1.45;
  color: #E4D8B0;
}
/* Quieter sibling of the warn line — the optional "add a reveal if the link
   is behind a menu" hint shown when auto-detection didn't flag a menu. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-addreveal-hint, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-addreveal-hint {
  display: block; margin-bottom: 9px;
  font-family: var(--sans); font-size: 12px; line-height: 1.45;
  color: #8A8A85;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-addreveal-hint, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node.is-expanded .tour-flow-spine-addreveal-hint { color: var(--ink50); }
/* Info marker — small ⓘ with an instant CSS tooltip (native title has a
   ~1.5s delay). Help cursor signals it's hoverable. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-info, :is(#manrec-markers, .tour-flyout) .tour-flow-info {
  position: relative;
  font-size: 12px; color: #8A8A85; cursor: help;
  vertical-align: 1px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-info::after, :is(#manrec-markers, .tour-flyout) .tour-flow-info::after {
  content: attr(data-tip);
  position: absolute; left: 0; bottom: calc(100% + 8px);
  width: 230px; padding: 8px 10px;
  background: #0E0E0C; color: #E4E2DC;
  border: 0.5px solid #3A3A38; border-radius: 8px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35);
  font-family: var(--sans); font-size: 11.5px; font-weight: 400;
  letter-spacing: 0; text-transform: none; line-height: 1.45;
  white-space: normal; text-align: left;
  opacity: 0; visibility: hidden; transform: translateY(3px);
  transition: opacity 0.1s ease, transform 0.1s ease;
  pointer-events: none; z-index: 60;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-info:hover::after, :is(#tour-multi-pages, .tour-flyout) .tour-flow-info:focus-visible::after, :is(#manrec-markers, .tour-flyout) .tour-flow-info:hover::after, :is(#manrec-markers, .tour-flyout) .tour-flow-info:focus-visible::after {
  opacity: 1; visibility: visible; transform: translateY(0);
}
/* Scroll note — neutral info line inside the spine card (dark), matching
   how the reveal note sits in the card. Not a warning, so no amber. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-scrollnote, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-scrollnote {
  margin-top: 12px; padding: 9px 12px;
  border-left: 2px solid #3A3A38;
  background: rgba(255, 255, 255, 0.025);
  border-radius: 0 8px 8px 0;
  font-family: var(--sans); font-size: 12px; line-height: 1.45;
  color: #A4A29B;
}
/* URL option — collapsed <details> offering a typed URL as an alternative
   to picking a link, on empty transitions. Quiet on light card surfaces. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-url-option, :is(#manrec-markers, .tour-flyout) .tour-flow-url-option { margin-top: 8px; }
/* On the empty spine node the URL option now sits BELOW the head row,
   full width, indented to the text column (disc 38px + 16px gap) so it
   reads as a quiet footnote to "Pick the link" rather than squeezing
   the head. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node > .tour-flow-url-option, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node > .tour-flow-url-option {
  margin: 12px 0 2px 54px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-url-option > summary, :is(#manrec-markers, .tour-flyout) .tour-flow-url-option > summary {
  font-family: var(--sans); font-size: 12px; color: #5F5E59;
  cursor: pointer; list-style: none; display: inline-flex; align-items: center;
  text-decoration: underline; text-decoration-color: rgba(95, 94, 89, 0.4);
  text-underline-offset: 3px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-url-option > summary:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-url-option > summary:hover { text-decoration-color: currentColor; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-url-option > summary::-webkit-details-marker, :is(#manrec-markers, .tour-flyout) .tour-flow-url-option > summary::-webkit-details-marker { display: none; }
#tour-multi-pages .tour-flow-url-row, #manrec-markers .tour-flow-url-row {
  display: flex; gap: 6px; align-items: center; margin-top: 8px;
}
#tour-multi-pages .tour-flow-url-row .text-input, #manrec-markers .tour-flow-url-row .text-input { flex: 1; min-width: 0; }
/* Light bridge: the URL fallback summary reads as a quiet ink link. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-flow-url-option > summary, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-flow-url-option > summary { color: var(--ink65); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-flow-url-option > summary::before, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-flow-url-option > summary::before { color: var(--ink50); }
/* ── Light bridge — empty "pick a link" prompt: eyebrow + outlined
   primary, with quiet pick-a-button / enter-a-URL fallbacks (Mark). */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head.is-pickprompt, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head.is-pickprompt { align-items: flex-start; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head.is-pickprompt .tour-flow-spine-disc, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head.is-pickprompt .tour-flow-spine-disc { margin-top: 1px; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head.is-pickprompt .tour-flow-spine-body, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head.is-pickprompt .tour-flow-spine-body { gap: 0; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-pick, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-pick {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%; margin-top: 7px; padding: 9px 12px;
  font-family: var(--sans); font-size: 12.5px; font-weight: 500; color: var(--ink);
  background: transparent; border: 1px solid var(--bstr); border-radius: 8px;
  cursor: pointer; transition: background .12s ease, border-color .12s ease;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-pick:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-pick:hover { background: var(--ink08); border-color: var(--ink32); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-pick svg, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-pick svg { flex: none; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-altrow, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-altrow {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 5px;
  margin-top: 9px; font-size: 11.5px; color: var(--ink50);
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-altsep, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-altsep { color: var(--ink32); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-altlink, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-altlink {
  background: none; border: 0; padding: 0; cursor: pointer; font-family: var(--sans);
  font-size: 11.5px; color: var(--ink65); line-height: 1.2;
  text-decoration: underline; text-decoration-color: var(--bstr); text-underline-offset: 3px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-altlink:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-altlink:hover { color: var(--ink); text-decoration-color: currentColor; }
/* Inline URL disclosure that lives on the fallback line and expands to a
   full-width input row when opened. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-url-inline, :is(#manrec-markers, .tour-flyout) .tour-flow-url-inline { display: inline-block; margin: 0; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-url-inline[open], :is(#manrec-markers, .tour-flyout) .tour-flow-url-inline[open] { display: block; flex-basis: 100%; width: 100%; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-url-inline > summary, :is(#manrec-markers, .tour-flyout) .tour-flow-url-inline > summary { font-size: 11.5px; }
/* ── Resolved row — mirrors a stop card head (Page N · dest · Edit · ×)
   so the controls sit and space exactly like the other cards (Mark). The
   head carries no gap; children use their own margins, same as a stop;
   the dest reuses .tour-section-label so a long URL truncates cleanly. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head.is-resolved, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head.is-resolved { gap: 0; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head.is-resolved .tour-flow-spine-disc, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head.is-resolved .tour-flow-spine-disc { margin-right: 12px; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-rmain, :is(#manrec-markers, .tour-flyout) .tour-flow-rmain { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-rline, :is(#manrec-markers, .tour-flyout) .tour-flow-rline { display: flex; align-items: baseline; min-width: 0; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-rsub, :is(#manrec-markers, .tour-flyout) .tour-flow-rsub { font-size: 11px; color: var(--ink50); line-height: 1.2; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-follow, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-follow { display: block; }
/* Role label is a full-width eyebrow above; the link name + destination
   and the Show/Change/× actions share a row that centres them against
   each other, so the actions read as inline with the link. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-follow-row, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-follow-row { display: flex; align-items: center; gap: 16px; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-follow-main, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-follow-main { flex: 1; min-width: 0; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-frole, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-frole {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; color: #8A8A85; margin-bottom: 7px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-flink, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-flink {
  font-size: 18px; letter-spacing: -0.01em; color: #FBFAF8; line-height: 1.15;
  overflow: hidden; text-overflow: ellipsis;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-fdest, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-fdest {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em;
  color: #A4A29B; margin-top: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-follow-actions, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-follow-actions { display: flex; gap: 6px; align-items: center; flex: none; }
/* .tour-flow-spine-wait now rides the shared .tour-flow-spine-row grid
   (label column + hairline separators) — no standalone box styling. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-wrole, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-wrole {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.13em;
  text-transform: uppercase; color: #8A8A85;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-wval, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-wval { font-size: 13px; color: #A4A29B; }
/* Inner controls re-themed for the dark node */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .ghost-btn.is-quiet, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .ghost-btn.is-quiet { color: #B7B5AD; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .ghost-btn.is-quiet:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .ghost-btn.is-quiet:hover { color: #FBFAF8; }
/* Bridge is light now: the base ghost-btn (identical to the stop cards'
   Edit) applies unchanged — no dark-node override. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-stepper, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-stepper { background: rgba(255,255,255,0.06); border-color: rgba(255,255,255,0.18); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-stepper input, :is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-step, :is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-step-unit, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-stepper input, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-step, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .num-step-unit { color: #FBFAF8; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .wait-inline, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .wait-inline { color: #A4A29B; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .selector-clear-btn, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .selector-clear-btn { color: var(--ink50); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .selector-clear-btn:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .selector-clear-btn:hover { color: var(--ink); }
/* Bridge is light: the base .tour-section-remove (ink50 / hover ink + pill)
   applies, matching the stop cards' × exactly. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-interaction-target-name, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-interaction-target-name { color: #CFCDC6; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .pill-select.seg, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .pill-select.seg { border-color: rgba(255, 255, 255, 0.18); }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .pill-select.seg .pill, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .pill-select.seg .pill { color: #B7B5AD; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .pill-select.seg .pill.is-selected, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .pill-select.seg .pill.is-selected { background: #FBFAF8; color: #1A1A18; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-interaction-num, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-node:not(.is-expanded) .tour-interaction-num { color: #8A8A85; }
#tour-multi-pages .tour-flow-link-actions, #manrec-markers .tour-flow-link-actions {
  display: flex;
  gap: 6px;
  align-items: center;
  margin-left: auto;
}
/* URL fallback hint + outside-viewport warning when they appear
   inside the connector — match the wrapper's full-width stretch so
   they don't pinch to the centered-column width of the lines. */
#tour-multi-pages .tour-flow-connector > .hint, #manrec-markers .tour-flow-connector > .hint {
  align-self: stretch;
  margin-top: 8px;
}
/* Soft guidance under the empty transition pill: tells users the
   v1 constraint (visible targets only) without being alarmist. */
#tour-multi-pages .tour-flow-connector-hint, #manrec-markers .tour-flow-connector-hint {
  align-self: stretch;
  margin-top: 8px;
  padding: 0 4px;
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: var(--text-muted, #6b6b6b);
  text-align: center;
}
/* Matches tour-section-num typography (mono 12px, ink50, tabular).
   Reads as "this is the bridge between page N and N+1" in the same
   visual vocabulary as the stop badges. */
#tour-multi-pages .tour-flow-connector-num, #manrec-markers .tour-flow-connector-num {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.12em;
  color: var(--ink50);
  text-transform: none;
  flex: none;
  white-space: nowrap;
}
/* Matches tour-section-label — serif feel, full ink, ellipsis on
   long targets so the buttons stay on the right edge. */
#tour-multi-pages .tour-flow-connector-target, #manrec-markers .tour-flow-connector-target {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 400;
  color: var(--ink);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Empty-state copy in the bridge box reads softer than a picked
   target chip — italic-style cue that the user still has work to do
   here. Stays in the same row layout so the Pick button sits inline. */
#tour-multi-pages .tour-flow-connector-target.tour-flow-connector-empty, #manrec-markers .tour-flow-connector-target.tour-flow-connector-empty {
  color: var(--ink50);
}
/* Transition buttons inherit the default ghost-btn sizing so they
   match the stop card's EDIT button exactly. Used to be overridden
   to a smaller scale; the inconsistency read as sloppy next to the
   matching card styling. */
#tour-multi-pages .tour-flow-connector .ghost-btn, #manrec-markers .tour-flow-connector .ghost-btn {
  flex: none;
}
#tour-multi-pages .tour-flow-connector .selector-clear-btn, #manrec-markers .tour-flow-connector .selector-clear-btn {
  flex: none;
}

/* In-card transition row — the "next action" affordance, shown
   inside the page card when no transition is picked yet (or when
   the picked target is a non-anchor needing a URL). Lighter divider
   than the previous dashed border-top since the container's border
   carries the surrounding shape. */
#tour-multi-pages .tour-transition, #manrec-markers .tour-transition {
  border-top: 1px solid var(--ink08);
}

/* Right preview storyboard — per-page group on multi-page tours.
   Lighter weight than the panel headers since this is a summary view. */
.preview-tour-group {
  margin-top: 8px;
}
.preview-tour-group:first-child {
  margin-top: 0;
}
.preview-tour-group-header {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 4px 0 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--bweak);
}
.preview-tour-group-num {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink50);
}
.preview-tour-group-host {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--ink65);
  text-transform: uppercase;
}

/* Transition link between pages in the Site Flow storyboard — the
   link Capt clicks (or auto-load wait) to move to the next page.
   Reads as a connector below a page's stops. */
.preview-transition-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 8px 0 2px;
  padding: 6px 0 6px 4px;
  border-left: 2px solid var(--bweak);
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink65);
}
.preview-transition-row.is-empty {
  color: var(--ink50);
  font-style: italic;
}
.preview-transition-icon {
  color: var(--ink50);
  font-size: 12px;
  line-height: 1;
}
.preview-transition-label {
  flex: 1;
  min-width: 0;
}

/* ---------- Recording button state ----------
   Monochrome system: the recording state shows ink-on-paper rather
   than a red accent. The pulsing dot reads as "live" without a
   chromatic cue. */
.btn-primary.is-recording {
  background: var(--ink);
  color: var(--paper);
  position: relative;
}
.btn-primary.is-recording::before {
  content: '';
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--paper);
  margin-right: 10px;
  animation: capt-pulse 1s ease-in-out infinite;
  vertical-align: middle;
}
@keyframes capt-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* ---------- Sidebar Stills/Video separator ---------- */
.sidebar-group .sidebar-label + .sidebar-label,
.sidebar-group .sidebar-label.with-rule {
  position: relative;
  margin-top: 28px;
  padding-top: 22px;
}

/* Guide nav is the longest rail (a page per mode, four sections), so it
   was overflowing into an internal scrollbar. Tighten its row + section
   spacing so the whole guide nav fits the rail without scrolling. */
[data-group="guide"] .side-item { padding-top: 4px; padding-bottom: 4px; }
[data-group="guide"] .sidebar-label { margin-bottom: 6px; }
[data-group="guide"] .sidebar-label.with-rule { margin-top: 12px; padding-top: 10px; }
[data-group="guide"] .side-back { margin-bottom: 8px; }
.sidebar-group .sidebar-label.with-rule::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 14px;
  height: 1px;
  background: var(--ink08);
}

/* ---------- Source bar ----------
   Lives in the topbar's centre slot now (the Capture/History tabs
   were dropped — the Library group moved into the sidebar permanently
   alongside Stills + Video). Centred horizontally so the pill sits
   between the brand on the left and the utility cluster on the
   right. */
.source-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  position: relative;
  /* The topbar's centre track is `auto`, so the source bar takes its
     intrinsic width and the surrounding `1fr` tracks soak up the rest
     of the row. No padding here — the topbar already pads the row. */
}

/* Wrapper around the pill so the picker dropdown anchors directly
   under the pill rather than the whole bar. */
.source-pill-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

/* The source pill itself. Soft filled background, rounded fully,
   reads as a tangible "you are connected to this page" element
   rather than a quiet text strip. */
.source-pill {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 10px 10px 10px 18px;
  /* Blend into the top bar (redesign, Mark): match the bar's own background
     and give it just a hairline + a whisper of shadow, instead of a filled
     grey chip. Uses --bg (= the topbar bg) so it blends in light AND dark. */
  background: var(--bg);
  border: 1px solid var(--border);
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04);
  /* Rounded-rect (10px) to match the mode switcher, not a full pill. */
  border-radius: 10px;
  /* Cap the pill so a long address collapses (fitSourceAddress) instead of
     pushing toward the mode switcher / utilities; shrinks with the viewport. */
  max-width: min(600px, calc(100vw - 460px));
}
/* (Black pill experiment reverted 2026-06-05 — Mark: "not liking it".
   Text/favicon/Change colours fall back to their base ink rules.) */

.source-pill-favicon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: 100px;
  background: var(--ink08) center/cover no-repeat;
}
/* Empty state: dim circle, no favicon image. */
.source-bar.is-empty .source-pill-favicon {
  background: var(--ink08);
}
.source-bar[hidden] { display: none; }

.source-meta {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
/* Tri-tone mono address (spec): the domain anchors dark, the middle path
   recedes, the final page is set a shade stronger as the part you read. The
   collapse-to-fit logic lives in fitSourceAddress(). */
.source-addr {
  flex: 1; min-width: 0; overflow: hidden; white-space: nowrap;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.02em;
}
.source-domain { color: var(--ink); }
.source-domain[hidden] { display: none; }
.source-path { color: var(--ink32); }
.source-page { color: var(--ink-soft); }
.source-bar.is-empty .source-domain { color: var(--ink50); }

/* Change: a quiet text action — sans medium ink-soft + chevron, no pill. (The
   border existed to stop it twinning with the old SOURCE label, which is gone,
   and a full pill inside the rounded-rect bar read as a shape mismatch.) */
.source-change {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-soft);
  padding: 4px 2px 4px 6px;
  margin-left: 2px;
  background: transparent;
  border: 0;
  flex-shrink: 0;
  transition: color .15s ease;
  cursor: pointer;
}
.source-change:hover { color: var(--ink); }
.source-change svg { transform: rotate(90deg); transition: transform .15s ease; }
.source-change.is-open svg { transform: rotate(-90deg); }

/* Picker dropdown */
.source-picker {
  position: absolute;
  /* Centred under the source pill (Mark) — was right-aligned to the Change
     button, which read as off-centre. translateX(-50%) centres the 460px
     panel on the pill's midpoint. */
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  width: 460px;
  max-width: calc(100vw - 48px);
  max-height: 420px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08);
  z-index: 50;
}
.source-picker[hidden] { display: none; }

.picker-header {
  font-family: var(--sans);
  font-size: 11.5px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: -0.005em;
  color: var(--ink65);
  padding: 14px 18px 8px;
  border-bottom: 1px solid var(--ink08);
}
.picker-list { display: flex; flex-direction: column; padding: 4px 0; }

.picker-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  width: 100%;
  text-align: left;
  transition: background .15s ease;
  cursor: pointer;
}
.picker-item:hover { background: var(--pill); }

.picker-favicon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: 2px;
  background: var(--ink08) center/cover no-repeat;
}
.picker-favicon-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  color: var(--ink65);
  background: var(--ink08);
}
.picker-text { min-width: 0; flex: 1; }
.picker-title {
  font-size: 12px;
  font-weight: 400;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 2px;
}
.picker-url {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  color: var(--ink50);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.picker-empty {
  padding: 14px 18px 18px;
  font-size: 11px;
  font-weight: 400;
  color: var(--ink50);
  line-height: 1.6;
}

/* ---------- Scrollbar (subtle) ---------- */
.sidebar::-webkit-scrollbar,
.main::-webkit-scrollbar { width: 6px; }
.sidebar::-webkit-scrollbar-thumb,
.main::-webkit-scrollbar-thumb { background: var(--ink18); border-radius: 100px; }
.sidebar::-webkit-scrollbar-thumb:hover,
.main::-webkit-scrollbar-thumb:hover { background: var(--ink32); }
.sidebar::-webkit-scrollbar-track,
.main::-webkit-scrollbar-track { background: transparent; }

/* Two-column grid for switch-only option rows (Page Clips Motion).
   Each cell keeps the standard option-row padding/border; the grid
   just halves the vertical run of four stacked switches. */
.opt-toggle-grid { display: grid; grid-template-columns: 1fr 1fr; column-gap: 28px; }
/* No per-row borders inside the grid — with two columns the row
   borders only underlined one half (whichever cell was shorter),
   which read as broken. The grid is one visual block. */
.opt-toggle-grid .option-row { min-width: 0; border-bottom: 0; }

/* Quick capture set — the three sizes as a related row of thumbnails. */
.quick-set-row { display: flex; gap: 8px; margin-top: 10px; }
.quick-set-tile {
  flex: 1; min-width: 0; border: 0; background: transparent;
  display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 0;
}
.quick-set-tile img {
  width: 100%; height: 76px; object-fit: cover; object-position: top;
  border-radius: 7px; border: 1px solid var(--ink08);
  background: var(--ink08);
}
.quick-set-tile:hover img { border-color: var(--ink32); }
.quick-set-cap {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink50);
}

/* Shared capture-action icons (download / copy) — consistent across
   every preview panel (latest capture, quick set, asset cards). */
.capt-icon-actions { display: inline-flex; gap: 6px; align-items: center; }
.capt-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0; cursor: pointer;
  border: 1px solid var(--ink15, rgba(14,14,12,0.15)); border-radius: 8px;
  background: transparent; color: var(--ink65);
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.capt-icon-btn:hover { background: var(--ink08); color: var(--ink); border-color: var(--ink32); }

/* ---------- Preview Lab (TEST MODE) ----------
   Isolated styles for the embedded-preview proof of concept. The
   canvas hosts the source page in an iframe at TRUE device pixels,
   wrapped in a scaler that shrinks it to fit the column — coordinates
   stay real, only the display scales. */
.lab-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 4px 0 12px;
}
.lab-status {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink50);
}
.lab-canvas {
  position: relative; /* anchors the tour-preview pick banner */
  border: 1px solid var(--bmed);
  border-radius: 14px;
  overflow: hidden;
  background: var(--card-fill);
}
/* Dark mode: the preview frame + its faux-browser top bar sit on the
   canvas so they read as part of the workspace, the same as the left and
   right columns — not lifted like a card (Mark). The card-fill lift was
   making the preview top bar lighter than the columns around it. */
.app.dark .lab-canvas { background: var(--canvas); }
/* Shell (idea #1): the preview is a full-width stage with the device scaled +
   centred inside, so the well BEHIND the device now shows. Make it the grey app
   canvas, not white, to match the mockup; keep the faux-browser top bar on paper
   so it still reads as a bar. (Dark mode already resolves --canvas/--bg to dark,
   so this is a no-op there.) */
/* No fake-browser window (Mark): drop the frame outline + rounded corners. The
   canvas is just the grey stage; the device sits on it, the toolbar floats above
   it as a SEPARATE panel (not touching, no shared box). */
.app.shell-modetop .lab-canvas { background: var(--canvas); border: 0; border-radius: 0; overflow: visible; }
/* Toolbar = a COMPACT floating panel: only as wide as its controls, centred
   above the device, with a gap before it (Mark). */
.app.shell-modetop .lab-canvas .lab-chrome {
  width: fit-content; max-width: 100%; margin: 0 auto 16px;
  height: auto; min-height: 0; padding: 5px 8px; gap: 4px;
  /* Match the top bar / Source pill surface (--paper), not the brighter pure
     white --card-fill (Mark). Light = #FAFAFA; dark = #222220 (a lift over the
     dark stage so it stays visible). */
  background: var(--paper); border: 0; border-radius: 10px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04), 0 6px 18px rgba(14, 14, 12, 0.06);
}
.app.dark.shell-modetop .lab-canvas .lab-chrome { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); }
/* ── Live preview frame (experiment, flag-gated, Viewport mode only) ──────────
   #tp-frame wraps the device; applyPreviewFrame() sets padding/background inline
   to mirror the stills Image-output framing live in the preview. When the
   feature is off the wrapper is display:contents (set inline), so the device
   centres exactly as it did before — the flag-off path is unchanged. */
.app.shell-modetop .tp-frame {
  width: fit-content; margin: 0 auto; box-sizing: content-box;
  /* No transition on padding: the slider recomputes the device's fit-scale
     instantly each input, so an animated padding made the background field lag
     the device and read as jitter while dragging. Padding now tracks the slider
     1:1 (like the Layouts board, which is smooth). Colour still fades. */
  transition: background-color .18s ease;
}
/* Transparent background → faint checkerboard in the padding area, so the
   padding amount stays visible and reads as "transparent". */
.app.shell-modetop .tp-frame.is-transparent {
  background-color: transparent;
  background-image:
    linear-gradient(45deg, rgba(14, 14, 12, .06) 25%, transparent 25%, transparent 75%, rgba(14, 14, 12, .06) 75%),
    linear-gradient(45deg, rgba(14, 14, 12, .06) 25%, transparent 25%, transparent 75%, rgba(14, 14, 12, .06) 75%);
  background-size: 16px 16px; background-position: 0 0, 8px 8px;
}
.app.dark.shell-modetop .tp-frame.is-transparent {
  background-image:
    linear-gradient(45deg, rgba(255, 255, 255, .05) 25%, transparent 25%, transparent 75%, rgba(255, 255, 255, .05) 75%),
    linear-gradient(45deg, rgba(255, 255, 255, .05) 25%, transparent 25%, transparent 75%, rgba(255, 255, 255, .05) 75%);
}

/* ═══ Layouts mode (multi-capture board builder, flag-gated) ═══════════════ */
.layouts-stage {
  position: relative; /* anchors the prep pill overlay */
  min-height: calc(100vh - 122px); max-height: calc(100vh - 122px); overflow: hidden;
  display: flex; flex-direction: column;
  /* Top-aligned like the other modes' canvas (lab-canvas): the toolbar pins
     near the top — only the main's 10px padding sits above it — and the plate
     flows below. (Was justify-content:center, which floated the toolbar to the
     vertical middle and made the top space far larger than the other modes.)
     Flush grey + radius 0 so it reads as the same full-bleed stage, not an
     inset panel. */
  align-items: center; justify-content: flex-start; gap: 16px;
  background: var(--canvas); border-radius: 0; padding: 0;
}
/* The plate's scroll viewport: a flex box filling the space below the toolbar.
   Its single child (the plate) centres via margin:auto and the box scrolls when
   the plate is bigger (zoomed in) — the toolbar stays pinned above. */
.layouts-scroller {
  flex: 1 1 auto; min-width: 0; min-height: 0; width: 100%;
  display: flex; overflow: hidden;
  /* `safe center` centres the plate when it fits but switches to start-align the
     moment it overflows — so a zoomed board stays scrollable on BOTH axes (plain
     centring/margin:auto leaves the start edge of the overflow unreachable). */
  justify-content: safe center; align-items: safe center;
}
.preview-panel .pill-select { flex-wrap: wrap; }
/* Canvas toolbar — floating pill above the plate. Matches the other modes'
   canvas toolbar (.lab-chrome in shell-modetop): same paper surface, 12px
   radius, shadow, compact 5px/10px padding and sans text. min-height keeps it
   the same bar height as the button-driven lab-chrome. */
/* Same box as the other modes' canvas toolbar (.lab-chrome in shell-modetop):
   5px/8px padding, content-driven height — no forced min-height that would float
   the content and read as extra padding. */
.layouts-toolbar { display: flex; align-items: center; gap: 9px; width: fit-content; max-width: 100%;
  padding: 5px 15px; background: var(--paper); border: 0; border-radius: 10px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, .04), 0 6px 18px rgba(14, 14, 12, .06); }
.layouts-toolbar .lt-seg { display: flex; align-items: center; gap: 5px; white-space: nowrap; }
.layouts-toolbar .lt-mono { font-family: var(--sans); font-size: 12px; letter-spacing: 0; color: var(--ink50); }
.layouts-toolbar .lt-status { color: var(--ink-mute); }
.layouts-toolbar .lt-div { width: 1px; height: 16px; background: var(--bmed); }
/* Device-size picker in the canvas toolbar (fixed templates): a quiet cat label
   + a compact native select that matches the toolbar's mono/quiet tone. (Mark) */
/* "Animation", the key to the motion button's value (Mark, 10 Aug 2026). It is
   a LABEL, not content, so it takes the same quiet tone as .lt-dev-cat — the
   bar's existing idiom for naming the control beside it — and sits tighter to
   its button than the bar's 9px rhythm, so the two read as one thing. */
.layouts-toolbar .lt-anim-lbl { color: var(--ink-mute); margin-right: -4px; }
.layouts-toolbar .lt-dev { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.layouts-toolbar .lt-dev-cat { font-family: var(--sans); font-size: 12px; color: var(--ink-mute); letter-spacing: 0; }
.layouts-toolbar .lt-dev-sel { font-family: var(--sans); font-size: 12px; color: var(--ink); background: transparent; border: 1px solid var(--bmed); border-radius: 7px; padding: 4px 6px; cursor: pointer; max-width: 150px; }
.layouts-toolbar .lt-dev-sel:hover { border-color: var(--ink); }
/* Value + unit inside the shared .num-stepper pill — body font (not mono), a
   touch shorter than the default input, with clear space between the number and
   its unit (the global unit hugs with a -3px margin, which jammed a word unit).
   NOT scoped to .layouts-toolbar any more (Mark, 4 Aug 2026: "+- pill is not
   looking consistent"). .num-step-val is emitted in exactly two places, both
   Layouts — layoutsStepper() and the toolbar's zoom stepper — but one of those
   renders in the SIDE PANEL, where the toolbar scope never reached it, so the
   Screens/Columns/Rows steppers came out with no padding and no size while the
   zoom stepper beside them looked right. Same markup has to mean the same pill
   wherever it lands.
   The unit is keyed off `.num-step-val +` on purpose rather than being unscoped
   too: .num-step-unit is ALSO minted generically by the stepper enhancer for any
   input carrying data-unit ("− 2.5 s +"), and that one is deliberately mono/10px
   because it qualifies an editable number. Only the unit that follows a
   read-only value span is ours. */
.num-step-val { align-self: center; font-family: var(--sans); font-size: 13px; color: var(--ink); padding: 6px 0 6px 9px; text-align: center; }
/* the board's zoom figure is a button: press it for 100% (12 Sep 2026) */
button.lt-zoom-val { background: none; border: 0; cursor: pointer; font: inherit; font-size: 13px; color: var(--ink); line-height: inherit; }
button.lt-zoom-val:hover { text-decoration: underline; text-underline-offset: 3px; }
.num-step-val + .num-step-unit { font-family: var(--sans); font-size: 12px; color: var(--ink-mute); letter-spacing: 0; margin-left: 4px; padding-right: 4px; }
/* Plate — square-cornered paper, sized to the aspect ratio + scaled to fit.
   overflow:hidden clips any slot shadow at the plate edge so it sits ON the
   paper (like the export, which clips at the image bound) and never spills onto
   the grey canvas. The plate padding grows with the shadow so it has room. */
.layouts-plate { background: var(--paper); border-radius: 2px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  /* Positioned so absolute children (the wall's bulk invite) anchor to the
     PLATE — unpositioned, they anchored to an ancestor and sat off-centre
     (Mark, 16 July 2026: "not in the middle"). */
  position: relative; }
/* padding:0 — the slots container also carries the generic `row` class, and
   the global `.row` utility (padding:14px 0) otherwise leaks a 14px top/bottom
   band inside the plate. Slot spacing is driven by the plate model + inline
   gap, so this container must never have intrinsic padding. (Mark) */
/* position:relative anchors the shadow-caster underlays (separate-arrangement
   shadows cast from a layer below every slot - see layoutsSyncShadowCasters). */
.layouts-slots { display: flex; align-items: center; justify-content: center; transform-origin: center; padding: 0; position: relative; }
.layouts-slots.col { flex-direction: column; }
/* Grid: a column of justified rows (semi-custom 'lanes' template). */
.layouts-slots.grid { flex-direction: column; }
.layouts-grid-row { display: flex; align-items: center; justify-content: center; }
.layouts-slots.rowend { align-items: flex-end; }
.layouts-slots.whole { align-items: center; }
/* Staggered: a diagonal cascade. The per-slot margin offsets are set inline
   (render-scaled) in renderLayoutsBoard so they measure + scale correctly. */
.layouts-slots.stagger { align-items: flex-start; }
/* Offset brick: two centred rows; flex:none keeps each row at its full natural
   width so the half-width edge slots stay put (no shrink-to-fit). (Mark) */
.layouts-slots.brick .layouts-grid-row { flex: none; }
/* Hero + row: desktop spanning the top, three phones in the row below. */
.layouts-slots.hero { display: grid; grid-template-columns: repeat(3, auto); justify-items: center; align-items: start; }
.layouts-slots.hero > .layouts-slot:first-child { grid-column: 1 / -1; }
/* ── Text on the board (#283) ─────────────────────────────────────────────
   Two canvases straddling .layouts-slots, painted by the SAME LoeyText.drawText
   the three export painters call, so the board and the file cannot drift. They
   fill the plate exactly, which is what makes an element's fractional x/y land
   on the same coordinates here and at export — the plate box and the export
   canvas are the same aspect by construction (layoutsFitBoard and
   layoutsBuildCanvas compute the same padded ratio).
   pointer-events:none throughout: these are paint, not surface. Every gesture
   belongs to .layouts-tx or to the slots underneath. */
.layouts-txcv { position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; }
/* Behind sits under the slots, front over them. The slots container is
   position:relative with no z-index, so it forms no stacking context of its own
   and plain document order decides — which is exactly what the DOM order in
   renderLayoutsBoard is written to express. z-index only pins the front layer
   above slot children that carry their own z (Part's column, the pair overlap). */
.layouts-txcv.is-front { z-index: 3; }
/* The editing surface. It renders NO glyphs — only frames, handles and, while
   you are typing, one contenteditable body which the canvas skips via drawText's
   skipId. pointer-events:none on the layer so an empty board is still an
   ordinary board: clicks fall through to the slots and drag-and-drop is
   untouched. Only the elements themselves take the pointer. */
/* The styling modal's stage carries the SAME editing surface (15 Aug 2026,
   text on a capture). Its nodes are .layouts-txel and friends deliberately: the
   two surfaces are one component wearing two hosts, and a second set of rules
   here is how they drift apart. Only the container differs, so only the
   container gets a second selector. */
.layouts-tx, .ss-tx { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
/* The piece and its type layer, sized to the canvas. The stage centres this;
   the canvas inside keeps the max-width/height contain rule it always had, and
   the wrapper shrink-wraps to whatever that resolves to — which is what lets an
   absolutely-positioned overlay line up with a letterboxed image. */
/* align-items: CENTER, and it is load-bearing (Mark: "captures are stretched in
   styleing"). This is a flex container, so its default align-items is STRETCH —
   and the canvas inside has `height: auto`, which is exactly the condition
   under which stretch applies. Any capture taller than the stage therefore had
   its height forced to the stage's while its width stayed at max-width: 100%,
   which is not a crop or a fit, it is a squash: a 400×1200 phone shot rendered
   at 918×744. Wide captures never hit it, because max-height never engaged —
   which is why it survived a probe run with a 2:1 test image and only showed up
   on real pages. With stretch off, `max-height: 100%` scales the canvas the way
   it is meant to: both axes together. */
.ss-piece { position: relative; display: flex; align-items: center; justify-content: center; max-width: 100%; max-height: 100%; min-width: 0; min-height: 0; }
/* The piece carries the ratio (set from the canvas in repaint), so it fits the
   stage on whichever axis binds; the canvas then simply fills it. This is what
   makes the piece's box and the plate's box the same box — which the text layer
   depends on, and which the old auto-sizing only achieved by accident for
   captures that happened to be wider than they were tall. */
.ss-piece { height: auto; width: auto; }  /* the binding axis is set per plate in repaint */
.ss-piece canvas { width: 100%; height: 100%; display: block; }
/* ROUND 13 (16 Aug 2026, Mark: "No box shadow on the capture!") — the
   wrapper's presentation shadow is GONE. Round 8 took the border and shadow
   off the canvas element; this one had moved to the wrapper (for the type
   layer's sake) and survived that sweep. The plate shows the STYLE'S own
   shadow when one is set, and nothing else — the stage must never dress a
   capture the export won't. */
.styleset-stage .ss-piece { box-shadow: none; }
.styleset-stage .ss-piece canvas { box-shadow: none; }
/* A per-capture note under a set-wide heading. Quiet, because it is a
   clarification and not a warning. */
.styleset-txnote { margin: 8px 2px 12px; font-size: 11px; line-height: 1.45; color: var(--ink50); }
/* Snap guides (#309). Identical to .reel-tguide — same yellow, same hairline,
   same never-takes-the-pointer rule — so the gesture reads the same in both
   modes. They exist only during a drag; layoutsOnTxUp clears them. */
.layouts-tguide { position: absolute; background: #F7CE46; z-index: 6; pointer-events: none; opacity: .9; }
.ss-tx .layouts-tguide { z-index: 6; }
.layouts-tguide.v { width: 1px; top: 0; bottom: 0; }
.layouts-tguide.h { height: 1px; left: 0; right: 0; }
.layouts-txel { position: absolute; transform: translate(-50%, -50%); box-sizing: border-box;
  pointer-events: auto; cursor: default; }
.layouts-txel .layouts-txbody { white-space: pre-wrap; word-break: break-word; outline: none; }
/* a text that hugs its words breaks only at its own Returns, in the box it is typed or dragged in too (21 Sep 2026) */
.layouts-txel.is-hug .layouts-txbody, .reel-txel.is-hug .reel-txbody { white-space: pre; }
.layouts-txel.empty .layouts-txbody::before { content: 'Type…'; opacity: .45; }
/* An outset ring, not a border: a border would change the box the type is
   measured into and the DOM frame would stop agreeing with the canvas. */
.layouts-txel::after { content: ''; position: absolute; inset: -5px; border: 1px solid transparent;
  border-radius: 3px; pointer-events: none; }
/* Honey, the same selection colour Sequence uses — one accent for one gesture
   across both modes, per Mark's standing consistency rule. */
.layouts-txel.on::after { border-color: #F7CE46; }
.layouts-txel.editing::after { border-color: #F7CE46; border-style: dashed; }
/* A behind element is UNDER the captures, so its selected frame is dashed too —
   otherwise the only way to tell which side a box is on is to read the panel,
   and you would be dragging an invisible thing wondering why nothing moved. */
.layouts-txel.is-behind.on::after { border-style: dashed; }
.layouts-txh { position: absolute; top: 50%; width: 9px; height: 9px; margin-top: -4.5px; border-radius: 2px;
  background: #fff; border: 1.5px solid #F7CE46; display: none; cursor: ew-resize; z-index: 2; }
.layouts-txel.on .layouts-txh { display: block; }
.layouts-txh.l { left: -9px; }
.layouts-txh.r { right: -9px; }
/* the corners, which scale the type with the box (21 Sep 2026, Mark: "for moyo we have added corner points to text for enlarging the text") */
.layouts-txh.c, .reel-txh.c { top: auto; margin: 0; border-radius: 50%; }
.layouts-txh.nw, .reel-txh.nw { left: -9px; top: -9px; cursor: nwse-resize; } .layouts-txh.se, .reel-txh.se { right: -9px; bottom: -9px; cursor: nwse-resize; }
.layouts-txh.ne, .reel-txh.ne { right: -9px; top: -9px; cursor: nesw-resize; } .layouts-txh.sw, .reel-txh.sw { left: -9px; bottom: -9px; cursor: nesw-resize; }

/* ── The Text row in the Layouts panel (#284) ──────────────────────────────
   The Sequence Text drawer, ported — and almost nothing needed porting, which
   is the point. .reel-txlist / .reel-txrow / .reel-txwarn / .reel-txhint are
   all unscoped rules, so the element list, the licensing note and the hint are
   literally the same components; .ghost-btn.is-add, .pill, .opt-toggle-row and
   .field-label are the app's; and the colour grid is this panel's own
   .layouts-swatch. What is left is the two dropdowns and three measurements.

   THE DROPDOWNS. .reel-dd's base is the 28px / 11.5px control the canvas
   toolbar uses, and .reel-side sizes it up to 39px / 13px for the Sequence
   panel. Neither is right here: dropped in raw it would sit next to this
   panel's 13px pills at two-thirds their type size and read as a different
   product (Mark's standing rule). So it takes the pill's TYPE SIZE — 13px, and
   the same --bmed edge.
   RADIUS IS 8px, NOT 100px (Mark, 6 Aug 2026: "the round pill drop downs for
   the font selection. They are too round pill shape. Make consistent with the
   rest in the app."). The original reasoning — "a pill is what it is standing
   in for" — held for the pill ROWS, which are short words in a wrapping group.
   These two are full-width controls carrying a font name and a chevron, and at
   100px they were the only lozenges of their size in the panel: the + Add text
   button directly above them is a .ghost-btn at 8px, .reel-dd's own base is
   8px, and .ghost-btn is the app's radius for a control this shape. 8px is the
   consistent answer in all three directions. */
/* SECOND HOST (15 Aug 2026). The styling modal mounts on .app, outside
   #previewPanel, so the scoped rules below would have missed it entirely and
   its font dropdowns would have rendered at the 28px/11.5px toolbar size beside
   13px pills — the exact "reads as a different product" failure this block was
   written to fix. Extending the selector, rather than restating the values, is
   what stops the two drifting. */
#previewPanel .layouts-txed .reel-dd-btn,
.styleset-overlay .layouts-txed .reel-dd-btn { width: 100%; justify-content: space-between;
  height: 33px; padding: 0 14px; border-radius: 8px; font-size: 13px;
  border-color: var(--bmed); gap: 10px; }
#previewPanel .layouts-txed .reel-dd-btn:hover,
.styleset-overlay .layouts-txed .reel-dd-btn:hover { border-color: var(--bstr); }
/* Scrollable because the curated list is longer than a menu should be tall,
   and min-width:100% so it can never be narrower than the button it drops from. */
#previewPanel .layouts-txed .reel-dd-menu,
.styleset-overlay .layouts-txed .reel-dd-menu { min-width: 100%; max-height: 250px; overflow: auto;
  border-color: var(--bmed); border-radius: 10px; padding: 5px; }
#previewPanel .layouts-txed .reel-dd-menu button,
.styleset-overlay .layouts-txed .reel-dd-menu button { font-size: 13px; padding: 8px 11px; border-radius: 8px; }
/* WIDTH IS THE SAME BUG #276 FIXED IN THE OTHER PANEL. The add button sits in
   an .option-expand, which is a BLOCK — so it shrinks to its own label and then
   visibly CHANGES WIDTH the first time you use it, when the label goes from
   "Add text" to "Add text element". width:100% holds either way. */
#layouts-txadd { width: 100%; }
/* The list, the button and the editor have no margins of their own and the
   expand is display:block, so without these three the list sits flush on the
   button and the button flush on the editor. */
#layouts-txlist { margin-bottom: 10px; }
#layouts-txed { margin-top: 4px; }
.layouts-txed[hidden] { display: none; }
/* The modal's copies of the three spacing rules above. They are ID-based on the
   Layouts side (#layouts-txadd / #layouts-txlist / #layouts-txed) and an id
   cannot be shared by two surfaces, so this is the same three measurements
   keyed off the sidebar instead. */
.styleset-side [data-sstxadd], .styleset-side [data-sstxpaste] { width: 100%; }
.styleset-side .reel-txlist { margin-bottom: 10px; }
.styleset-side .layouts-txed { margin-top: 4px; }
/* 16px on top of the app's own 14px makes a break that is visibly larger than
   the rhythm above it, so the drawer ENDS at the destructive action rather
   than continuing into it. */
#layouts-txrm { margin-top: 16px; }
.layouts-slot { position: relative; cursor: pointer; }
.layouts-frame { position: relative; overflow: hidden; box-sizing: border-box; }
.layouts-slot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }
/* A video fill plays over its poster <img>: same cover sizing, overlaid on top. */
.layouts-slot-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
/* The clip diagnosis tag (31 Aug 2026): a slot whose clip is not playing says
   why, on the plate, in words — see lyClipDiag in app.js. */
.ly-clip-diag { position: absolute; left: 6px; bottom: 6px; z-index: 5; max-width: calc(100% - 12px);
  background: rgba(178, 38, 30, .92); color: #fff; font: 500 10.5px/1.35 var(--sans, sans-serif);
  padding: 4px 8px; border-radius: 6px; pointer-events: none; }
/* Empty slots are colour fills (no border): inactive darker, ACTIVE lighter —
   the active (ready-to-fill) slot reads brighter than the rest, matching the
   dark-plate behaviour below (Mark). */
/* FIXED tints, not theme tokens: the slots sit on the plate (the export preview,
   which stays the chosen background colour), so they must read the same in dark
   mode as light — a token would flip to a light tint and vanish on the white plate. */
/* SOLID fills (not translucent) so the plate background can't show through an
   empty slot — a see-through tint over a coloured/gradient plate read oddly
   (Mark). Inactive darker, ACTIVE lighter (ready-to-fill reads brighter). */
.layouts-slot.empty .layouts-frame { background: #e2e2dd; }
/* THE DEVICE BOARDS (11 Sep 2026): the device is the slot, no plate behind it, and a drag turns it */
.layouts-slot.is-device .layouts-frame, .layouts-slot.is-device.active .layouts-frame { background: transparent; }
.layouts-slot.is-device { cursor: grab; touch-action: none; }
.layouts-slot.is-device.is-dev-drag { cursor: grabbing; }
.layouts-slot.active .layouts-frame { background: #edede8; }
/* Empty slots keep ONE solid light fill on every plate - the dark-plate
   variants read as a translucent wash over black (Mark, 10 July 2026).
   The light card with ink buttons is readable on any background. */
/* (Dark-plate button flips removed with the dark slot tints - the buttons
   always sit on the light slot fill now.) */
.layouts-slots.is-dark-plate .layouts-cap-t { color: rgba(255, 255, 255, .8); }
.layouts-slots.is-dark-plate .layouts-cap-hint { color: rgba(255, 255, 255, .56); }
.layouts-slots.is-dark-plate .layouts-cap-spinner { border-color: rgba(255, 255, 255, .22); border-top-color: rgba(255, 255, 255, .85); }
/* Drag a filled slot onto a same-device slot to swap the captures (reorder). */
.layouts-slot.filled { cursor: grab; }
.layouts-slot.filled:active { cursor: grabbing; }
.layouts-slot.dragging { opacity: .4; }
.layouts-slot.drag-over { box-shadow: 0 0 0 2.5px var(--ink); }
/* Counter-scaled UI wrapper — keeps the prompt/spinner at true app size no
   matter how big the board is drawn (the board itself is fit-scaled). */
.layouts-slot-ui { position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 8px; padding: 6px; box-sizing: border-box;
  transform-origin: center; }
.layouts-cap-t { font-family: var(--sans); font-size: 12px; font-weight: 500; color: rgba(14, 14, 12, .68); letter-spacing: -.01em; }
/* Stack the add buttons so they never get cut off in a narrow mobile slot.
   align-items: stretch makes them all take the widest one's width, so the
   + Capture / + Library / + Video buttons line up at an even width. */
.layouts-cap-actions { display: flex; flex-direction: column; align-items: stretch; gap: 5px; max-width: 100%; }
/* Cascade slots overlap down-right, so centred buttons on an earlier slot get
   covered at low gap / Recede - park them in the bottom-left corner there,
   the one region the next slot never reaches. (Mark) */
.layouts-slot-ui.is-corner { align-items: flex-start; justify-content: flex-end; padding: 14px;
  /* The counter-scale (fitBoard) blows the UI up by 1/boardScale about its
     ORIGIN. Centred UI survives that (origin center), but corner-parked
     content was pushed clean out of the slot - which is why the cascade
     add buttons vanished (Mark, 11 July 2026). Anchor the corner. */
  transform-origin: bottom left; }
/* Dashed "+" add buttons (.ghost-btn.is-add), sized for the slot — the same
   affordance the Custom slot and Page tour / Page clips use. */
/* FIXED colours, not theme tokens — the slot UI sits on the white plate, so
   tokens would flip light in dark mode and the buttons/text would vanish. */
.layouts-slot-ui .layouts-add-dashed { font-family: var(--sans); font-size: 11.5px; font-weight: 400; line-height: 1;
  padding: 7px 11px; border-radius: 9px; white-space: nowrap; max-width: 100%; text-align: center;
  color: rgba(14, 14, 12, .55); border: 1px dashed rgba(14, 14, 12, .22); background: transparent; }
.layouts-slot-ui .layouts-add-dashed:hover { color: rgba(14, 14, 12, .9); border-color: rgba(14, 14, 12, .45); background: rgba(14, 14, 12, .05); }
.layouts-slot-ui .layouts-add-dashed[disabled] { opacity: .5; cursor: default; }
.layouts-slot-ui .layouts-add-dashed[disabled]:hover { color: rgba(14, 14, 12, .55); border-color: rgba(14, 14, 12, .22); background: transparent; }
/* The ONE adaptive Add control (#311). Three labels are in the markup; the
   slot's width tier decides which one is shown. Default (wide): the full
   sentence. .is-mid (~90-168px real): "Add". .is-small (<90px): a bare ＋.
   Swapping labels rather than scaling type is deliberate — the slot UI is
   already counter-scaled to true size, so shrinking here would make the
   control unreadable exactly when the frame is hardest to hit. */
.layouts-add-one { display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.layouts-add-one .ad-m { display: none; }
.layouts-slot.is-mid .layouts-add-one .ad-l { display: none; }
.layouts-slot.is-mid .layouts-add-one .ad-m { display: inline; }
.layouts-slot.is-small .layouts-add-one .ad-l,
.layouts-slot.is-small .layouts-add-one .ad-m { display: none; }
.layouts-slot.is-small .layouts-add-one .ad-g { display: inline; font-size: 13px; }
.layouts-slot.is-small .layouts-add-one { padding: 5px 9px; border-radius: 7px; }
/* .is-large (≥300px real): the tier above the default. The same reasoning that
   shrinks the control on a phone frame has to run the other way on a big one —
   on a Showreel stack slot the true-size button is a 27px target adrift in an
   empty frame ten times its height. This is the one place in the slot UI where
   the TYPE grows, and it is deliberate: there is no shorter wording left to
   swap to, and nothing else in an empty slot competes with it. */
.layouts-slot.is-large .layouts-slot-ui .layouts-add-dashed {
  font-size: 14.5px; padding: 13px 20px; border-radius: 11px;
}
.layouts-slot.is-large .layouts-add-one { gap: 7px; }
.layouts-slot.is-large .layouts-add-one .ad-g { font-size: 16px; }
.layouts-slot.is-large .layouts-cap-actions { gap: 8px; }
/* The Add popover is parented to `.app` (it must escape .layouts-stage's clip
   and the slot UI's counter-scale, but it must NOT leave the element that
   declares every design token — see layoutsOpenAddMenu), so it is
   fixed-positioned from script. Everything else it borrows wholesale from the
   toolbar's motion menu, which is the point: one popover idiom in Layouts.
   Compound selector on purpose: .lt-anim-menu's own min-width is declared LATER
   in this file, and two single-class selectors tie on specificity so source
   order would win. Doubling up outranks it without moving the rule away from
   the code it belongs with. */
.lt-anim-menu.layouts-add-menu { position: fixed; top: 0; left: 0; min-width: 150px; z-index: 90; }
/* These labels are short sentences, not one-word motion names, so they get a
   hair more horizontal room than the motion menu and are never allowed to wrap
   — a wrapped row reads as two options. */
.layouts-add-menu .lt-anim-item { padding: 7px 11px; white-space: nowrap; }
.layouts-cap-err { font-size: 10.5px; color: #B4321F; max-width: 96%; text-align: center; line-height: 1.3; }
.layouts-cap-hint { font-size: 10.5px; color: rgba(14, 14, 12, .46); max-width: 96%; text-align: center; line-height: 1.35; }
.layouts-cap-spinner { width: 24px; height: 24px; border-radius: 50%; border: 2.5px solid rgba(14, 14, 12, .16);
  border-top-color: rgba(14, 14, 12, .85); animation: layouts-spin .7s linear infinite; }
@keyframes layouts-spin { to { transform: rotate(360deg); } }
/* Filled-slot hover actions — subtle dark icon buttons in the corner, like the
   library cards. Counter-scaled (transform-origin top-right) to stay app-sized. */
.layouts-slot-corner { position: absolute; top: 6px; right: 6px; display: flex; gap: 4px;
  opacity: 0; transition: opacity .12s; transform-origin: top right; z-index: 2; }
.layouts-slot.filled:hover .layouts-slot-corner { opacity: 1; }
/* …and when the pointer is over the slot but the topmost element is a text box
   (Mark, 6 Aug 2026). :hover never reaches a slot under `.layouts-txel`, so
   without this the Replace and Remove buttons stayed invisible — and therefore
   unfindable — anywhere type crossed a capture. layoutsBindTxGestures sets the
   class from the real hit-test and forwards the press. */
.layouts-slot.filled.is-txhover .layouts-slot-corner { opacity: 1; }
/* SHOWREELS: the dashed invite stays on the board while the queue has room, and
   it must sit ABOVE the running belt without blocking it. Same shell as the
   columns' empty-board invite — an inset overlay that takes no pointer events,
   so only the button itself is clickable and the board underneath still works.
   CENTRED (Mark, 8 Aug 2026: "should be in the middle, not sure why it is at
   the bottom"). It was parked at the bottom on the theory that a centred
   control would cover the screen the reel is showing; centring is where the
   eye goes, and every other board's empty-slot invite is centred too, so the
   bottom was the odd one out. */
.layouts-reel-invite {
  position: absolute; inset: 0; z-index: 4;
  display: flex; justify-content: center; align-items: center;
  pointer-events: none;
}
/* Sized like the .is-large slot tier below. This invite only ever appears on an
   EMPTY reel board, so it is always sitting in the largest empty frame the app
   ever shows and there is nothing for it to compete with — the default 12px
   ghost button was pitched for a control among other controls. */
.layouts-reel-invite button { pointer-events: auto; }
/* .ghost-btn.is-add in the selector, not a bare `button`: is-add sets its own
   padding at (0,2,0) and would out-specify a one-class rule, so the type would
   grow and the box around it would not. */
.layouts-reel-invite .ghost-btn.is-add {
  font-size: 14.5px; padding: 13px 20px; border-radius: 11px;
  /* PAPER BEHIND THE WORDS (30 Aug 2026, Mark: "cannot see the button text on
     desktop rows"). The invite floats over the BOARD, and on a two-row belt
     its centre lands in the gap between the rows — ink type on a black plate,
     invisible. Every plate colour is legal under this button, so it carries
     its own paper instead of borrowing whatever happens to be behind it. */
  background: var(--paper); color: var(--ink);
}
/* CUT-OFF slots (tagged by layoutsFitBoard — wall/brick screens bleeding past
   the plate): the clipped side's UI flips into the visible area so swap and
   delete stay reachable (Mark, 16 July 2026). Right-cut: corner actions move
   to the top-LEFT and the numeral to the bottom-left (bottom, not "below the
   icons" — the icons' counter-scaled height varies with the board scale, the
   slot's bottom corner never collides). Left-cut: numeral to the bottom-right,
   under the untouched actions. */
.layouts-slot.is-cut-right .layouts-slot-corner { right: auto; left: 6px; transform-origin: top left; }
.layouts-slot.is-cut-right .layouts-slot-index { top: auto; bottom: 6px; transform-origin: bottom left; }
.layouts-slot.is-cut-left .layouts-slot-index { left: auto; right: 8px; top: auto; bottom: 6px; transform-origin: bottom right; }
/* VERTICAL cut (31 July 2026): the motion templates clip at the plate always,
   so a card resting past the top or bottom edge hid its own furniture there.
   Top-cut drops both the numeral and the corner actions to the bottom edge
   (opposite corners, so they never collide); bottom-cut lifts the numeral back
   to the top, where the actions already live. The centred add buttons are
   recentred on the visible band by layoutsFitBoard itself, not by CSS — the
   band's size is only known after the fit. */
.layouts-slot.is-cut-top .layouts-slot-corner { top: auto; bottom: 6px; transform-origin: bottom right; }
.layouts-slot.is-cut-top .layouts-slot-index { top: auto; bottom: 6px; transform-origin: bottom left; }
.layouts-slot.is-cut-top.is-cut-right .layouts-slot-corner { right: auto; left: 6px; transform-origin: bottom left; }
.layouts-slot.is-cut-top.is-cut-right .layouts-slot-index { left: auto; right: 8px; top: 6px; bottom: auto; transform-origin: top right; }
.layouts-slot.is-cut-bottom .layouts-slot-index { top: 6px; bottom: auto; transform-origin: top left; }
.layouts-corner-btn { width: 24px; height: 24px; padding: 0; border: 0; border-radius: 7px;
  background: rgba(16, 16, 14, .82); color: #FBFAF8; display: flex; align-items: center; justify-content: center;
  cursor: pointer; -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.layouts-corner-btn:hover { background: rgba(16, 16, 14, 1); }
.layouts-corner-btn svg { width: 14px; height: 14px; }
/* SMALL slots (tagged .is-small by layoutsFitBoard when rendered < ~90px):
   compact everything so the true-size UI doesn't swamp the screen (Mark,
   16 July 2026). Corner actions shrink; the numeral hides on filled slots
   (the buttons cover it anyway); the dashed add buttons collapse to a bare
   "+" — Capture drops (library is the primary path), leaving ONE tidy +. */
.layouts-slot.is-small .layouts-corner-btn { width: 18px; height: 18px; border-radius: 5px; }
.layouts-slot.is-small .layouts-corner-btn svg { width: 10px; height: 10px; }
.layouts-slot.is-small .layouts-slot-corner { top: 3px; right: 3px; gap: 3px; }
.layouts-slot.is-small.filled .layouts-slot-index { display: none; }
.layouts-slot.is-small .layouts-cap-actions [data-lcap] { display: none; }
.layouts-slot.is-small .layouts-cap-actions .layouts-add-dashed { font-size: 0; padding: 4px 8px; border-radius: 7px; }
.layouts-slot.is-small .layouts-cap-actions .layouts-add-dashed::before { content: '+'; font-size: 14px; line-height: 1; }
/* …but the ONE adaptive Add (#311) carries its own three labels, so it opts
   out of that ::before glyph and swaps WORDS instead — the type stays 11.5px
   at every tier, which is the whole point: "the editor controls should remain
   a fixed readable size even when the layout preview is zoomed down." */
.layouts-slot.is-small .layouts-cap-actions .layouts-add-one { font-size: 11.5px; }
.layouts-slot.is-small .layouts-cap-actions .layouts-add-one::before { content: none; }
.layouts-slot.is-small .layouts-cap-err { display: none; }
/* Slot order numeral — small, quiet, mono. Counter-scaled (transform-origin
   top-left) to stay app-sized at any board scale. Faint ink on the grey
   empty/active fills; on the dark plate it flips to a light tint. */
.layouts-slot-index { position: absolute; top: 6px; left: 8px; z-index: 2; pointer-events: none;
  font-family: var(--mono); font-size: 11px; line-height: 1; letter-spacing: .04em;
  color: rgba(14, 14, 12, .34); transform-origin: top left; white-space: nowrap; }
.layouts-slots.is-dark-plate .layouts-slot-index { color: rgba(255, 255, 255, .44); }
/* On a FILLED slot the numeral would sit on the image — hide it until hover,
   then show it as a small dark chip (legible over any photo, matching the
   corner action buttons). */
.layouts-slot.filled .layouts-slot-index { opacity: 0; transition: opacity .12s;
  padding: 2px 5px; border-radius: 6px; color: #FBFAF8; background: rgba(16, 16, 14, .72);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.layouts-slots.is-dark-plate .layouts-slot.filled .layouts-slot-index { color: #FBFAF8; }
.layouts-slot.filled:hover .layouts-slot-index { opacity: 1; }
/* Reflow stage: the numeral follows the window (device label live-updates).
   With CAPTURES showing it behaves like any filled slot — hidden until hover,
   then the dark chip (Mark, 16 July 2026: "i didnt actually mean for the
   labels for reflow to be on the capture, only the empty slots"). The BLANK
   stage keeps the quiet faint numeral, readable on the grey screens. */
.layouts-slot.is-morphing.morph-filled .layouts-slot-index { opacity: 0; transition: opacity .12s;
  padding: 2px 5px; border-radius: 6px; color: #FBFAF8; background: rgba(16, 16, 14, .72);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.layouts-slot.is-morphing.morph-filled:hover .layouts-slot-index { opacity: 1; }
/* Panel heading — matches the other modes' "{Mode} capture (i)" header: a medium
   sentence-case heading with the info dot beside it. */
.layouts-panel-head { display: flex; align-items: center; gap: 7px; margin-bottom: 18px; }
/* No hairlines between the settings in this panel (Mark, 3 Aug — the same call
   he made for the template groups in the rail on the same day). The lines are
   right in a capture mode, where the panel is a form you fill in from the top
   down and each row is a step. Layouts is not that: the board is the subject,
   the panel is a set of knobs you reach for one at a time, and eleven ruled
   rows read as a table of contents for a document nobody is reading. The rows
   keep their padding, so the rhythm survives without the ink. Matched with
   :has() rather than a class, because the panel shell is shared with Edits and
   only Layouts renders this head.

   STYLE JOINS IT 17 Aug 2026 (#288). Its head is not a direct child of
   #previewPanel — it rides inside .styleset-side, which is the element that
   gets relocated into the column — so the `>` version cannot see it and the
   descendant one is added beside rather than replacing it. Same argument for
   the same reason: Style's subject is the plate, and the panel beside it is
   knobs you reach for one at a time. */
#previewPanel:has(> .layouts-panel-head) .option-row,
#previewPanel:has(.mode-panel-head) .option-row { border-bottom: 0; }
.layouts-panel-title { font-family: var(--sans); font-size: 14px; font-weight: 500; letter-spacing: -.005em; color: var(--ink); }
/* right-panel layout cards (outlined device glyphs at true proportions). */
.layouts-lay-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
/* Collapsible template groups (Phone / Desktop / Mixed / Pages / Reels): an
   icon + label + chevron row that toggles its card grid. Grid (the versatile
   builder) sits pinned above these, always visible. (Mark, 15 July 2026) */
/* Same hairline token as the option rows (--ink08) — the picker groups used
   the darker --rule-faint and read as a second, heavier line system inside an
   already-lined panel (Mark, 16 July 2026: "make the lines the same"). */
.layouts-lay-group { border-top: 1px solid var(--ink08); }
.layouts-lay-group:first-child { border-top: none; }
.layouts-lay-grouphead { display: flex; align-items: center; gap: 11px; width: 100%;
  background: none; border: 0; cursor: pointer; padding: 12px 2px; text-align: left;
  font-family: var(--sans); font-size: 13px; color: var(--ink-soft); transition: color .12s; }
.layouts-lay-grouphead:hover { color: var(--ink); }
.layouts-lay-groupicon { flex: 0 0 auto; width: 20px; height: 20px; color: var(--ink-mute);
  display: flex; align-items: center; justify-content: center; }
.layouts-lay-groupicon svg { width: 20px; height: 20px; display: block; }
.layouts-lay-grouplbl { flex: 1 1 auto; letter-spacing: -.005em; }
.layouts-lay-group.is-open .layouts-lay-grouphead { color: var(--ink); font-weight: 500; }
.layouts-lay-group.is-open .layouts-lay-groupicon { color: var(--ink); }
.layouts-lay-grouphead .option-chevron { flex: 0 0 auto; width: 10px; height: 10px;
  margin-left: 4px; color: var(--ink32); transition: transform .15s ease; }
.layouts-lay-group.is-open .layouts-lay-grouphead .option-chevron { transform: rotate(90deg); }
.layouts-lay-groupbody { padding: 2px 0 14px; }
.layouts-lay-groupbody[hidden] { display: none; }
/* Empty columns board: the library-first invite, centred over the placeholder
   belts — reuses the reels' .layouts-slot-ui markup so the dashed button reads
   identically. (Mark, 15 July 2026) */
.layouts-col-invite { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.layouts-col-invite button { pointer-events: auto; }
/* Desktop Wall empty board: the bulk-add invite, centred on the visible plate
   (the brick bleeds off the sides, so a per-slot button would clip). Reuses the
   reels' .layouts-slot-ui markup. (Mark, 15 July 2026) */
/* (.layouts-wall-invite retired 16 July 2026 — the wall uses the standard
   in-slot bulk invite on slot 2 now.) */
.layouts-lay { position: relative; background: var(--card-fill); border: 1px solid var(--rule); border-radius: 10px;
  padding: 16px 12px 13px; cursor: pointer; text-align: center; }
.layouts-lay.sel { border: 1.5px solid var(--ink); }
.layouts-lay-dg { height: 52px; display: flex; align-items: center; justify-content: center; }
.layouts-glyph { display: block; border: 1.4px solid var(--ink-mute); border-radius: 3px; box-sizing: border-box; }
.layouts-lay.sel .layouts-glyph { border-color: var(--ink); }
.layouts-lay-cap { font-size: 13px; color: var(--ink-soft); margin-top: 9px; }
.layouts-lay.sel .layouts-lay-cap { color: var(--ink); font-weight: 500; }
.layouts-lay-check { position: absolute; top: 9px; right: 10px; color: var(--ink); display: flex; }
.layouts-lay-check svg { width: 13px; height: 13px; }
/* Reset image-output settings to default (subtle text button under the options). */
.layouts-reset { margin-top: 14px; width: 100%; background: transparent; border: none;
  font-family: var(--sans); font-size: 12px; color: var(--ink-mute); cursor: pointer;
  /* Left-aligned like every other row (Molly: nothing else centres). */
  text-align: left; padding: 9px 2px; border-radius: 9px; transition: color .12s; }
/* Quiet text hover — a background pill reads wrong on a left-aligned text button
   (the pill hugs the full row while the label sits at the edge). Ink shift only. */
.layouts-reset:hover { color: var(--ink); }
/* ══ RESET STYLING BEGIN ══════════════════════════════════════════════════
   A BUTTON, not another row in the option list (Mark, 11 Aug 2026). It used
   to sit at 14px 0 with no inset so it lined up with the option summaries
   around it — which is precisely what was wrong: it read as one more label
   naming a setting, when it is the only irreversible action on the panel.
   Now it is a ghost button, inline-sized, with the reset glyph. */
/* OUTLINE, not a filled ghost (Mark, 11 Aug 2026). Same call he made on the
   Reorder button on 4 Aug — "just needed to be an outline button" — and for the
   same reason: a filled chip is the app's affirmative weight, and this is a
   step back, not a step on. .is-choice is the app's existing outlined chip, so
   the outline here is drawn the same way (inset ring, transparent fill) rather
   than inventing a second outlined button. */
.finish-reset {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  width: auto;
  margin: 6px 0 2px;
  text-align: left;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--bmed);
}
.finish-reset:hover { background: transparent; box-shadow: inset 0 0 0 1px var(--bstr); color: var(--ink); }
.finish-reset svg { width: 13px; height: 13px; flex: none; }
/* Armed: the same button is now the way back. The ring goes to full ink —
   enough to say "this changed under you" without a colour that would put it in
   competition with Capture. */
.finish-reset.is-undo {
  color: var(--ink);
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--ink);
}
.finish-reset.is-undo:hover { background: var(--btn2bg-hover); color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
/* ══ RESET STYLING END ════════════════════════════════════════════════════ */
.layouts-export { width: 100%; justify-content: center; }
.layouts-export[disabled] { opacity: .4; cursor: default; }
/* The render percentage's own slot (Mark, 6 Aug 2026). Tabular figures so the
   digits don't jostle; the WIDTH is set in px by layoutsExBusy, measured from
   this slot's own widest reading ("100%"), because a ch-based guess falls short
   of three digits plus a percent sign and lets the button grow at 100 — the
   exact wobble this exists to stop. The button sits in a right-pinned topbar
   row with two ghost buttons to its left, so any growth pushed them sideways. */
.lt-exp-pct { display: inline-block; text-align: right;
  font-variant-numeric: tabular-nums; }
/* library picker overlay */
.layouts-picker { position: fixed; inset: 0; background: rgba(14, 14, 12, .5); display: none;
  align-items: center; justify-content: center; z-index: 10000; } /* above the Style modal (9998) so the backdrop picker can borrow it */
/* Multi-select (the reels): a picked card's own border goes ink + doubles up
   (border + a ring in the same colour = one clean 2px line, no layout shift) —
   not a separate outline floating around the thin border (Mark, 13 July). */
.layouts-pk-grid .lib-card.is-picked { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }
/* The picker's Add is a ghost-btn (the app's own button voice; Mark, 13 July
   2026), kept compact for the head row — and, since 12 Aug 2026, FILLED once
   there is something to add.
   It is the commit of a modal: press it and the picks land and the picker
   closes. Everything else in that toolbar — the filters, the search, the mode
   menu — is a way of looking, and a commitment that wears the same grey as the
   controls around it is a commitment you have to hunt for. Ink fill at 12/500
   is the same treatment the crop overlay's Done and the library's Done wear,
   which is the point of the pass.
   Disabled (nothing picked yet) it stays grey, because then it genuinely is
   not the thing to press. */
.layouts-pk-count { flex: none; white-space: nowrap; font-family: var(--mono); font-size: 11.5px; color: var(--ink50, #8a8a88); }
.layouts-pk-count[hidden] { display: none; }
.layouts-pk-done { flex: none; white-space: nowrap; }
.layouts-pk-done:not(:disabled) { background: var(--ink); color: var(--paper); font-weight: 500; }
.layouts-pk-done:not(:disabled):hover { background: var(--ink); color: var(--paper); opacity: .86; }
/* .btn-primary sets display — reassert `hidden` so the Done button truly
   disappears outside multi-select (it rendered as an empty stretched pill). */
.layouts-pk-done[hidden] { display: none; }
.layouts-picker.show { display: flex; }
/* ── Style's start page IS the picker (31 Aug 2026) ────────────────────────
   The same sheet, reparented into the Style panel's slot (see
   layoutsPickerDock in app.js). Dressed as the LAYOUTS START PAGE (Mark:
   "paper background, title, description etc."): the page carries the serif
   title + muted sub (.lay-gal-h / .lay-gal-sub, the gallery's own classes),
   and the sheet stops being a card entirely — no border, no shadow, its head
   row ruled like the gallery's filter bar, the grid flowing on the paper so
   the PAGE scrolls, not a box inside it. The sheet's inner title hides — the
   page h1 is the name. */
.style-door-page { width: 100%; max-width: 1240px; margin: 0 auto; }
.style-inline-pick { width: 100%; }
.layouts-pk.is-inline { position: static; width: 100%; max-width: none;
  height: auto; min-height: 0; box-shadow: none; border: 0; border-radius: 0;
  background: transparent; overflow: visible; }
.layouts-pk.is-inline .layouts-pk-head { padding: 0 0 13px; border-bottom: 1px solid var(--ink08); }
/* THE HEAD THAT STAYS (21 Sep 2026, Mark: "you might scroll down then have to scroll up to find the style button", then "try
   sticky"): the filters, the search, the count and the Style button pin at the top of the page as the grid scrolls under them */
.layouts-pk.is-inline .layouts-pk-head { position: sticky; top: 0; z-index: 5; background: var(--bg); padding: 10px 24px 13px; margin: -10px -24px 0; transition: box-shadow .15s; }   /* 24px beyond the grid each side, so the cards pass under it with room (21 Sep 2026, Mark: "there is no padding on the sides") */
.layouts-pk.is-inline .layouts-pk-head.is-stuck { box-shadow: 0 10px 18px -12px rgba(14,14,12,.32); border-radius: 12px; border-bottom-color: transparent; }   /* pinned, it reads as a bar, rounded like the app's (Mark: "needs to be easier to see", then "rounded corners") */
.layouts-pk-top { display: block; height: 1px; margin-top: -1px; }
.style-door-page:has(.layouts-pk.is-inline), #style-panel-slot:has(.layouts-pk.is-inline), .layouts-pk.is-inline .layouts-pk-grid { overflow: visible; }   /* nothing between the head and the page's scroll may clip, or the pin fails */
.layouts-pk.is-inline .layouts-pk-grid { overflow: visible; padding: 18px 0 40px; }
.layouts-pk.is-inline .layouts-pk-close { display: none; }
.layouts-pk.is-inline .layouts-pk-title { display: none !important; }
/* On paper the head controls wear the LIBRARY's dress (1 Sep 2026 — Mark:
   "should be more like the library. white on paper doesnt look good"):
   transparent fills, --bmed borders, ink on hover/focus — .lib-dev /
   .lib-search verbatim. */
.layouts-pk.is-inline .pk-dev { background: transparent; border-color: var(--bmed); }
.layouts-pk.is-inline .pk-dev:hover { color: var(--ink); border-color: var(--ink); background: transparent; }
.layouts-pk.is-inline .pk-dev.is-on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.layouts-pk.is-inline .layouts-pk-search,
.layouts-pk.is-inline .layouts-pk-site,
.layouts-pk.is-inline .capt-dd { background: transparent; border-color: var(--bmed); }
.layouts-pk.is-inline .layouts-pk-search:focus { border-color: var(--ink); outline: none; }
/* The empty state centres the FALLBACK door; the page top-anchors and takes
   the gallery's page padding + paper surface. */
.app.mode-style.style-empty #style-panel-slot:has(.style-door-page) {
  display: block; padding: 26px 34px 40px; box-sizing: border-box; min-height: 0;
}
.app.mode-style.style-empty .main { background: var(--bg); }
.app.shell-modetop.is-capture-mode.mode-style.style-empty .workspace > .main { background: var(--bg); }
/* FIXED height (not max-) so the card never shrinks to fit a short grid —
   with one row of results the toolbar used to jump around as filters/search
   changed the result count. Sparse results just leave space below. (Mark) */
.layouts-pk { width: min(1240px, 94vw); height: min(760px, 84vh);   /* wider, so its head holds the title, the filters and the search on one row (21 Sep 2026, Mark: "the pickers top section is breaking... shall we just make the picker wider?") */ background: var(--paper, #FAFAFA); color: var(--ink, #0E0E0C);
  border: 1px solid var(--rule, #D8D7D2); border-radius: 16px;
  overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 30px 80px rgba(0, 0, 0, .4); }
.layouts-pk-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px;
  border-bottom: 1px solid var(--rule); flex-wrap: wrap; row-gap: 8px; }   /* wraps rather than truncates (17 Sep 2026, Mark: "picker drop downs truncated") */
.pk-sep { width: 1px; height: 22px; background: var(--rule); margin: 0 4px; flex: none; }
.layouts-pk-title { font-size: 15px; font-weight: 500; flex: none; white-space: nowrap; }
/* "slot N" matches the "Add to" font/size — only the colour is muted, so the
   line reads as one phrase, not two clashing fonts (Mark). */
.layouts-pk-title span { color: var(--ink-mute); }
.layouts-pk-filters { display: flex; gap: 4px; flex: none; }
/* Device filter: compact icon buttons (was text pills) so the head fits on one
   line alongside the site filter + search (Mark). */
.pk-dev { width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--rule); border-radius: 8px; background: var(--card-fill); color: var(--ink-soft); cursor: pointer;
  transition: background .12s, color .12s, border-color .12s; }
.pk-dev svg { width: 16px; height: 16px; }
.pk-dev:hover { color: var(--ink); border-color: var(--bmed); }
.pk-dev.is-on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
/* "Made for 9:16" — the ratio in words rather than a glyph, sized to its text.
   Three near-identical rectangles at 16px all read as the same shape. (Mark) */
.pk-dev.pk-dev-txt { width: auto; min-width: 32px; padding: 0 9px; flex: none; }
/* 500, not 600 — it was reading as bold next to the glyph chips (Mark). */
.pk-dev.pk-dev-txt b { font: 500 11.5px/1 var(--sans); letter-spacing: .01em; font-variant-numeric: tabular-nums; }
.layouts-pk-search { margin-left: auto; height: 32px; padding: 0 12px; border-radius: 9px; border: 1px solid var(--rule);
  background: var(--card-fill); font-family: var(--sans); font-size: 13px; flex: 1 1 172px; min-width: 140px; color: var(--ink); }
/* Site filter — sits with the device icons; hidden when only one site is present. (Mark) */
.layouts-pk-site { height: 32px; padding: 0 8px; border-radius: 9px; border: 1px solid var(--rule);
  background: var(--card-fill); font-family: var(--sans); font-size: 12.5px; color: var(--ink); cursor: pointer; flex: none; min-width: 120px; max-width: 190px; }
.layouts-pk-site[hidden] { display: none; }
.layouts-pk-close { width: 32px; height: 32px; flex: none; border-radius: 8px; border: 1px solid var(--rule);
  background: var(--card-fill); cursor: pointer; color: var(--ink-soft); }
/* Grid + cards reuse the real library styles (.lib-grid / .lib-card …) so the
   picker matches All captures exactly; only the picker chrome is bespoke. */
.layouts-pk-modes { display: flex; align-items: center; flex: 0 1 auto; min-width: 0; }
.layouts-pk-grid { overflow: auto; padding: 16px; }
/* Smaller cards than the full library — fit 3–4 per row in the picker sheet. */
.layouts-pk-grid .lib-grid { grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 12px; }
.layouts-pk-empty { padding: 44px; text-align: center; color: var(--ink-mute); font-size: 13px; }
/* Kill the full-width spacer so the controls sit tight together in the panel. */
.app.shell-modetop .lab-chrome > span[style*="flex"] { display: none; }
/* Hairline dividers grouping the clusters — Reload · device toggle · rehearse.
   Each only shows when the cluster before it is actually visible (:has), so a
   hidden Reload or empty rehearse group never leaves a dangling line. */
.app.shell-modetop .lab-chrome:has(#tour-preview-reload:not([style*="display: none"])) #topbar-device-slot {
  margin-left: 4px; padding-left: 10px; border-left: 1px solid var(--border);
}
.app.shell-modetop .lab-chrome .lab-chrome-tools:has(> :not([style*="display: none"])) {
  margin-left: 4px; padding-left: 10px; border-left: 1px solid var(--border);
}
/* Pick banner — instruction pill floating over the embedded page,
   app-sized (immune to the frame's CSS scale). Dark to read over any
   page; sits just below the faux browser chrome. */
.tour-preview-banner {
  position: absolute;
  bottom: 18px; /* bottom like the capture window's pill — the top blocked the page's own nav */
  left: 50%;
  transform: translateX(-50%);
  z-index: 8;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: calc(100% - 32px);
  background: #1A1A18;
  color: #FBFAF8;
  border-radius: 18px;
  padding: 8px 10px 8px 16px;
  box-shadow: 0 10px 28px rgba(17, 17, 17, 0.28);
}
.tour-preview-banner[hidden] { display: none; }
.tour-preview-banner-text {
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.5;
  letter-spacing: 0.02em;
}
/* THE BANNER PAIR — Done / Cancel over the live preview. Part of the button
   pass of 12 Aug 2026 (Mark: "they seem to be all over the place in terms of
   matching the styling with what they do").
   12px, not 11.5: the app's button type is 12px everywhere else and half a
   pixel of difference is not a distinction, it is a wobble.
   8px radius, not a 100px stadium: the same call the tour spine's Edit/Done
   made in July — "the old 100px stadium made these read as a different
   component from every other button". The stadium is reserved for the one
   full-width .btn-primary at the foot of a panel. These are small buttons in a
   strip, so they are 8px like every other small button.
   The PADDING is deliberately left tight (4px). The rule the pass settles on is
   that shape and type unify while padding follows the density of the container
   — a banner floating over someone's website has to stay thin. */
.tour-preview-banner-btn {
  font-family: var(--sans);
  font-size: 12px;
  border: 1px solid rgba(251, 250, 248, 0.35);
  background: transparent;
  color: #FBFAF8;
  border-radius: 8px;
  padding: 4px 13px;
  cursor: pointer;
  white-space: nowrap;
}
/* Filled paper on dark ground is the SAME decision as filled ink on light: the
   commitment is the solid one. Fill is a contrast choice here, never a
   hierarchy one. */
.tour-preview-banner-btn.is-primary {
  background: #FBFAF8;
  color: #1A1A18;
  border-color: #FBFAF8;
  font-weight: 500;
}
/* Off-source offer carries a sentence (page name + question) + two buttons.
   Stack it: the text takes a full row (wrapping, breaking long URLs) and the
   buttons drop to their own row, centred — so they can never overlap the text,
   and the pill stays readable even on a narrow mobile frame. */
.tour-preview-banner.is-offsource {
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  max-width: min(360px, calc(100% - 20px));
  padding: 14px 16px;
}
.tour-preview-banner.is-offsource .tour-preview-banner-text {
  flex: 1 1 100%;
  white-space: normal;
  overflow-wrap: anywhere;
  min-width: 0;
  text-align: center;
}
.tour-preview-banner.is-offsource .tour-preview-banner-btn { flex: 0 0 auto; }

/* Save vs Save as — icon + caret, "Editing: Name" indicator + unsaved dot. */
#setup-save-wrap { align-items: center; gap: 1px; }
.setup-link-indicator {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.01em;
  color: var(--ink65);
  white-space: nowrap;
  margin-right: 6px;
  max-width: 190px;
  overflow: hidden;
}
.setup-link-indicator[hidden] { display: none; }
#setup-link-name {
  color: var(--ink);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 130px;
}
.setup-link-dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink);
}
/* Just the arrow — no grey button fill next to Save (Mark). */
#setup-save-caret { color: var(--ink65); background: transparent; }
#setup-save-caret:hover { color: var(--ink); background: transparent; }
.setup-save-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  min-width: 150px;
  background: var(--paper);
  border: 1px solid var(--bmed);
  border-radius: 10px;
  padding: 4px;
  box-shadow: 0 8px 24px rgba(17, 17, 17, 0.14);
}
.setup-save-menu[hidden] { display: none; }
.setup-save-menu button {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--ink);
  padding: 7px 10px;
  border-radius: 7px;
  cursor: pointer;
}
.setup-save-menu button:hover { background: rgba(14, 14, 12, 0.06); }
/* "New (blank)" is a different kind of action (discard + start over) than
   "Save as new…" — a thin divider separates the two intents. */
#setup-new-blank { margin-top: 4px; border-top: 1px solid var(--bmed); border-radius: 0 0 7px 7px; }
.lab-chrome {
  display: flex;
  align-items: center;
  /* One gap for the whole bar. Clusters set their own internal gap to the same
     number, so nothing in here is spaced by accident. */
  gap: 6px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--ink08);
}
/* Dots are ONLY the aria-hidden spans — the flex spacer and the
   tools cluster must not inherit the dot treatment (the spacer was
   rendering as a giant stretched grey pill). */
.lab-chrome > span[aria-hidden="true"] {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--bmed);
  flex: none;
}
.lab-chrome-url {
  flex: none;
  max-width: 46%;
  margin-left: 10px;
  text-align: left;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink50);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Stage tools in the chrome bar — quiet text buttons + faded status. */
.lab-chrome-tools {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
/* Every cluster's hairline sits the same distance from the thing on each side of
   it: 6px of bar gap + 4px of margin on the left, 10px of padding on the right.
   Symmetric, and identical for both dividers. */
.lab-chrome-tools .ghost-btn {
  font-size: 11px;
  padding: 3px 9px;
  border-color: transparent;
}
.lab-chrome-tools .ghost-btn:hover { border-color: var(--bmed); }
.lab-chrome-tools .lab-status {
  font-size: 10px;
  color: var(--ink32);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-right: 6px;
}
/* The viewport clips the scaled iframe; its height is set from JS to
   deviceHeight × scale so the box hugs the visible page exactly. */
.lab-viewport {
  position: relative;
  overflow: hidden;
}
.lab-scaler {
  transform-origin: top left;
}
.lab-scaler > iframe {
  display: block;
  border: 0;
  background: #fff;
}

/* Preview Lab — test results log (test mode only). */
.lab-results {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--ink08);
}
.lab-results-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.lab-results-title {
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink65); font-weight: 500;}
.lab-results-tally {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink50);
}
.lab-results-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.lab-result-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  background: var(--card-fill);
  border-radius: 10px;
}
.lab-result-dot {
  width: 8px; height: 8px; border-radius: 50%;
  flex: none;
}
.lab-result-dot.is-ok { background: #1D9E75; }
.lab-result-dot.is-block { background: #D85A30; }
.lab-result-host {
  flex: 1; min-width: 0;
  font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lab-result-verdict {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink50);
  flex: none;
}

.lab-allhosts {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--ink50); cursor: pointer; user-select: none;
}
.lab-allhosts input { accent-color: var(--ink); cursor: pointer; }

/* ---------- Nav rethink (Flodesk grammar) ----------
   No slab, no pill backgrounds, no badge chrome. Hierarchy is type
   weight + ink + whitespace. Identical grammar on work, guide and
   settings pages; the collapsed rail keeps the icons. */
.sidebar {
  background: transparent;
  border-radius: 0;
  padding: 26px 10px 22px 2px;
}
.sidebar-label {
  /* #297: this rule never set the font, so it escaped the mono sweep above and
     kept the mono VOICE anyway — 9.5px with 0.18em tracking is a size and a
     rhythm that only make sense in caps. Reset to the heading's own measure;
     the margin, colour and flush inset it exists for are untouched. */
  font-size: 12px;
  letter-spacing: -0.005em;
  color: var(--ink32);
  margin: 28px 10px 10px;
  /* No left inset — the section headings sit flush, not indented past
     the nav items (Mark). Overrides the earlier 10px padding-left. */
  padding-left: 0;
}
.sidebar .sidebar-label:first-child { margin-top: 0; }
.side-item {
  position: relative;
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 13.5px;
  color: var(--ink50);
  transition: color .12s ease;
}
.side-item:hover { background: transparent; color: var(--ink); }
.side-item.is-active {
  /* Grey pill on the current item — easier to see where you are (Mark). */
  background: var(--pill);
  color: var(--ink);
  font-weight: 500;
}
/* (The left-edge ink accent line on the active item was removed — Mark. The
   grey pill alone marks the current item.) */
.side-item .side-icon { opacity: 0.5; transition: opacity .12s ease; }
.side-item:hover .side-icon,
.side-item.is-active .side-icon { opacity: 1; }
/* Pro: a whisper, not a badge. */
.side-item .pro-badge {
  background: transparent;
  border: 0;
  padding: 0;
  margin-left: auto;
  font-size: 8px;
  letter-spacing: 0.14em;
  color: var(--ink32);
}
/* Guide + settings items carry identical placeholder circles — pure
   noise in the expanded nav. Text carries them; icons return in the
   collapsed rail where they're the only handle. */
.app:not(.sidebar-collapsed) .side-item[data-panel^="guide-"] .side-icon,
.app:not(.sidebar-collapsed) .side-item[data-anchor] .side-icon {
  display: none;
}

/* Sidebar geometry merged here so the cascade can't resurrect the
   slab: no grey, no radius, scrolls only when genuinely needed and
   then with an INVISIBLE scrollbar (the visible one read as a grey
   box on guide/settings). */
.sidebar {
  max-height: calc(100vh - 110px);
  overflow-y: auto;
  scrollbar-width: none;
}
.sidebar::-webkit-scrollbar { display: none; }

/* Accordion bodies: controls sized for the 270px column — pills and
   rows wrap and shrink instead of forcing horizontal scroll. */
#canvas-settings { overflow-x: hidden; }
/* …but never clip an open colour picker popover (Mark: "cut off"). */
#canvas-settings.has-pop-open { overflow: visible; }
.canvas-acc-body { min-width: 0; }
.canvas-acc-body .pill-select {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.canvas-acc-body .pill {
  font-size: 10.5px;
  padding: 4px 10px;
}
.canvas-acc-body .row,
.canvas-acc-body .opt-toggle-row { flex-wrap: wrap; }
.canvas-acc-body .num-inp { width: 52px; }
.canvas-acc-body .option-row { min-width: 0; }
.canvas-acc-body .option-summary { padding: 8px 2px; }
.canvas-acc-body .label-block { min-width: 0; }

/* ---------- The shell (frame around the canvas) ----------
   Not one big card: the CHROME — top bar, nav, sequence/options —
   are their own sections divided by hairlines, framing a recessed
   canvas well in the centre. Each column scrolls independently; the
   shell itself never moves. Consistent across every tab and both
   editors. */
body { background: var(--bg); }
.topbar {
  border-bottom: 1px solid var(--ink08);
  padding: 13px 20px;
}
.workspace {
  max-width: none;
  padding: 0;
  column-gap: 0;
  height: calc(100vh - 63px);
  overflow: hidden;
}
.sidebar-col {
  position: static;
  height: 100%;
  overflow-y: auto;
  border-right: 1px solid var(--ink08);
  scrollbar-width: none;
}
.sidebar-col::-webkit-scrollbar { display: none; }
.sidebar { max-height: none; overflow: visible; padding: 16px 16px 22px 22px; height: 100%; box-sizing: border-box; }
/* Nav head: collapse toggle at the top, right-aligned (centred when
   the rail is collapsed). */
.sidebar-head { display: flex; justify-content: flex-end; margin-bottom: 6px; }
.sidebar-head .sidebar-toggle { width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; }
.app.sidebar-collapsed .sidebar-head { justify-content: center; }
/* Nav foot: guide + settings (larger) in a row, theme toggle beneath. */
.sidebar-utils { flex-direction: column; align-items: flex-start; gap: 12px; }
.sidebar-utils-row { display: flex; align-items: center; gap: 6px; }
.sidebar-utils .topbar-util-btn svg { width: 20px; height: 20px; }
.app.sidebar-collapsed .sidebar-utils { align-items: center; }
.app.sidebar-collapsed .sidebar-utils-row { flex-direction: column; gap: 10px; }
.workspace > .main {
  height: 100%;
  overflow-y: auto;
  background: var(--work-bg);
  padding: 24px 28px 40px;
  box-sizing: border-box;
}
.app.dark .workspace > .main { background: rgba(242, 242, 240, 0.055); } /* middle surface step (Framer panel layer) */
.workspace > .preview-panel {
  position: static;
  height: 100%;
  max-height: none;
  overflow-y: auto;
  border: 0;
  border-left: 1px solid var(--ink08);
  border-radius: 0;
  background: var(--bg);
  padding: 14px 28px 22px 22px;
  box-sizing: border-box;
  scrollbar-width: none;
}
/* Options list starts flush with the canvas top — no double rule. */
#canvas-settings { margin-top: 0; }
#canvas-settings .canvas-acc:first-child { border-top: 0; }
.workspace > .main { scrollbar-width: none; }
.workspace > .main::-webkit-scrollbar { display: none; }

/* Device control on the window's chrome bar: quieter, smaller —
   part of the window, not a floating pill. */
.lab-chrome .tb-device { border: 0; padding: 0; gap: 1px; }
.lab-chrome .tb-dev { width: 22px; height: 22px; }

/* ---------- Generous margins (the shell was hugging the window) ---------- */
/* Left pad = sidebar's 32px + the .side-item's own 10px inner pad, so the
   "loey" wordmark lines up with the nav item TEXT edge below it (not the
   nav column's bare edge). Right stays 32px to match the preview panel. */
.topbar { padding: 13px 32px 13px 42px; }
.sidebar { padding: 18px 18px 24px 32px; }
.workspace > .main { padding: 18px 36px 48px; }
/* Canvas mode: the main column holds ONLY the stage, which
   applyTourPreviewZoom height-fits (it leaves a fixed ~28px gap to the
   window bottom itself). The column's own 48px bottom padding then became
   dead, empty scrollable space below a portrait tablet/mobile frame
   (Mark: "I can scroll a bit down, nothing to scroll to"). Drop it in
   canvas mode — the JS gap is the breathing room; non-canvas forms keep
   their 48px so the last field isn't flush. */
.app.canvas-mode .workspace > .main { padding-bottom: 0; }
/* All three columns share an 18px top so the sidebar, the canvas/stage,
   and the options panel start on the same line in EVERY mode (sidebar is
   18px above; main + preview now match it). */
.workspace > .preview-panel { padding: 18px 32px 24px 24px; }
.app.sidebar-collapsed .sidebar { padding: 18px 10px; }

/* Status toast over the canvas well — warnings get their own stage
   instead of squeezing beside the Capture button. */
.topbar-actions-slot .status { display: none; }
/* A wide, short status card spanning the STAGE, centred over the frame bottom.
   position:fixed (out of flow → can't re-create the phantom scroll a sticky
   toast caused under the frame); left/bottom are set in JS from the canvas rect.
   min(460px, 80vw) keeps it readable on ~1–2 lines instead of wrapping into a
   tall squashed blob inside the narrow mobile frame (Mark "all squashed").
   Modest radius reads as a card, not an oval pill. */
#canvas-status-toast {
  position: fixed;
  margin: 0;
  width: fit-content;
  max-width: min(460px, 80vw);
  background: #1A1A18;
  color: #FBFAF8;
  font-size: 12px;
  line-height: 1.45;
  text-align: center;
  border-radius: 20px;
  padding: 10px 18px;
  opacity: 0;
  transform: translate(-50%, 6px);
  transition: opacity .18s ease, transform .18s ease;
  pointer-events: none;
  z-index: 40;
}
#canvas-status-toast.is-show { opacity: 1; transform: translate(-50%, 0); }
#canvas-status-toast.is-error { background: #7a2e22; }
/* Frosted glass over the preview (Mark) — the marker-set / area-drawn toast. */
.app.shell-modetop #canvas-status-toast {
  background: rgba(250, 250, 250, 0.72);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border: 1px solid rgba(14, 14, 12, 0.08); border-radius: 14px; color: var(--ink); font-weight: 450;
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04), 0 14px 36px rgba(14, 14, 12, 0.10);
}
.app.dark.shell-modetop #canvas-status-toast { background: rgba(30, 30, 28, 0.68); border-color: rgba(255, 255, 255, 0.14); color: #FBFAF8; }
.app.shell-modetop #canvas-status-toast.is-error { background: rgba(122, 46, 34, 0.84); border-color: rgba(255, 255, 255, 0.2); color: #FBEAE6; }

/* Chrome-bar icon buttons (Reload beside the URL — browser grammar). */
.chrome-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-left: 4px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink50);
  cursor: pointer;
  flex: none;
}
.chrome-icon-btn:hover { color: var(--ink); background: var(--ink08); }
/* Off the tour page: the reload icon turns amber — it's the way home. */
.chrome-icon-btn.is-alert { color: #F7CE46; }

/* Narrow device frames: shed the chrome decorations so the controls
   that matter (reload · devices · capture) fit one clean row. */
#tour-preview-canvas.is-narrow .lab-chrome > span[aria-hidden="true"] { display: none; }
#tour-preview-canvas.is-narrow .lab-chrome-url { max-width: 60%; }

/* Floating tool column at a narrow frame's right shoulder. */
#embed-preview-pane { position: relative; }
#stage-side-tools {
  position: absolute;
  z-index: 9;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  white-space: nowrap;
}
#stage-side-tools[hidden] { display: none; }
#stage-side-tools .tb-device {
  flex-direction: row;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  padding: 3px;
  gap: 2px;
  background: var(--bg);
}
#stage-side-tools .ghost-btn,
#stage-side-tools #tour-preview-capture {
  font-size: 11px;
  white-space: nowrap;
}

/* "Capture this view" reads as THE capture action — ink pill, same
   voice as the top bar's primary, not a quiet ghost link. */
.lab-chrome-tools #tour-preview-capture {
  background: var(--ink);
  color: var(--bg);
  border: 0;
  border-radius: 100px;
  padding: 4px 14px;
  font-size: 11.5px;
  font-weight: 500;
}
.lab-chrome-tools #tour-preview-capture:hover { opacity: 0.85; border-color: transparent; }
.workspace > .preview-panel::-webkit-scrollbar { display: none; }

/* Source pill: centred, compact, page name + full URL. The top bar
   stacks ABOVE the workspace so its dropdowns (Change source) can
   never hide behind the canvas. */
.topbar { position: relative; z-index: 80; }
.source-bar {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: min(44vw, 560px);
}
/* Compact topbar on narrow widths. The source pill is absolutely centred, so
   on small screens the left (brand/mode) and right (library/guide/settings +
   Save / "Editing <file>") clusters grew UNDERNEATH it — the nav icons hid
   behind the pill and the saved-setup name overlapped (Mark). Below ~1080px
   drop the pill into the flex flow so all three clusters share the row and the
   pill shrinks (truncates) instead of colliding; the editing pill compacts. */
@media (max-width: 1080px) {
  .source-bar {
    position: static;
    transform: none;
    left: auto;
    top: auto;
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
  }
  .source-pill-wrap { min-width: 0; max-width: 100%; }
  .source-pill { min-width: 0; max-width: 100%; }
  .source-addr { min-width: 0; overflow: hidden; }
  .setup-link-indicator { max-width: 110px; }
}

/* ---------- Options, redesigned ----------
   A property list: ONE control per row, label left, control right,
   even 36px rhythm, hairlines between rows. No side-by-side toggle
   cramming, no oversized pills. */
.canvas-acc-body { font-size: 12.5px; }
.canvas-acc-body .row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  margin: 0;
}
.canvas-acc-body .opt-toggle-row,
.canvas-acc-body .row > .label-block + * {
  flex-wrap: nowrap;
}
.canvas-acc-body .opt-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
  border-top: 1px solid var(--ink08);
}
.canvas-acc-body .opt-toggle-row:first-child { border-top: 0; }
.canvas-acc-body .label-block { flex: 1; min-width: 0; }
.canvas-acc-body .label-block .hint { display: none; }
.canvas-acc-body .label-block .label {
  font-size: 12.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.canvas-acc-body .opt-switch { transform: scale(0.85); transform-origin: right center; flex: none; }
.canvas-acc-body .pill-select { gap: 3px; }
.canvas-acc-body .pill { font-size: 11px; padding: 5px 12px; }
/* Layouts option pills match the capture-mode pills (.canvas-acc-body) — the
   base .pill is 13px, which made the Off/Soft/Float/Directional row wrap in the
   layouts panel. Same 11px keeps it on one line at the same panel width. (Mark) */
.mode-layouts #previewPanel .pill-select { gap: 3px; }
.mode-layouts #previewPanel .pill-select .pill { font-size: 11px; padding: 5px 12px; }
.canvas-acc-body .num-stepper { transform: scale(0.92); transform-origin: right center; }
.canvas-acc-body .option-summary { padding: 9px 0; font-size: 12.5px; }
.canvas-acc-body select { font-size: 12px; }

/* Layouts Export format menu (Mark, 25 July 2026): a MOVING board offers
   MP4 or GIF under the Export button; stills export PNG with no menu.
   Body-mounted + position:fixed (coords set from the button rect in JS) —
   inside the action-footer the topbar's strip rule hid it and the slot's
   overflow:hidden clipped it. */
/* THE EXPORT SHEET (21 Sep 2026, Mark: "We need a pop up to appear on export offering different options, size, quality etc.",
   the export sheet mockup: "ok build both"): the modal card, wider, with the choices laid out as pills and a foot that reads back */
.capt-modal-card.lxs { max-width: 860px; padding: 0; overflow: hidden; }
.lxs-head { display: flex; align-items: center; padding: 22px 28px 0; }
.lxs-head h2 { margin: 0; flex: 1; font: 500 20px/1.2 var(--sans); color: var(--ink); }
.lxs-x { width: 28px; height: 28px; border: 0; background: none; color: var(--ink65, rgba(14,14,12,.6)); font-size: 18px; cursor: pointer; border-radius: 7px; }
.lxs-x:hover { color: var(--ink); background: var(--ink08, rgba(14,14,12,.08)); }
.lxs-tabs { display: flex; gap: 22px; padding: 18px 28px 0; border-bottom: 1px solid var(--ink08, rgba(14,14,12,.08)); }
.lxs-tabs button { padding: 0 0 12px; margin-bottom: -1px; border: 0; border-bottom: 2px solid transparent; background: none; font: 400 13.5px/1 var(--sans); color: var(--ink50, rgba(14,14,12,.5)); cursor: pointer; }
.lxs-tabs button.is-on { color: var(--ink); border-color: var(--ink); }
.lxs-body { display: grid; grid-template-columns: 280px 1fr; gap: 30px; padding: 22px 28px 26px; }
.lxs-body.no-prev { grid-template-columns: 1fr; }
.lxs-prevh { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 10px; font: 400 12.5px/1 var(--sans); color: var(--ink); }
.lxs-prevh i { font: 400 11px/1 var(--mono, ui-monospace, Menlo, monospace); font-style: normal; color: var(--ink50, rgba(14,14,12,.5)); }
.lxs-frame { background: var(--paper, #f6f6f4); border-radius: 10px; height: 340px; display: flex; align-items: center; justify-content: center; overflow: hidden; padding: 12px; box-sizing: border-box; }
.lxs-frame img { width: 100%; height: 100%; object-fit: contain; display: block; }   /* the frame fills its column (21 Sep 2026, Mark: "thumbnail tiny") */
.lxs-lbl { font: 500 13px/1 var(--sans); color: var(--ink); margin: 0 0 10px; }
.lxs-lbl:not(:first-child) { margin-top: 20px; }
.lxs-pills { display: grid; gap: 8px; grid-template-columns: repeat(4, 1fr); }
.lxs-pills.n1 { grid-template-columns: repeat(2, 1fr); } .lxs-pills.n2 { grid-template-columns: repeat(2, 1fr); } .lxs-pills.n3 { grid-template-columns: repeat(3, 1fr); }
.lxs-pill { border: 1px solid var(--bmed, rgba(14,14,12,.16)); border-radius: 8px; padding: 10px 8px 9px; background: transparent; text-align: center; font: 400 12.5px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); cursor: pointer; }
.lxs-pill small { display: block; margin-top: 5px; font: 400 10px/1 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink50, rgba(14,14,12,.5)); }
.lxs-pill:hover { border-color: var(--ink); color: var(--ink); }
.lxs-pill.is-on { border-color: var(--ink); color: var(--ink); background: var(--paper, #f6f6f4); }
.lxs-note { margin: 10px 0 0; font: 400 12px/1.5 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); }
.lxs-more { display: flex; align-items: center; justify-content: space-between; width: 100%; margin-top: 22px; padding: 16px 0 0; border: 0; border-top: 1px solid var(--ink08, rgba(14,14,12,.08)); background: none; font: 400 13px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); cursor: pointer; }
.lxs-more:hover { color: var(--ink); }
.lxs-morebody { margin-top: 14px; display: grid; gap: 12px; }
.lxs-morebody[hidden] { display: none; }   /* the grid display beat the hidden attribute, and the fold showed its rows folded (21 Sep 2026) */
.lxs-opt { display: flex; align-items: center; justify-content: space-between; gap: 12px; font: 400 12.5px/1 var(--sans); color: var(--ink); }
.lxs-mini { display: flex; gap: 6px; }
.lxs-mini button { padding: 6px 10px; border: 1px solid var(--bmed, rgba(14,14,12,.16)); border-radius: 6px; background: transparent; font: 400 11.5px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); cursor: pointer; }
.lxs-mini button.is-on { border-color: var(--ink); color: var(--ink); }
.lxs-opt input { width: 240px; height: 30px; box-sizing: border-box; border: 1px solid var(--bmed, rgba(14,14,12,.16)); border-radius: 6px; padding: 0 9px; font: 400 12px/1 var(--sans); color: var(--ink); background: var(--paper, #fff); }
.lxs-opt input:focus { outline: none; border-color: var(--ink); }
.lxs-range { display: inline-flex; align-items: center; gap: 10px; width: 240px; }
.lxs-range input[type="range"] { flex: 1; width: auto; height: auto; border: 0; padding: 0; background: none; accent-color: var(--ink); }
.lxs-range i { font: 400 11px/1 var(--mono, ui-monospace, Menlo, monospace); font-style: normal; color: var(--ink50, rgba(14,14,12,.5)); min-width: 34px; text-align: right; }
.lxs-foot { display: flex; align-items: center; gap: 10px; padding: 16px 28px; border-top: 1px solid var(--ink08, rgba(14,14,12,.08)); background: var(--paper, #f6f6f4); }
.lxs-read { flex: 1; font: 400 11.5px/1.3 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink50, rgba(14,14,12,.5)); }
.layouts-export-menu {
  position: fixed; z-index: 200;
  display: flex; flex-direction: column; gap: 2px; padding: 5px;
  background: var(--paper); border: 1px solid var(--bmed); border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.16); min-width: 170px;
}
.layouts-export-menu button {
  border: 0; background: transparent; color: var(--ink); cursor: pointer;
  font-family: var(--sans); font-size: 12.5px; text-align: left;
  padding: 8px 11px; border-radius: 7px; white-space: nowrap;
}
.layouts-export-menu button:hover { background: var(--ink08); }
.layouts-export-menu button:disabled { opacity: 0.4; cursor: default; }

/* Primary action, quieter still — hugs its text (Mark: way too long). */
.topbar-actions-slot .btn-primary,
#topbar-actions-slot .btn-primary {
  padding: 8px 16px;
  font-size: 12px;
  width: auto;
  margin: 0;
  border-radius: 100px;
}


/* ════════ OPTIONS — single source of truth ════════
   #canvas-settings (id) outranks every scattered earlier rule, ending
   the specificity war that kept half the option styling from applying
   (Smooth/Natural overflow included). Design:
   – Accordion HEADINGS are Söhne medium, sentence-case, ink, in a tonal
     band — they read as interactive section toggles, NOT labels. (Was
     mono-caps, which made them indistinguishable from the area labels;
     mono-caps is now reserved for the quiet labels so it does one job.)
   – Option LABELS are sentence-case sans in ink — the readable layer.
   – ONE control per row, label left, control right, hairline rhythm.
   – Toggles single column in every mode; pills wrap, sized to fit. */
#canvas-settings .canvas-acc {
  border: 0;
  background: transparent;
}
/* No divider line between the bands (Mark): the filled band bars + the
   gap between them already separate the sections; the hairline on top of
   each was a leftover and read as a stray underline. */
/* Option B (Mark): each section title is a tonal BAND — a subtle filled
   bar with the title in full ink — so the headers read as distinct bands
   above the bordered option rows, not faint metadata that gets lost. */
#canvas-settings .canvas-acc > summary {
  list-style: none;
  cursor: pointer;
  padding: 10px 12px;
  background: var(--acc-band);
  border-radius: 8px;
  /* Söhne MEDIUM, sentence-case — reads as an interactive section
     toggle, NOT a label. The mono-caps treatment is now reserved for
     the quiet area labels (SOURCE / SEQUENCE / eyebrows), so it does
     one job instead of three and the hierarchy stops reading flat. */
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
#canvas-settings .canvas-acc > summary:hover { background: var(--acc-band-hover); color: var(--ink); opacity: 1; }
#canvas-settings .canvas-acc-body {
  padding: 2px 2px 16px;
  font-size: 12.5px;
  min-width: 0;
}
#canvas-settings .canvas-acc-body .field-label,
#canvas-settings .canvas-acc-body .label,
#canvas-settings .canvas-acc-body .option-label {
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}
/* Rows: stack any multi-control row; each toggle unit becomes its own
   full-width row with the switch pinned right. */
#canvas-settings .canvas-acc-body .row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  margin: 0;
}
#canvas-settings .canvas-acc-body .opt-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 0;
  border-top: 1px solid var(--ink08);
  flex-wrap: nowrap;
}
#canvas-settings .canvas-acc-body .opt-toggle-row:first-child { border-top: 0; }
#canvas-settings .canvas-acc-body .label-block { flex: 1; min-width: 0; }
#canvas-settings .canvas-acc-body .label-block .hint { display: none; }
#canvas-settings .canvas-acc-body .hint { font-size: 11px; color: var(--ink50); }
#canvas-settings .canvas-acc-body .opt-switch {
  transform: scale(0.85);
  transform-origin: right center;
  flex: none;
}
/* Pills (Smooth/Natural, speeds, easings): wrap, compact, never
   overflow the column. */
#canvas-settings .pill-select {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  max-width: 100%;
}
#canvas-settings .pill-select .pill {
  font-size: 11px;
  padding: 5px 12px;
  white-space: nowrap;
}
#canvas-settings .num-stepper { transform: scale(0.92); transform-origin: right center; }
/* …but LEFT-origin where the stepper sits under its own label rather than at
   the right of a label/control row. Scaling from the right moves the left edge
   in by 8px on a ~100px stepper, which is precisely the indent Mark saw ("why
   indented?") — the control was aligned to a right edge that no longer exists
   in this rail. */
#canvas-settings[data-canvas-panel="video-smooth"] .field-stack .num-stepper {
  transform-origin: left center;
}
#canvas-settings .option-row { min-width: 0; }
#canvas-settings .option-summary { padding: 9px 0; font-size: 12.5px; }
#canvas-settings select { font-size: 12px; max-width: 100%; }
#canvas-settings input.text-input { max-width: 100%; box-sizing: border-box; }
/* motion-line rows (Scroll style, Speed, Easing): label ABOVE the
   pills at this width — side-by-side clipped the last pill. Controls
   keep their natural width (stretched full-width pills/steppers read
   as a mess). */
#canvas-settings .motion-line {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
#canvas-settings .pill-select { width: fit-content; }
#canvas-settings .num-stepper,
#canvas-settings .num-inp-unit { width: fit-content; }
#canvas-settings .row > .label-block { margin-bottom: 4px; }
#canvas-settings .motion-extras { margin-top: 8px; }

/* ══ ONE CONTROL SIZE IN THE SIDE PANEL ═══════════════════════════════════
   Mark, 11 Aug 2026: "this toggle is bigger than the toggle in page capture
   mode settings. make this and any other big ones smaller." And: "the +-
   steppers are big as well in layouts. need consistency across the app."

   The compaction was scoped to #canvas-settings — the rail the capture modes
   use. Layouts renders its settings straight into #previewPanel and never
   passed through it, so it kept the full-size 44x24 switch and the full
   stepper while every capture mode showed a smaller one, side by side in the
   same column of the same screen.

   Scoped to the PANEL rather than the rail, so anything that renders settings
   there is the same size by default and nobody has to remember to opt in.
   Modals and Settings keep the full size — a control on its own page is not
   competing for a 260px column. */
#previewPanel .opt-switch { transform: scale(0.85); transform-origin: right center; flex: none; }
#previewPanel .num-stepper { transform: scale(0.92); transform-origin: right center; }

/* THE two-column toggle culprit: pairs live in .opt-toggle-grid (a
   2-col grid) which the earlier row rules never touched. One column,
   everywhere. */
#canvas-settings .opt-toggle-grid {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* Air between options — the panel read as crammed. (Band padding lives
   with the tonal-band rule above; this no longer overrides it.) */
#canvas-settings .canvas-acc-body { padding: 16px 2px 20px; }
#canvas-settings .canvas-acc-body .opt-toggle-row { padding: 12px 0; }
#canvas-settings .option-summary { padding: 12px 0; }
#canvas-settings .motion-line { gap: 8px; margin-bottom: 14px; }
#canvas-settings .canvas-acc-body .row { margin-bottom: 10px; }
#canvas-settings .canvas-acc-body .option-row { margin-bottom: 2px; }

/* The route readout duplicated what the Start/End cards + SCROLL
   accordion already say — and sat confusingly right above the SCROLL
   heading. Rail keeps the actions, drops the readout. */
#previewPanel.is-tour-sequence .tour-list-footer .page-range-summary { display: none; }

/* Classic view: the Visual Editor entry point sits at the very top of
   the panel, above Device — a deliberate dark pill, same voice as the
   primary action, identical in every mode. */
.ve-launch,
button.ghost-btn.ve-launch {
  display: inline-flex;
  align-items: center;
  background: var(--ink);
  color: var(--bg);
  border: 0;
  border-radius: 100px;
  padding: 9px 20px;
  font-size: 13px;
  font-weight: 500;
  margin: 0 0 22px;
  width: fit-content;
}
.ve-launch:hover { opacity: 0.85; }

/* ---------- Site Flow in the rail (P16) ----------
   The frame does the SHOWING now — cards become a slim map of the
   flow: clickable page rows (active page marked), compact stop rows
   (shared rail grammar), transitions as a quiet connector line
   instead of the dark bridge. Thumbnails retired — the live frame
   replaced them. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group, #previewPanel.is-tour-sequence #manrec-markers .tour-group {
  background: transparent;
  border-radius: 0;
  padding: 0;
  margin: 0;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-page, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-page {
  margin-bottom: 10px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-header, #previewPanel.is-tour-sequence #manrec-markers .tour-group-header {
  border: 0;
  padding: 8px 10px;
  border-radius: 10px;
  cursor: pointer;
  font-size: 11px;
  display: flex;
  align-items: center;
  gap: 8px;
  /* Every page row lives in a quiet container — un-viewed pages were
     floating as bare text and got lost between the cards. */
  background: var(--card-fill);
  box-shadow: 0 0 0 1px var(--bmed) inset;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-header:hover, #previewPanel.is-tour-sequence #manrec-markers .tour-group-header:hover { background: var(--ink08); }
#previewPanel.is-tour-sequence #tour-multi-pages .is-active-page .tour-group-header, #previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-header {
  background: var(--bg);
  box-shadow: 0 0 0 1.5px var(--ink) inset;
}
#previewPanel.is-tour-sequence #tour-multi-pages .is-active-page .tour-group-header::after, #previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-header::after {
  content: 'VIEWING';
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.08em;
  color: var(--ink50);
  margin-left: auto;
  flex: none;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-host, #previewPanel.is-tour-sequence #manrec-markers .tour-group-host {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  /* It's a page NAME now (Home / Charter), not a URL — read it in the
     normal font + sentence case, not mono-uppercase which looked like a
     code label (Mark). */
  font-family: var(--sans);
  text-transform: none;
  letter-spacing: 0;
  font-size: 12.5px;
}
/* Thumbnails + heavyweight chrome retired in the rail. */
#previewPanel.is-tour-sequence #tour-multi-pages img,
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-thumb,
#previewPanel.is-tour-sequence #tour-multi-pages [class*="thumb"],
#previewPanel.is-tour-sequence #manrec-markers img,
#previewPanel.is-tour-sequence #manrec-markers .tour-flow-thumb,
#previewPanel.is-tour-sequence #manrec-markers [class*="thumb"] {
  display: none;
}
/* Transitions: light bridge (Mark) — rail-sized, compact, shadowless,
   truncating cleanly. Matches the page cards instead of a dark cut. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded), #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) {
  /* Chrome OFF in the rail (Mark, 19 July: the boxed transition read
     messy) — the pick-link button carries its own outline; the section
     sits bare between the page cards, nudged up so the gaps read equal. */
  margin-top: -2px;
  background: transparent;
  border: 0;
  border-radius: 10px;
  /* Horizontal padding matches the stop card (12px) so the Edit/× line up
     down the right edge of every card (Mark). */
  padding: 4px 12px 6px;
  font-size: 11px;
  box-shadow: none;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
}
/* Rail layout: STACKED, nothing truncates. The 38px disc and the
   side-by-side prompt+button were built for the wide column — in the
   rail they fought for 270px and the prompt read "the ne…". Eyebrow,
   full prompt (wraps), then a compact action row. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-disc, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-disc { display: none; }
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head {
  flex-wrap: wrap;
  gap: 10px 12px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-body, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-body {
  flex: 1 1 100%;
  gap: 5px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-link, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-link {
  min-width: 0;
  white-space: normal;
  overflow: visible;
  font-size: 13px;
  line-height: 1.45;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-eyebrow, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-eyebrow {
  /* Same title voice as every other card — the 9px micro-type made
     "Link to Page 02" read as a different design (Mark). Size comes
     from the base eyebrow rule (11.5px sans 500). */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-edit, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-edit {
  padding: 6px 14px;
  font-size: 11.5px;
  align-self: flex-start;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-connector-line, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-connector-line { display: none; }
/* "Show all steps" opens the capture-window marker overlay — redundant
   in the Visual Editor, where markers live on the preview itself. */
#previewPanel.is-tour-sequence [data-tour-multi-show-annotations] { display: none; }
/* Expanded transition editing keeps the standard light card. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded {
  box-shadow: none;
  padding: 12px;
  font-size: 12px;
}

/* Narrow-frame view buttons: fixed beside the canvas's right edge,
   pinned to the bottom of the viewport so they're always visible. */
#stage-side-foot {
  position: fixed;
  z-index: 30;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 18px;
}
/* Both view switches read as quiet text — no pill chrome down here. */
#stage-side-foot .ghost-btn {
  border: 0;
  background: transparent;
  padding: 0;
  border-radius: 0;
  box-shadow: none;
  color: var(--ink-soft);
  font-size: 12.5px;
}
#stage-side-foot .ghost-btn:hover { color: var(--ink); background: transparent; }
#stage-side-foot[hidden] { display: none !important; }

/* Narrow-frame pick banner: fixed side panel in the space left of the
   frame — fixed positioning escapes the canvas's overflow:hidden. */
.tour-preview-banner.is-side {
  position: fixed;
  right: auto;
  transform: none;
  z-index: 30;
  width: 238px;
  flex-flow: row wrap;
  gap: 8px 8px;
  border-radius: 14px;
  padding: 13px 16px 12px;
}
.tour-preview-banner.is-side .tour-preview-banner-text { flex: 1 0 100%; }

/* Site Flow: wider rail — page headers + transition bridges need the
   extra line length. */
.workspace.tour-preview-mode.rail-wide {
  grid-template-columns: 200px minmax(0, 1fr) minmax(316px, 372px);
}
.app.sidebar-collapsed .workspace.tour-preview-mode.rail-wide {
  grid-template-columns: 64px minmax(0, 1fr) minmax(316px, 372px);
}

/* ── Transition node: empty-state action row ──────────────────────
   Prompt reads as one line of text; Pick link + Enter a URL sit
   together beneath it as one decision. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-actions, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 12px;
  margin-top: 12px;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option {
  margin: 0;
  /* Tight inline box so the "Enter a URL" pill sits level with the Pick
     buttons — a block <details> added line-box descender space that pushed
     it up out of line (Mark). Column keeps the input row stacked under it
     when the option is opened. */
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option > summary, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option > summary {
  display: inline-flex;
  align-items: center;
  /* EXACTLY the same box as the filled Pick buttons (padding 7px 14px, no
     border) so the three sit level. The outline is an inset box-shadow,
     which doesn't add to the box height the way a 1px border did — that
     extra 2px is what made "Enter a URL" stand taller (Mark). */
  padding: 6px 14px;
  border: 0;
  box-shadow: inset 0 0 0 1px rgba(251, 250, 248, 0.28);
  border-radius: 8px;
  font-family: var(--sans);
  font-size: 12px;
  color: #FBFAF8;
  text-decoration: none;
  cursor: pointer;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option > summary::before, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option > summary::before { content: none; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option > summary:hover, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option > summary:hover {
  background: rgba(251, 250, 248, 0.1);
  text-decoration: none;
}
/* Open: the input row takes the full card width below the buttons. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option[open], :is(#manrec-markers, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option[open] { flex: 1 1 100%; }
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option[open] > summary, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-actions .tour-flow-url-option[open] > summary { margin-bottom: 8px; }

/* ── Rail: EXPANDED transition editing, decluttered ───────────────
   The wide-column spec sheet (64px role column, 18px link type,
   nowrap URLs) collapsed into mush at 280px. Stack each row: small
   mono role heading, control below, hairline between rows. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded {
  margin-top: 16px;
  padding: 14px 16px;
  /* Light card on a light rail blended away — same strong ring as the
     VIEWING page header marks "this is what you're editing". */
  background: var(--bg);
  border-radius: 10px;
  box-shadow: 0 0 0 1.5px var(--ink);
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded .tour-flow-spine-disc, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded .tour-flow-spine-disc { display: none; }
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded .tour-flow-spine-link, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded .tour-flow-spine-link {
  font-size: 13px;
  white-space: normal;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded .tour-flow-spine-row, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded .tour-flow-spine-row {
  display: block;
  padding: 12px 0;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded .tour-flow-spine-role, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded .tour-flow-spine-role {
  display: flex;
  flex: none;
  margin-bottom: 8px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded .tour-flow-spine-flink, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded .tour-flow-spine-flink {
  font-size: 13px;
  white-space: normal;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded .tour-flow-spine-fdest, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded .tour-flow-spine-fdest {
  font-size: 10.5px;
  white-space: normal;
  word-break: break-all;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node.is-expanded .tour-flow-spine-follow-row, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node.is-expanded .tour-flow-spine-follow-row {
  flex-wrap: wrap;
  gap: 8px 12px;
}

/* ── Manual Recording rail: page-grouped markers (P17) ────────────
   Mirrors the Site Flow rail grammar — quiet page-header cards,
   strong ring + VIEWING on the page the frame is showing. */
#previewPanel.is-tour-sequence #manrec-markers .tour-group {
  background: transparent;
  border-radius: 0;
  padding: 0;
  margin: 0;
}
#previewPanel.is-tour-sequence #manrec-markers .manrec-page {
  margin-bottom: 14px;
}
#previewPanel.is-tour-sequence #manrec-markers .tour-group-header {
  border: 0;
  padding: 8px 10px;
  border-radius: 10px;
  cursor: pointer;
  font-size: 11px;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--card-fill);
  box-shadow: 0 0 0 1px var(--bmed) inset;
}
#previewPanel.is-tour-sequence #manrec-markers .tour-group-header:hover { background: var(--ink08); }
#previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-header {
  background: var(--bg);
  box-shadow: 0 0 0 1.5px var(--ink) inset;
}
#previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-header::after {
  content: 'VIEWING';
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.08em;
  color: var(--ink50);
  margin-left: auto;
  flex: none;
}
#previewPanel.is-tour-sequence #manrec-markers .tour-group-host {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Add-page row: input + button stay on one line in the rail. */
#previewPanel.is-tour-sequence #manrec-list-footer { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
#previewPanel.is-tour-sequence #manrec-list-footer .text-input { font-size: 11px; }

/* ── Flyout polish (Mark): motion + tighter type ──────────────────
   Open: slides in from the page edge. Close: contracts back (the JS
   defers hiding 150ms so the exit can play). The open animation only
   replays on a real display toggle — re-renders while open stay calm. */
@keyframes captFlyIn {
  from { opacity: 0; transform: translateX(16px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes captFlyOut {
  to { opacity: 0; transform: translateX(12px) scale(0.99); }
}
.tour-flyout { animation: captFlyIn 0.2s cubic-bezier(0.2, 0.7, 0.3, 1); }
.tour-flyout.is-closing { animation: captFlyOut 0.15s ease forwards; pointer-events: none; }

/* Type scale: one consistent grammar — 9.5px mono eyebrows for field
   labels, 11.5px controls, 13px name. Everything fits 420px without
   wrapping surprises. */
.tour-flyout .tour-field-label {
  /* Inherits the sans title token from the base rule — flyout only
     tunes colour (quieter against the white flyout card). */
  color: var(--ink50);
}
.tour-flyout .tour-section-label-input,
.tour-flyout .tour-section-label { font-size: 13px; }
.tour-flyout .tour-section-num { font-size: 10px; }
.tour-flyout .ghost-btn { font-size: 11.5px; padding: 5px 12px; }
.tour-flyout .picked-target-name { font-size: 11.5px; }
.tour-flyout .tour-interaction-target-name {
  font-size: 11.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 180px;
}
.tour-flyout .wait-inline { font-size: 11px; }

.tour-flyout .num-inp { font-size: 11.5px; }
.tour-flyout .stop-timing-unit,
.tour-flyout .stop-timing-sep { font-size: 11px; }
.tour-flyout .tour-section-card { padding: 16px 18px; }
.tour-flyout .tour-action-sub { margin-left: 0; }
.tour-flyout .tour-field-row { margin-top: 14px; }

/* ── HOLD row inline in the flyout (Mark) — same label·control line as
   POSITION/ACTIONS. With actions present ("2.5s before · 1s after")
   the steppers stay nowrap on one line; inputs slimmed to fit. */
.tour-flyout .tour-field-control.stop-timing {
  flex-basis: auto;
  flex: 1 1 auto;
  flex-wrap: nowrap;
  min-width: 0;
  white-space: nowrap;
}
.tour-flyout .tour-field-control.stop-timing .num-inp { width: 52px; }

/* ── Collapsed-card action icons ── */
.tour-actions-toggle svg:not(:last-child) { margin-right: 1px; }
.tour-actions-toggle { display: inline-flex; align-items: center; gap: 4px; }

/* ── View buttons in the well corner (Mark): Open full size reads as a
   REAL button; Classic editor stays quiet text beside it. */
#stage-side-foot #tour-preview-fullsize {
  background: var(--bg);
  border: 1px solid var(--bmed);
  border-radius: 100px;
  padding: 8px 18px;
  font-size: 12px;
  color: var(--ink);
  box-shadow: 0 8px 22px rgba(17, 17, 17, 0.10);
}
#stage-side-foot #tour-preview-fullsize:hover {
  border-color: var(--ink);
  background: var(--bg);
  color: var(--ink);
}
#stage-side-foot #tour-preview-toggle {
  border: 0;
  background: transparent;
  padding: 0;
  color: var(--ink-soft);
  font-size: 12px;
}
#stage-side-foot #tour-preview-toggle:hover { color: var(--ink); background: transparent; }

/* Collapsed nav: the light/dark toggle goes VERTICAL — the 64px rail
   can't fit two pills side by side (Mark). */
.app.sidebar-collapsed .mode-toggle {
  flex-direction: column;
  gap: 4px;
  padding: 3px;
}

/* Flat canvas option groups (e.g. Page Capture's capture range): the
   step label reads as the same eyebrow the accordions use; content
   sits permanently open below it. */
/* No divider under the flat Capture range / Capture Mode block — the
   tonal accordion bands below already separate it (Mark). */
#canvas-settings .canvas-flat { padding: 4px 2px 18px; }
#canvas-settings .canvas-flat .step-label::before { content: none; }
/* The flat group's step heading is redundant in the rail — the mode
   title directly above already says what this is (Mark). */
#canvas-settings .canvas-flat .step-label { display: none; }
/* …EXCEPT a flat group that isn't the primary one (e.g. Motion) — it
   needs its own eyebrow so the always-visible block reads as a labelled
   section, not loose pills. Same mono-caps voice as the accordions. */
/* ══ RANGE RAIL BEGIN ════════════════════════════════════════════════════════
   Mark, 11 Aug 2026. A kept flat label is a QUIET NAME, not a band. The rule
   the whole rail now follows: a band is something you press — Styling and Hide
   elements open — and a mono label just names a group you cannot press. Keeping
   those two apart is what stops eight sections reading as eight equal headers.
   (This selector previously drew a band and was used by nothing.) */
#canvas-settings .canvas-flat .step-label.flat-keep-label {
  display: flex;
  align-items: center;
  gap: 8px;
  /* SANS, not mono (Mark, 11 Aug 2026: "mono should not be used for titles").
     Same voice as the accordion summaries below — Söhne medium, sentence case —
     so a section that opens and a section that does not read as the same kind of
     thing, told apart by the band rather than by the typeface. Mono is left to
     what it is for here: the quiet START / END labels and the metadata line. */
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  background: transparent;
  border-radius: 0;
  padding: 0;
  /* .step-label's own 52px top margin is for a form's steps down a wide panel.
     In the rail these are section names in a 260px column and the rules above
     them do the separating, so the margin is zeroed rather than fought. */
  margin: 0 0 8px;
}
/* The kept label can carry one thing on its right: the staging switch, or the
   Rendering chip. Both push to the edge; nothing else is allowed in here. */
#canvas-settings .canvas-flat .step-label.flat-keep-label .flat-lbl-txt {
  display: flex; align-items: center; gap: 7px; flex: 1; min-width: 0;
}
#canvas-settings .canvas-flat .step-label.flat-keep-label.has-switch,
#canvas-settings .canvas-flat .step-label.flat-keep-label.has-chip { margin-bottom: 6px; }
/* HOLDS ITS SPACE when it goes (Mark: "the recommended tag disappears and
   makes everything move"). visibility, not display — a chip that only exists on
   Auto must not be able to reflow the four sections under it. */
.flat-lbl-chip.is-off { visibility: hidden; }
.flat-lbl-chip {
  flex: none;
  font-family: var(--sans); font-size: 10.5px; font-weight: 500;
  letter-spacing: 0; text-transform: none;
  background: var(--ink08); color: var(--ink-soft);
  padding: 3px 8px; border-radius: 5px;
}
/* START / END — a step below the section name, so the pair reads as two parts
   of Range rather than two more sections. */
.page-sub-lbl {
  margin: 0 0 7px;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--ink-mute);
}
/* ONE PILL LANGUAGE. Start, End, Crop ratio and Rendering are all
   .ghost-btn.is-choice now — the control the ratio chips already used — wrapped
   so a long label drops to the next line instead of squeezing its neighbours,
   which is the failure the segmented track could not avoid. */
.page-pills, .pr-list.is-compact { display: flex; flex-wrap: wrap; gap: 7px; }
/* The pills are left at the size the rail already gave them (Mark, 11 Aug 2026:
   "I only meant the preview crop one. revert the other buttons, they were ok").
   Only the corner is set here. NOTE for anyone sizing them later:
   `#previewPanel.is-tour-sequence .ghost-btn` — a rule written for the tour
   sequence list — squashes every ghost button in this container to 11px/4px, so
   a plain .page-pills rule will not win. It takes an id in the selector. */
.page-pills .ghost-btn.is-choice,
.pr-list.is-compact .pill {
  border-radius: 9px;
  min-height: 0;
}
/* The rendering pills borrow the choice pill's dress without borrowing
   .pill-select's shared track — the group keeps its data-bind, so the existing
   bound-pill handler still writes; only the presentation changed. */
.pr-list.is-compact { background: transparent; padding: 0; border: 0; }
.pr-list.is-compact .pill {
  background: transparent; border: 1px solid var(--rule-faint); color: var(--ink);
  font-weight: 400;
}
.pr-list.is-compact .pill:hover:not([disabled]) { border-color: var(--ink20); background: rgba(14, 14, 12, .04); }
.pr-list.is-compact .pill.is-selected { background: var(--ink); border-color: var(--ink); color: var(--paper); }
/* …and STAYS black under the hand. :hover:not() out-specifies .is-selected on
   its own, so the selected pill went grey-on-white the moment you passed over
   it (Mark, 11 Aug 2026). */
.pr-list.is-compact .pill.is-selected:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.pr-list.is-compact .pill[disabled] { opacity: .38; cursor: default; }
/* The line under a flat section: one sentence, never a paragraph. */
.page-flat-note {
  margin: 9px 0 0;
  font-family: var(--sans); font-size: 12px; line-height: 1.5; color: var(--ink-mute);
}
/* PREVIEW CROP — an outline button, because it is a view action. It used to
   wear .ghost-btn.is-add, the app's dashed MAKE-SOMETHING affordance, and was
   the loudest thing in the group. */
/* Same height as the ratio row it sits under (Mark) — it is one more control in
   that group, not a footer for it. */
#canvas-settings .see-render-btn,
.see-render-btn {
  width: 100%; margin-top: 8px; gap: 8px;
  justify-content: center; border-radius: 9px;
  padding: 0 14px; height: 38px; font-size: 12.5px;
  background: transparent; border: 1px solid var(--rule-faint); color: var(--ink);
}
.see-render-btn:hover { background: rgba(14, 14, 12, .05); border-color: var(--ink20); }
/* A run of accordions under one quiet name (Prepare the page). Same voice as a
   kept flat label, for the same reason: it names, it does not open. */
.canvas-group-lbl {
  margin: 22px 0 2px;
  /* Sans, like every other title in the rail — mono is for quiet labels and
     metadata, not headings (Mark, 11 Aug 2026). */
  font-family: var(--sans); font-size: 13px; font-weight: 500;
  letter-spacing: 0; text-transform: none; color: var(--ink);
}
/* Section rhythm in the Page Capture rail. Several flat groups run one after
   another here, so they get a hairline between them and tighter padding — the
   dividers do the separating that a card border used to, at a fraction of the
   weight. Scoped to this panel: every other rail has one flat group and would
   gain a rule with nothing on the other side of it. */
/* PAGE SCROLL SHARES THIS RAIL LANGUAGE (Mark, 19 Aug 2026: "lets style the
   right panel similar to that of page capture"). Added to the selector rather
   than copied into a second block, so the two rails cannot drift the way the
   pills had. */
#canvas-settings[data-canvas-panel="stills-page"] .canvas-flat,
#canvas-settings[data-canvas-panel="video-smooth"] .canvas-flat { padding: 0 2px 2px; }
#canvas-settings[data-canvas-panel="stills-page"] .canvas-flat + .canvas-flat,
#canvas-settings[data-canvas-panel="video-smooth"] .canvas-flat + .canvas-flat {
  border-top: 1px solid var(--rule-faint);
  margin-top: 17px;
  padding-top: 17px;
}
/* A hidden group must not leave its rule and its air behind — Frame sections
   only exists for Browsing and Flick. pageScrollSyncRows sets this. */
#canvas-settings[data-canvas-panel="video-smooth"] .canvas-flat.vtl-group-off { display: none; }
/* …except the crop section and the summary, which are continuations of Range
   rather than sections of their own — no rule, just air. */
#canvas-settings[data-canvas-panel="stills-page"] .canvas-flat:has(#page-crop-lbl) {
  border-top: 0; margin-top: 15px; padding-top: 0;
}
/* The summary is a footnote to the range, not a section under it. */
#canvas-settings[data-canvas-panel="stills-page"] .canvas-flat:has(#page-range-summary) {
  border-top: 0; margin-top: 0; padding-top: 0;
}
/* A hidden crop section must not leave its air behind. */
#canvas-settings[data-canvas-panel="stills-page"] .canvas-flat:has(#page-crop-lbl[hidden]) { display: none; }
/* The two bands close the rail, so they get the same rule between them and the
   section above that every other break gets. */
#canvas-settings[data-canvas-panel="stills-page"] .canvas-flat + .canvas-acc {
  border-top: 1px solid var(--rule-faint);
  margin-top: 17px;
  padding-top: 17px;
}
/* Page Scroll dropped its flat Style/Speed bands into the Scroll Style
   dropdown (25 Aug 2026, Mark: "remove the hairlines") — with only the rail
   above, the accordions start ruleless, exactly as Page Tour's do. */
#canvas-settings[data-canvas-panel="video-smooth"] .canvas-flat + .canvas-acc { margin-top: 17px; }
#canvas-settings[data-canvas-panel="stills-page"] .canvas-group-lbl { margin: 24px 0 4px; }
/* ══ RANGE RAIL END ═════════════════════════════════════════════════════════ */

/* Soft info note on action cards (target outside the stop's view) —
   informational tone, not the amber warning: the engine handles it,
   the note just says HOW. */
.tour-interaction-note {
  margin: 2px 0 8px 28px;
  padding: 6px 0 6px 10px;
  border-left: 2px solid var(--bmed);
  font-size: 11px;
  line-height: 1.5;
  color: var(--ink50);
}

/* ── Timeline LAB ── */
.tl-kind-chip {
  flex: none;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--ink);
  color: var(--bg);
  border-radius: 100px;
  padding: 3px 10px;
}
.tour-section-card.tl-bare { background: var(--bg); border: 1px dashed var(--bmed); }
#timeline-sections { display: flex; flex-direction: column; gap: 14px; }

/* ── Canvas options: read the hierarchy (Mark) ────────────────────
   Two levels of disclosure were styled almost identically. Outer
   accordion summaries stay the mono-caps eyebrows; INNER option rows
   become clearly subordinate — smaller sans labels, quiet mono value,
   smaller chevron, indented — and their expanded body hangs off a
   left guide line so "child of" reads at a glance. */
#canvas-settings .canvas-acc-body .option-row { padding-left: 10px; }
#canvas-settings .canvas-acc-body .option-summary .option-label {
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-soft);
}
#canvas-settings .canvas-acc-body .option-summary:hover .option-label { color: var(--ink); }
#canvas-settings .canvas-acc-body .option-summary .option-value {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink50);
}
#canvas-settings .canvas-acc-body .option-summary .option-chevron {
  width: 8px;
  height: 8px;
  color: var(--ink32);
}
#canvas-settings .canvas-acc-body .option-expand {
  margin: 4px 0 12px;
  padding: 12px 14px;
  background: var(--ink08);
  border-radius: 10px;
}

/* ── Boxed setting rows (Mark) ─────────────────────────────────────
   The flat hairline list let the dropdowns blend together. Each option
   now sits in its own paper-fill box with a soft border + rounded
   corners, so every control reads as a distinct unit and the option
   value / chevron stand out. The whole row is the box; the inner toggle
   or summary keeps the vertical rhythm, the box adds the sides. */
/* Flat rows, no dividers (Mark: the per-row boxes read "way too cardy",
   then the hairlines between them were "thin lines" to drop too — the
   section BANDS alone carry the structure now). */
#canvas-settings .canvas-acc-body .option-row {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0 12px;
  margin: 0;
}
#canvas-settings .canvas-acc-body .option-row .opt-toggle-row,
#canvas-settings .canvas-acc-body .option-row .option-summary { padding: 11px 0; }
/* Expanded body: the hairline sits BELOW the controls (dividing the open
   row from the next row), not between the summary and its own settings
   (Mark, 16 July 2026: "the hairline border is above the actual options
   buttons/settings, it should probably be below"). */
#canvas-settings .canvas-acc-body .option-row .option-expand {
  margin: 0;
  padding: 4px 0 14px;
  background: transparent;
  border-bottom: 1px solid var(--ink08);
  border-radius: 0;
}
#canvas-settings .canvas-acc-body .option-row:last-child .option-expand { border-bottom: 0; }
/* Page Capture's flat (non-accordion) group: same flat rows, no dividers. */
#canvas-settings .canvas-flat .option-row {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0 12px;
  margin: 0;
}

/* Latest capture as a rail accordion — the inner block keeps its
   filename/actions but drops its duplicate heading. The summary speaks
   the SAME mono-caps eyebrow as the option accordions (it lives in
   #tour-rail-latest, outside #canvas-settings, so it needs its own
   copy of the voice) and the holder's bottom margin collapses away so
   a closed block sits flush above IMAGE OUTPUT. */
.latest-acc .preview-group-label { display: none; }
.latest-acc .canvas-acc-body { padding: 4px 2px 14px; }
/* Match the new sidebar accordions exactly: a tonal band bar (Söhne
   medium, sentence-case) instead of the old mono-caps eyebrow, and no
   hairline dividers — the filled bar is the separator (Mark). */
.latest-acc > summary {
  list-style: none;
  cursor: pointer;
  padding: 10px 12px;
  background: var(--acc-band);
  border-radius: 8px;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.latest-acc > summary:hover { background: var(--acc-band-hover); color: var(--ink); opacity: 1; }

/* ── Page Capture range in the rail: lined up (Mark) ──────────────
   Narrow column grammar: SELECTED RANGE / START / END become small
   mono eyebrows on their own line; the segmented pill takes a full
   row; the marker readout + Adjust sit together on the next line. */
/* The rail's old summary block is retired with the card (11 Aug 2026) — the
   base .page-range-summary rule is already narrow-column grammar, and the
   section above it does the naming, so there is nothing left to override. */
#canvas-settings .canvas-flat .page-range-summary-value { font-size: 11px; color: var(--ink-soft); }
#canvas-settings .canvas-flat .page-position { display: flex; flex-direction: column; gap: 16px; }
#canvas-settings .canvas-flat .page-position-row {
  display: block;
}
#canvas-settings .canvas-flat .page-position-tag {
  display: block;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink50);
  margin: 0 0 7px;
  width: auto;
}
/* Real rows now (the flex-basis hack stretched the pills full width
   no matter what): pill row, then the readout row beneath. */
#canvas-settings .canvas-flat .pp-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
#canvas-settings .canvas-flat .pp-row .pill-select.seg { width: fit-content; }
#canvas-settings .canvas-flat .pp-row-info { margin-top: 7px; }
#canvas-settings .canvas-flat .page-position-body .picked-target-name {
  font-size: 12px;
  color: var(--ink-soft);
}
#canvas-settings .canvas-flat .page-position-body .ghost-btn.is-quiet { font-size: 11px; }

/* Transition editing in the flyout (Mark): the dark bridge expanding
   down the column was cramped — the expanded card now floats beside
   the rail like stop cards, at the wide layout's full grammar. */
.tour-flyout .tour-flow-spine-node { margin: 0; min-width: 0; }
.tour-flyout.is-transition { padding: 12px; }

/* Ghost left in the rail while a card edits in the flyout. */
.tour-fly-ghost {
  border: 1px dashed var(--bmed);
  border-radius: 10px;
  padding: 14px 16px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink50);
}

/* ── Flyout transition card, tidied (Mark) ── */
.tour-flyout .tour-flow-spine-head { gap: 12px; }
.tour-flyout .tour-flow-spine-disc { width: 30px; height: 30px; }
.tour-flyout .tour-flow-spine-eyebrow {
  font-size: 9px;
  letter-spacing: 0.12em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tour-flyout .tour-flow-spine-link {
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tour-flyout .tour-flow-spine-row { padding: 12px 0; }
.tour-flyout .tour-flow-spine-fdest {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 240px;
}
.tour-flyout .tour-flow-spine-flink { font-size: 13px; }

/* Site Flow rail: consistent rhythm for the dashed affordances —
   + Add stop, the end "+ Add another page" button, and the
   bridges all breathe the same 12px. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-end, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-end { margin-top: 12px; }
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-end-note, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-end-note { font-size: 12px; }
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group > div[style], #previewPanel.is-tour-sequence #manrec-markers .tour-group > div[style] { margin-top: 10px !important; }
/* When the add-row holds only rail-hidden buttons (Rehearse / Show all) and
   no "+ Add scroll stop", it's an invisible 10px-margin band before the
   transition — collapse it so the transition sits close to the stop (Mark). */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group > div[style]:not(:has([data-tour-multi-add-section])), #previewPanel.is-tour-sequence #manrec-markers .tour-group > div[style]:not(:has([data-tour-multi-add-section])) { display: none; }
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded), #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) { margin-top: 0; }
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-page, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-page { margin-bottom: 12px; }

/* ── Canvas notices (off-page, blocked-embed…) ────────────────────
   The raw status + hint lines under the canvas read as debug spill.
   Status = small mono lead-in; hint = a proper amber-tinted callout. */
.app.canvas-mode #tour-preview-status {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink50);
}
/* Dark pill, on-brand (locked monochrome — the old amber tint was both
   off-palette AND low-contrast: light text on a pale amber wash read as
   "hard to read", Mark). Matches the status pill it sits beside. */
.app.canvas-mode #tour-preview-hint {
  display: block;
  max-width: 340px;
  margin: 10px 0 0 !important;
  background: #1A1A18;
  border: 0;
  border-radius: 10px;
  padding: 10px 13px;
  font-size: 11.5px;
  line-height: 1.5;
  color: rgba(251, 250, 248, 0.9);
  box-shadow: 0 8px 22px rgba(17, 17, 17, 0.24);
}

/* ── Site Flow rail: the page reads as ONE unit (Mark) ────────────
   Header + Start/Arrive + scroll stops + the add affordance live in
   a soft contained group; the dark transition bridge stays OUTSIDE
   as the cut between pages — filmstrip grammar, now with chapters. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group, #previewPanel.is-tour-sequence #manrec-markers .tour-group {
  /* Non-viewed pages sit on the panel colour (not white) so they recede —
     the viewed page is the only one that should carry weight (Mark). The
     --bmed hairline still reads them as cards against the ground. */
  background: var(--bg);
  border: 1px solid var(--bmed);
  border-radius: 14px;
  padding: 10px;
}
/* Header flattens inside the group — the group is the container now. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-header, #previewPanel.is-tour-sequence #manrec-markers .tour-group-header {
  background: transparent;
  box-shadow: none;
  padding: 4px 6px 12px;
  border-radius: 0;
  margin-bottom: 10px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-header:hover, #previewPanel.is-tour-sequence #manrec-markers .tour-group-header:hover { background: transparent; color: var(--ink); }
/* Active page: solid black ring back (Mark) — the viewed page reads at a
   glance against the receded, panel-coloured non-viewed cards. */
#previewPanel.is-tour-sequence #tour-multi-pages .is-active-page .tour-group, #previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group {
  background: var(--bg);
  box-shadow: 0 0 0 1.5px var(--ink);
  border-color: transparent;
}
#previewPanel.is-tour-sequence #tour-multi-pages .is-active-page .tour-group-header, #previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-header {
  background: transparent;
  box-shadow: none;
}
/* Cards inside the group sit on the page colour so they read as the
   group's contents, not stacked siblings. ONE hairline only: the 1px
   --bmed border (above). No inset box-shadow — together they doubled up
   and the active card read as a thick border (Mark). */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-section-card, #previewPanel.is-tour-sequence #manrec-markers .tour-section-card {
  background: var(--bg);
  box-shadow: none;
}
#previewPanel.is-tour-sequence #tour-multi-pages .is-active-page .tour-section-card, #previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-section-card {
  box-shadow: none;
}
/* ── Site Flow accordion: only the ACTIVE page stays open ────────────
   Stacked open, the page cards read too alike (Mark). Collapse every
   NON-active page to its header row; the active one shows its stops +
   transition. The :has() gate means nothing collapses until a page is
   active (editor open), so the closed-editor view is unchanged. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-header, #previewPanel.is-tour-sequence #manrec-markers .tour-group-header { cursor: pointer; }
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-host, #previewPanel.is-tour-sequence #manrec-markers .tour-group-host { flex: 1; min-width: 0; }
#previewPanel.is-tour-sequence #tour-multi-pages:has(.is-active-page) .tour-flow-page:not(.is-active-page) .tour-group > *:not(.tour-group-header), #previewPanel.is-tour-sequence #manrec-markers:has(.is-active-page) .tour-flow-page:not(.is-active-page) .tour-group > *:not(.tour-group-header) {
  display: none;
}
#previewPanel.is-tour-sequence #tour-multi-pages:has(.is-active-page) .tour-flow-page:not(.is-active-page) .tour-group-header, #previewPanel.is-tour-sequence #manrec-markers:has(.is-active-page) .tour-flow-page:not(.is-active-page) .tour-group-header {
  border-bottom: 0; margin-bottom: 0; padding-bottom: 4px;
}
/* Stop-count summary — surfaced only on a collapsed page (the open one
   shows its stops, so the count would be redundant there). */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-stopn, #previewPanel.is-tour-sequence #manrec-markers .tour-group-stopn {
  flex: none; font-family: var(--mono); font-size: 9px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink50);
}
#previewPanel.is-tour-sequence #tour-multi-pages .is-active-page .tour-group-stopn, #previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-stopn { display: none; }
/* Disclosure chevron (Mark): a collapsed page card needs a visible cue
   that it opens. It sits at the right of the header on non-viewed pages;
   the viewed page shows "VIEWING" instead, so the caret hides there. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-caret, #previewPanel.is-tour-sequence #manrec-markers .tour-group-caret {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  color: var(--ink32);
  transition: color .12s ease;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-header:hover .tour-group-caret, #previewPanel.is-tour-sequence #manrec-markers .tour-group-header:hover .tour-group-caret { color: var(--ink65); }
#previewPanel.is-tour-sequence #tour-multi-pages .is-active-page .tour-group-caret, #previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-caret { display: none; }
/* Whole collapsed page card reads as clickable — its hairline darkens on
   hover, matching the stop cards. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-page:not(.is-active-page) .tour-group:hover, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-page:not(.is-active-page) .tour-group:hover { border-color: var(--ink32); }
/* Page header: the remove × sits at the very right (order:3, after the
   "VIEWING" ::after and the caret which are order 0). */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-group-header .tour-section-remove, #previewPanel.is-tour-sequence #manrec-markers .tour-group-header .tour-section-remove {
  order: 3;
  margin-left: 0;
}

/* Flyout page context line — which page this stop/transition lives on. */
.tour-flyout-pagectx {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 2px 4px 10px;
  min-width: 0;
}
.tour-flyout-pagectx-num {
  flex: none;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink50);
}
.tour-flyout-pagectx-url {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* Rail: the Clear all row's divider doubled up with the section
   hairlines — drop it (Mark). */
#previewPanel.is-tour-sequence .tour-multi-clear-all-row {
  border-top: 0;
  padding-top: 0;
  margin-top: 12px;
}

/* Zoom-to-cursor toggle — quiet checkbox row in the Camera field. */
.cz-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--ink-soft);
  cursor: pointer;
}
.cz-toggle input { margin: 0; accent-color: var(--ink); cursor: pointer; }
.cz-toggle:hover { color: var(--ink); }

/* Right-column mode title — the "what am I working on" anchor when
   the nav is collapsed. */
.rail-mode-title {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.005em;
  margin: 0 0 14px;
}
/* The mode description now hangs off an info "i" beside the title. */
.rail-mode-title .info-tip { vertical-align: middle; margin-left: 6px; position: relative; top: -1px; }
/* The sequence rail hides card-level info-tips for density, but the MODE
   TITLE's "i" (the relocated description) must stay visible there. */
#previewPanel.is-tour-sequence .rail-mode-title .info-tip { display: inline-flex; }

/* Transition LINK row in the flyout: the URL owns the line; actions
   wrap beneath it instead of crushing it to "https://fl…". */
.tour-flyout .tour-flow-spine-follow-row { flex-wrap: wrap; gap: 6px 12px; }
.tour-flyout .tour-flow-spine-follow-main { flex: 1 1 100%; }
.tour-flyout .tour-flow-spine-fdest {
  max-width: none;
  white-space: normal;
  word-break: break-all;
  line-height: 1.4;
}

/* Action tools — duplicate + drag grip + remove, pinned TOP-RIGHT of
   the card so the × can't wander to the bottom corner when rows wrap
   in the narrow rail (Mark). */
.tour-action-card { position: relative; }
.act-tools {
  position: absolute;
  top: 8px;
  right: 10px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.act-tools .tour-section-remove { position: static; }
.tour-action-card .tour-interaction-row { padding-right: 86px; }
.act-tool-btn {
  border: 0;
  background: transparent;
  color: var(--ink50);
  cursor: pointer;
  padding: 4px;
  line-height: 0;
  display: inline-flex;
  border-radius: 6px;
}
.act-tool-btn:hover { color: var(--ink); background: var(--ink08); }
.act-grip {
  display: inline-flex;
  color: var(--ink32);
  cursor: grab;
  padding: 3px 2px;
}
.act-grip:hover { color: var(--ink); }
.act-grip:active { cursor: grabbing; }
.tour-action-card.is-dragging { opacity: 0.45; }
.tour-action-card.is-drop-target { box-shadow: 0 0 0 1.5px var(--ink) inset; }

/* ── Flyout: strict label/control grid (Mark: "muddled") ──────────
   Every field row is the SAME two columns: 74px mono eyebrow, then
   the control. HOLD's steppers sit as two tidy units — [− 1s +]
   before   [− 1s +] after — no stray dots, shared baseline. */
.tour-flyout .tour-field-row {
  display: grid !important;
  grid-template-columns: 74px 1fr;
  column-gap: 14px;
  align-items: center;
  margin-top: 16px;
}
.tour-flyout .tour-field-label { padding-top: 0; }
.tour-flyout .tour-field-control { min-width: 0; width: auto; }
.tour-flyout .tour-field-control.stop-timing {
  display: flex;
  align-items: center;
  gap: 8px 10px;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.tour-flyout .stop-timing-sep { display: none; }
.tour-flyout .stop-timing-unit {
  /* The straggler that kept BEFORE/AFTER ACTIONS uppercase in the flyout
     after the base rule went sans (Mark: "its all still messy"). One
     voice: sans, sentence case, everywhere. */
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink50);
  margin-right: 8px;
}
.tour-flyout .tour-actions-add { display: flex; align-items: center; gap: 8px; }

/* ── Hairline de-duping in the rail (Mark: stray underline) ───────
   1) Page Capture: the flat CAPTURE RANGE group's bottom hairline met
      the first accordion's top hairline — one line, not two.
   2) A hidden accordion (suppressed empty group) stole :first-child,
      so the first VISIBLE accordion kept its top border and drew a
      lone line under the cards.
   3) Empty latest holder: keep its separator tied to having content. */
#canvas-settings .canvas-flat + .canvas-acc { border-top: 0; }
#canvas-settings .canvas-acc[hidden] + .canvas-acc { border-top: 0; }
#tour-rail-latest:empty { border-bottom: 0; }

/* Air between the cards (Flow/Sequence/Markers/Clips) and the options
   stack — the first accordion sat tight against the list footer.
   Card-list rails only: rail-less modes (Page Capture, Viewport) go
   straight from title to options, where this read as dead space. */
#previewPanel.is-tour-sequence.has-rail #canvas-settings { margin-top: 30px; }
#previewPanel.is-tour-sequence:not(.has-rail) #canvas-settings { margin-top: 4px; }
#previewPanel.is-tour-sequence:not(.has-rail) .rail-mode-title { margin-bottom: 8px; }

/* HOLD steppers, final form: a two-column mini-grid — stepper | unit —
   one row per value. Never wraps mid-unit, never clips the +, works
   with or without the AFTER pair. (Overrides the earlier nowrap row,
   which overflowed the 420px flyout when both steppers were present.) */
.tour-flyout .tour-field-control.stop-timing {
  display: grid !important;
  grid-template-columns: auto auto;
  justify-content: start;
  align-items: center;
  gap: 9px 12px;
  white-space: normal;
  flex-wrap: initial;
}
/* The Slow-for control is a single stepper + unit + "i", NOT the Hold
   field's two stepper|unit pairs — so the two-column grid above dropped its
   "i" onto a line of its own. Keep it as one inline row (Mark). */
.tour-flyout .tour-field-control.stop-timing:has([data-tour-slow-secs]),
#previewPanel.is-tour-sequence .tour-field-control.stop-timing:has([data-tour-slow-secs]) {
  display: flex !important;
  flex-wrap: nowrap;
  align-items: center;
  gap: 8px 10px;
}

/* ── Manual Recording rail polish (consistency pass) ──────────────
   Page header never wraps its chip; marker cards share the stop-card
   grammar exactly; add-page is a contained dashed card like Site
   Flow's end card. */
#previewPanel.is-tour-sequence #manrec-markers .tour-group-num { white-space: nowrap; flex: none; }
#previewPanel.is-tour-sequence #manrec-markers .tour-group-header { flex-wrap: nowrap; }
.manrec-addpage-card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  border: 1px dashed var(--bmed);
  border-radius: 10px;
  padding: 14px 16px;
  flex: 1;
  min-width: 0;
}
.manrec-addpage-note {
  font-size: 12px;
  color: var(--ink50);
  line-height: 1.5;
}
.manrec-add-url-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.manrec-add-url-row .text-input { flex: 1; min-width: 0; font-size: 12px; }
.manrec-add-url-row .ghost-btn { white-space: nowrap; }

/* Viewed clip ring — clips are islands; the ring says which one the
   frame is showing (same grammar as Site Flow's VIEWING page). */
#previewPanel.is-tour-sequence #page-clips-sections .tour-section.is-active-clip > .tour-section-card,
#previewPanel.is-tour-sequence #iso-sections .tour-section.is-active-clip > .tour-section-card,
#previewPanel.is-tour-sequence #tour-sections .tour-section.is-active-clip > .tour-section-card,
#previewPanel.is-tour-sequence #page-clips-sections .tour-section.is-rehearsing-solid > .tour-section-card,
#previewPanel.is-tour-sequence #iso-sections .tour-section.is-rehearsing-solid > .tour-section-card,
#previewPanel.is-tour-sequence #tour-sections .tour-section.is-rehearsing-solid > .tour-section-card,
#previewPanel.is-tour-sequence #page-clips-sections .tour-section.is-rehearsing-solid > .tour-section-card,
#previewPanel.is-tour-sequence #tour-multi-pages .tour-section.is-active-clip > .tour-section-card,
#previewPanel.is-tour-sequence #tour-multi-pages .tour-section.is-rehearsing-solid > .tour-section-card,
#previewPanel.is-tour-sequence #manrec-markers .tour-section.is-active-clip > .tour-section-card,
#previewPanel.is-tour-sequence #manrec-markers .tour-section.is-rehearsing-solid > .tour-section-card {
  box-shadow: 0 0 0 1.5px var(--ink) inset;
  background: var(--bg);
  /* Drop the default grey card border so only the black ring reads — the
     bmed border peeked out around the ring (Mark). */
  border-color: transparent;
}
/* While a quick rehearse is running, the card the user had SELECTED before
   pressing play kept its black active ring on top of the moving rehearse
   ring — two black outlines (Mark). Suppress the static active ring on any
   card that isn't the one the rehearse is currently on, so only the
   rehearse card reads as active. :has() means it self-restores the instant
   the rehearse ends (the solid class is gone). */
#previewPanel.is-tour-sequence #page-clips-sections:has(.tour-section.is-rehearsing-solid) .tour-section.is-active-clip:not(.is-rehearsing-solid) > .tour-section-card,
#previewPanel.is-tour-sequence #iso-sections:has(.tour-section.is-rehearsing-solid) .tour-section.is-active-clip:not(.is-rehearsing-solid) > .tour-section-card,
#previewPanel.is-tour-sequence #tour-sections:has(.tour-section.is-rehearsing-solid) .tour-section.is-active-clip:not(.is-rehearsing-solid) > .tour-section-card,
#previewPanel.is-tour-sequence #page-clips-sections:has(.tour-section.is-rehearsing-solid) .tour-section.is-active-clip:not(.is-rehearsing-solid) > .tour-section-card,
#previewPanel.is-tour-sequence #tour-multi-pages:has(.tour-section.is-rehearsing-solid) .tour-section.is-active-clip:not(.is-rehearsing-solid) > .tour-section-card,
#previewPanel.is-tour-sequence #manrec-markers:has(.tour-section.is-rehearsing-solid) .tour-section.is-active-clip:not(.is-rehearsing-solid) > .tour-section-card {
  box-shadow: none;
  background: var(--card-fill);
  border-color: var(--bmed);
}

/* ── Manual Recording: SAME group grammar as Site Flow (Mark) ─────
   The page group is a soft contained card (not the base square box);
   the header flattens inside it with a hairline; the marker cards sit
   on the page colour; the VIEWING ring wraps the whole group. */
#previewPanel.is-tour-sequence #manrec-markers .tour-group {
  background: var(--card-fill);
  border: 1px solid var(--ink08);
  border-radius: 14px;
  padding: 10px;
  margin: 0;
}
#previewPanel.is-tour-sequence #manrec-markers .tour-group-header {
  background: transparent;
  box-shadow: none;
  padding: 4px 6px 12px;
  border-radius: 0;
  border-bottom: 1px solid var(--ink08);
  margin-bottom: 10px;
}
#previewPanel.is-tour-sequence #manrec-markers .tour-group-header:hover { background: transparent; color: var(--ink); }
#previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group {
  background: var(--bg);
  box-shadow: 0 0 0 1.5px var(--ink);
  border-color: transparent;
}
#previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-group-header {
  background: transparent;
  box-shadow: none;
}
#previewPanel.is-tour-sequence #manrec-markers .tour-section-card {
  background: var(--bg);
  box-shadow: 0 0 0 1px var(--ink08) inset;
}
#previewPanel.is-tour-sequence #manrec-markers .is-active-page .tour-section-card {
  box-shadow: 0 0 0 1px var(--bmed) inset;
}
/* Add-page card: one quiet note, one input row. */
.manrec-addpage-note {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink50);
}

/* Capturing pill — centred over the frame while the quiet capture
   window works behind the app. Pulsing dot says "in progress". */
#tour-preview-capturing {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 12;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: rgba(14, 14, 12, 0.92);
  color: #FBFAF8;
  font-family: var(--mono);
  font-size: 12px;
  padding: 12px 20px;
  border-radius: 100px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
  pointer-events: none;
}
#tour-preview-capturing[hidden] { display: none; }
.tp-cap-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #F7CE46;
  animation: tpCapPulse 1s ease-in-out infinite;
}
@keyframes tpCapPulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.75); }
}

/* ── Responsive canvas: first pass (Mark) ─────────────────────────
   Tiers: <1240 the nav auto-collapses (JS) and the rail + flyout
   tighten; <1080 they tighten further and the well margins shrink.
   The frame's own fit-to-view zoom already absorbs the rest. */
/* Tiers keep the RAIL at full width (narrowing it broke the card
   layouts — Mark); the nav auto-collapse + canvas fit-to-view absorb
   the squeeze, and the rail can be collapsed entirely instead. */
@media (max-width: 1240px) {
  .workspace.tour-preview-mode,
  .app.sidebar-collapsed .workspace.tour-preview-mode {
    grid-template-columns: 64px minmax(0, 1fr) minmax(280px, 300px);
  }
  .tour-flyout { width: 372px; }
}
@media (max-width: 1080px) {
  .tour-flyout { width: 336px; }
  .workspace { padding-left: 14px; padding-right: 14px; }
}

/* Floor tier — 1024px is the supported minimum window. Rail at its
   tightest, flyout caps to stay off the rail, margins minimal. */
@media (max-width: 1024px) {
  .tour-flyout { width: 312px; }
  .workspace { padding-left: 10px; padding-right: 10px; }
}

/* Tight-desktop tier: device trio + Rehearse ride the caption row
   under the canvas instead of overflowing the chrome bar. */
.app.canvas-mode #tour-preview-bar.is-tools {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 10px;
  flex-wrap: wrap;
}


/* ── Collapsible rail (Mark) ──────────────────────────────────────
   Mirrors the nav collapse: a slim strip with the expand chevron;
   the canvas takes the freed width. */
.workspace.tour-preview-mode.rail-collapsed,
.app.sidebar-collapsed .workspace.tour-preview-mode.rail-collapsed {
  grid-template-columns: var(--nav-col, 200px) minmax(0, 1fr) minmax(46px, 46px) !important;
}
.app.sidebar-collapsed .workspace.tour-preview-mode.rail-collapsed { --nav-col: 64px; }
.workspace.tour-preview-mode.rail-collapsed { --nav-col: 200px; }
/* Collapsed: only the head row survives (it holds the expand button —
   sparing the bare button hid its PARENT row, stranding the rail
   collapsed). The title inside it hides; the button centres. */
#previewPanel.is-rail-collapsed > *:not(.rail-collapse-head) { display: none !important; }
#previewPanel.is-rail-collapsed .rail-collapse-head .rail-mode-title { display: none; }
#previewPanel.is-rail-collapsed .rail-collapse-head { justify-content: center; }
.rail-collapse-btn {
  border: 0;
  background: transparent;
  color: var(--ink50);
  cursor: pointer;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.rail-collapse-btn:hover { color: var(--ink); background: var(--ink08); }
#previewPanel .rail-collapse-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}
#previewPanel .rail-collapse-head .rail-mode-title { flex: 1; min-width: 0; }
/* Reset/clear-all action inline with the list heading (SEQUENCE / Clips),
   right-aligned — a quiet text link, not the eyebrow's mono/uppercase (Mark). */
#previewPanel .rail-list-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
#previewPanel .rail-list-label .ghost-btn {
  flex: none; margin: 0; padding: 4px 7px; border: 0; border-radius: 7px;
  background: transparent; color: var(--ink50); cursor: pointer; white-space: nowrap;
  font-family: var(--sans); font-size: 11.5px; font-weight: 400;
  letter-spacing: normal; text-transform: none; line-height: 1.2;
}
#previewPanel .rail-list-label .ghost-btn:hover { background: var(--ink08); color: var(--ink); }
#previewPanel.is-rail-collapsed { padding-top: 18px; }

/* Mode description under the rail title — the classic subtitle's copy
   in the canvas voice. */
.rail-mode-desc {
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink50);
  margin: -6px 0 16px;
}

/* ── Expanded transition card: SAME column grammar as the stop cards
   (74px label / control), no disc (the collapsed bridge carries the
   flow glyph), short identity that never truncates mid-word. */
.tour-flyout .tour-flow-spine-head-expanded .tour-flow-spine-disc { display: none; }
.tour-flyout .tour-flow-spine-head-expanded .tour-flow-spine-eyebrow {
  font-size: 9.5px;
  letter-spacing: 0.12em;
  white-space: nowrap;
}
.tour-flyout .tour-flow-spine-head-expanded .tour-flow-spine-link {
  font-family: var(--mono);
  font-size: 13px;
}
.tour-flyout .tour-flow-spine-row {
  display: grid !important;
  grid-template-columns: 74px 1fr;
  column-gap: 14px;
  align-items: start;
  padding: 14px 0;
}
.tour-flyout .tour-flow-spine-role {
  flex: none;
  width: auto;
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  gap: 4px 5px;
}
/* Verdict drops to its own quiet line under "Reveal ⓘ". */
.tour-flyout .tour-flow-spine-role-note { flex-basis: 100%; }
.tour-flyout .tour-flow-spine-role-note { margin: 0; }
.tour-flyout .tour-flow-spine-rowctl {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  min-width: 0;
}
.tour-flyout .tour-flow-spine-rowctl.is-inline {
  flex-direction: row;
  align-items: center;
}

/* ── Slim collapsed transition (Mark): one line — disc, short
   destination ("/email · via menu"), Edit inline. The eyebrow's full
   sentence lives in the title tooltip and the expanded card. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-sub, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-sub {
  font-size: 12px;
  color: #8A8A85;
  letter-spacing: 0;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-head.is-slim .tour-flow-spine-link, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-head.is-slim .tour-flow-spine-link {
  font-family: var(--mono);
  font-size: 13px;
}
/* The rail's stacked-layout rules (built for the old two-line head)
   would wrap Edit onto its own row — the slim head stays one line,
   and gets its flow disc back (it IS the flow glyph here). */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim {
  flex-wrap: nowrap;
  gap: 12px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-disc, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-disc {
  display: flex;
  width: 26px;
  height: 26px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-disc svg, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-disc svg {
  width: 14px;
  height: 14px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-body, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-body {
  flex: 1 1 auto;
  min-width: 0;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-link, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-link {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-edit, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-slim .tour-flow-spine-edit {
  align-self: center;
}
/* Resolved row mirrors a stop card head: no head gap — disc, name and the
   Edit/× controls carry their own margins, same as the stop cards. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-resolved, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-head.is-resolved {
  gap: 0;
}
/* Transition = a SECOND CARD in the rail (Mark, 23 July 2026): once the
   link is set, the collapsed node wears the same chrome as the stop card
   above it — the bare floating row read unfinished next to it. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded):has(.tour-flow-spine-head.is-resolved), #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded):has(.tour-flow-spine-head.is-resolved) {
  margin-top: 0;
  background: var(--card-fill);
  border: 1px solid var(--bmed);
  border-radius: 10px;
  padding: 12px;
}
/* Empty state: the pick button spans the FULL card column (the node's
   12px side padding inset it, so it read narrower than the position
   card). Radius matches the cards so the outline reads as one of them. */
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded):has(.tour-flow-spine-head.is-pickprompt), #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded):has(.tour-flow-spine-head.is-pickprompt) {
  padding-left: 0;
  padding-right: 0;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-pick, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-pick {
  border-radius: 10px;
  padding: 11px 12px;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-altrow, #previewPanel.is-tour-sequence #manrec-markers .tour-flow-spine-node:not(.is-expanded) .tour-flow-spine-altrow {
  padding: 0 2px;
}

/* ── Reveal row = a Page Tour ACTION row, exactly (Mark). Same
   content, same base flex grammar, no special grid: the custom grid
   collapsed the target name to one letter while space sat free. */
.tour-flyout .tour-flow-spine-row .tour-interaction-row {
  flex-wrap: wrap;
  gap: 8px 10px;
}

/* Spine ghost sits directly under the page card — give it the same
   breathing room the spine node had. */
#tour-multi-pages .tour-fly-ghost.is-spine, #manrec-markers .tour-fly-ghost.is-spine { margin-top: 14px; }

/* Verified tick on the collapsed transition line — on-palette ink, not
   an off-brand green (Mark). */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-subok, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-subok { color: var(--ink); font-weight: 600; }

/* ── Transition card legibility (Mark): the path may truncate, the
   "via menu ✓" note may not; the reveal's target name takes all the
   spare width instead of collapsing to "S…". */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-link, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-link {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 7px;
  min-width: 0;
}
/* Two lines (Mark: "/co… what?") — the path owns line one in full,
   the note sits underneath instead of starving it. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-path, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-path {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1 1 100%;
}
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-sub, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-sub {
  flex: none;
  white-space: nowrap;
  font-size: 11px;
}
.tour-flyout .tour-flow-spine-row .tour-interaction-row .tour-interaction-target-name {
  flex: 1 1 auto;
}
.tour-flyout .tour-flow-spine-fdest { max-width: none; }

/* Quiet "rehearse to test" teaching note on the collapsed transition. */
:is(#tour-multi-pages, .tour-flyout) .tour-flow-spine-subhint, :is(#manrec-markers, .tour-flyout) .tour-flow-spine-subhint { color: #8A8A85; font-style: italic; }

/* ── Rail action cards: the pinned tools (duplicate · grip · ×) get
   their own top strip — in the narrow rail they sat ON the row's
   Change button (Mark: "icons overlapping"). The row then keeps the
   full card width. Flyout/classic stay on the side-padding layout. */
#previewPanel.is-tour-sequence .tour-action-card { padding-top: 32px; }
#previewPanel.is-tour-sequence .tour-action-card .tour-interaction-row { padding-right: 0; }

/* ── Modal polish: blur the page behind, soften the overlay. */
.capt-modal-root { -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.capt-modal-card { box-shadow: 0 28px 80px rgba(14, 14, 12, 0.22); }
.capt-modal-field-input, .capt-modal-field-select { font-size: 13.5px; padding: 9px 12px; }

/* ── Rail foot: Open full size / Classic editor pinned at the very
   bottom of the right column, always visible, separate from options. */
#rail-view-foot {
  position: fixed;
  bottom: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px 12px;
  background: var(--bg);
  border-top: 1px solid var(--border);
  box-sizing: border-box;
}
#rail-view-foot[hidden] { display: none; }
/* Proper pill buttons (Mark: "looks broken, not a button like before"). */
#rail-view-foot .ghost-btn {
  border: 1px solid var(--bmed);
  border-radius: 100px;
  background: var(--bg);
  padding: 7px 16px;
  font-size: 12px;
  white-space: nowrap;
}
#rail-view-foot .ghost-btn:hover { border-color: var(--ink); background: var(--bg); }
/* (rail-collapsed: the foot lives on body now, geometry tracks the panel) */

/* ── Save setup: compact icon beside the capture CTA. */
#topbar-actions-slot { display: flex; align-items: center; gap: 10px; }
#tour-preview-savesetup.is-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 100px;
  border-color: transparent;
  background: transparent;
  color: var(--ink65, var(--ink));
}
#tour-preview-savesetup.is-icon:hover { border-color: var(--bmed); color: var(--ink); }

/* Rail content keeps clear of the fixed foot. */
.preview-panel.is-tour-sequence { padding-bottom: 64px; }

/* ── Topbar CTA: id-weight so nothing outruns it — narrow, source-bar
   height (Mark: "still so long; narrower but a bit taller"). */
#topbar-actions-slot .btn-primary,
#topbar-actions-slot .action-footer .btn-primary {
  width: auto !important;
  min-width: 0 !important;
  margin: 0 !important;
  padding: 10px 19px !important;
  font-size: 13px;
  border-radius: 100px;
  white-space: nowrap;
}
#topbar-actions-slot .action-footer { display: inline-flex; align-items: center; gap: 10px; margin: 0; }
#topbar-actions-slot .action-footer .status:empty { display: none; }

/* ── Branded tooltip for any control (same look as the ⓘ tips). */
.capt-tip { position: relative; }
.capt-tip::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: max-content;
  max-width: 250px;
  background: var(--ink);
  color: var(--paper);
  padding: 8px 11px;
  border-radius: 8px;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  opacity: 0;
  pointer-events: none;
  transition: opacity .12s ease, transform .12s ease;
  transition-delay: 0s;
  z-index: 300;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}
.capt-tip:hover::after, .capt-tip:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  transition-delay: .35s;
}
.capt-tip.tip-below::after {
  bottom: auto;
  top: calc(100% + 8px);
  transform: translateX(-50%) translateY(-4px);
}
.capt-tip.tip-below:hover::after, .capt-tip.tip-below:focus-visible::after {
  transform: translateX(-50%) translateY(0);
}
/* Foot tooltips hug their button's side so they stay on screen. */
#rail-view-foot .capt-tip::after { left: 0; transform: translateX(0) translateY(4px); }
#rail-view-foot .capt-tip:hover::after { transform: translateX(0) translateY(0); }
#rail-view-foot .capt-tip:last-child::after { left: auto; right: 0; }

/* ── Unified branded tooltip (JS-positioned) ─────────────────────────
   One bubble for everything: native title=, .info-tip "i" markers, and
   .capt-tip buttons all route through this. position:fixed so it never
   clips inside the chrome bar / stage / overflow panels (the CSS ::after
   versions below are neutralised so they don't double up). */
.info-tip::after,
.capt-tip::after { content: none !important; }
.capt-tooltip {
  position: fixed;
  /* Above EVERY overlay (layouts picker 1000, modal/lightbox 9999) — it
     was z 400 and rendered behind the picker sheet (Mark). */
  z-index: 10001;
  max-width: 250px;
  padding: 8px 11px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: 0;
  text-align: left;
  text-transform: none;
  white-space: normal;
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity .12s ease, transform .12s ease;
}
.capt-tooltip.is-in { opacity: 1; transform: translateY(0); }
.capt-tooltip::after {
  content: "";
  position: absolute;
  left: var(--tip-arrow, 50%);
  top: 100%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--ink);
}
.capt-tooltip.is-below::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: var(--ink);
}
/* Collapsed-nav flyout: a right-of-the-rail label, no pointer arrow,
   and a tighter single-line pill. */
.capt-tooltip.is-nav { white-space: nowrap; padding: 6px 10px; }
.capt-tooltip.is-nav::after { display: none; }

/* ── Side message panel (narrow frames): status + hint left of frame. */
#stage-side-msgs {
  position: fixed;
  bottom: 92px;
  width: 240px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 30;
}
#stage-side-msgs[hidden] { display: none; }
#stage-side-msgs .lab-status { display: block; }
#stage-side-msgs #tour-preview-hint { margin: 0 !important; max-width: 240px; }

/* Desktop: embed status/hint float as quiet dark pills pinned to the canvas
   bottom-left (over the page), so a tall preview can't push them out of view.
   The wrapper is TRANSPARENT and each message is its OWN pill, so a
   display:none status/hint leaves nothing on screen (no empty box). The
   transient action toast + the pick banner sit bottom-CENTRE, so this left
   lane avoids colliding with them. */
#stage-side-msgs.is-over-canvas {
  width: auto;
  max-width: min(340px, calc(100% - 40px));
  background: none;
  align-items: center;
  text-align: center;
}
/* Frosted-glass pills over the preview (Mark) — semi-transparent paper with a
   backdrop blur, dark ink text, so they read over any page content. */
#stage-side-msgs.is-over-canvas > * {
  background: rgba(250, 250, 250, 0.72);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border: 1px solid rgba(14, 14, 12, 0.08);
  border-radius: 14px;
  padding: 9px 13px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04), 0 14px 36px rgba(14, 14, 12, 0.10);
}
.app.dark #stage-side-msgs.is-over-canvas > * {
  background: rgba(30, 30, 28, 0.68);
  border-color: rgba(255, 255, 255, 0.14);
}
/* An element that's SHOWN (display !== none) but has no text would render as an
   empty pill. :empty misses whitespace-only text, so JS flags blanks with
   [data-blank] (kept live by an observer). */
#stage-side-msgs.is-over-canvas > *:empty,
#stage-side-msgs.is-over-canvas > [data-blank] { display: none; }
#stage-side-msgs.is-over-canvas .lab-status:not([data-blank]):not(:empty) {
  display: block;
  color: var(--ink);
  font-size: 11.5px;
}
.app.dark #stage-side-msgs.is-over-canvas .lab-status:not([data-blank]):not(:empty) { color: #FBFAF8; }
#stage-side-msgs.is-over-canvas #tour-preview-hint {
  color: var(--ink65);
  font-size: 11.5px;
  line-height: 1.5;
  max-width: 320px;
}
.app.dark #stage-side-msgs.is-over-canvas #tour-preview-hint { color: rgba(251, 250, 248, 0.85); }

/* Device controls as a tidy box at the phone's right shoulder (Mark): a paper
   card with a full-width device switcher + full-width action buttons. */
.app.shell-modetop #stage-side-tools {
  flex-direction: column; align-items: stretch; gap: 10px; width: 236px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 14px;
  padding: 14px; box-shadow: 0 1px 2px rgba(14, 14, 12, 0.06), 0 14px 38px rgba(14, 14, 12, 0.10);
}
.app.shell-modetop #stage-side-pill { width: 100%; align-items: stretch; gap: 10px; }
.app.shell-modetop #stage-side-tools .tb-device { width: 100%; }
.app.shell-modetop #stage-side-tools .tb-device .tb-dev { flex: 1 1 0; }
.app.shell-modetop #stage-side-tools .tb-device .tb-dev-more { flex: 0 0 auto; }
.app.shell-modetop #stage-side-tools .ghost-btn {
  width: 100%; justify-content: center; font-size: 13px; padding: 9px 12px;
  background: var(--btn2bg); border: 1px solid var(--border); border-radius: 9px; color: var(--ink65);
}
.app.shell-modetop #stage-side-tools .ghost-btn:hover { background: var(--btn2bg-hover); border-color: var(--bmed); color: var(--ink); }

/* Everything that floats OVER the preview is frosted glass, not black (Mark):
   the pick banner, the off-source offer, and the canvas-mode hint pill. */
.app.shell-modetop .tour-preview-banner,
.app.shell-modetop.canvas-mode #tour-preview-hint {
  background: rgba(250, 250, 250, 0.72);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border: 1px solid rgba(14, 14, 12, 0.08);
  border-radius: 14px;
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.04), 0 14px 36px rgba(14, 14, 12, 0.10);
}
.app.dark.shell-modetop .tour-preview-banner,
.app.dark.shell-modetop.canvas-mode #tour-preview-hint {
  background: rgba(30, 30, 28, 0.68);
  border-color: rgba(255, 255, 255, 0.14);
  color: rgba(251, 250, 248, 0.95);
}
.app.shell-modetop .tour-preview-banner-text { color: inherit; }
.app.shell-modetop .tour-preview-banner-btn { border-color: var(--border); color: var(--ink65); }
.app.shell-modetop .tour-preview-banner-btn:hover { border-color: var(--bmed); color: var(--ink); }
.app.shell-modetop .tour-preview-banner-btn.is-primary { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.app.dark.shell-modetop .tour-preview-banner-btn { border-color: rgba(255, 255, 255, 0.3); color: rgba(251, 250, 248, 0.85); }
.app.dark.shell-modetop .tour-preview-banner-btn.is-primary { background: #FBFAF8; color: #1A1A18; border-color: #FBFAF8; }
/* Pick banner: don't constrain it to the (narrow) phone width — give it a
   comfortable desktop-style width so the text reads on a line or two with the
   button beside it, instead of a bunched two-column squeeze (Mark). It already
   floats free over the phone, so it can extend past the frame. */
.app.shell-modetop .tour-preview-banner:not(.is-offsource) {
  max-width: min(460px, calc(100vw - 320px));
}
.app.shell-modetop .tour-preview-banner:not(.is-offsource) .tour-preview-banner-text {
  white-space: normal; line-height: 1.55;
}

/* Shell: the floating "Activity" card in the left flank beside a phone preview
   (mockup's .messages). The eyebrow only shows in this card state. */
.ssm-eyebrow { display: none; }
.app.shell-modetop #stage-side-msgs.is-activity {
  width: 248px; align-items: stretch; gap: 10px; padding: 16px 18px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, 0.06), 0 16px 44px rgba(14, 14, 12, 0.10);
}
.app.shell-modetop #stage-side-msgs.is-activity .ssm-eyebrow {
  display: block; font-family: var(--mono); font-size: 10px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink50); margin-bottom: 2px;
}
.app.shell-modetop #stage-side-msgs.is-activity .lab-status,
.app.shell-modetop #stage-side-msgs.is-activity #tour-preview-hint {
  display: flex; align-items: flex-start; gap: 9px; margin: 0; max-width: none;
  background: var(--ink08); border-radius: 10px; padding: 10px 12px;
  font-size: 12.5px; line-height: 1.45; color: var(--ink65);
}
.app.shell-modetop #stage-side-msgs.is-activity .lab-status::before,
.app.shell-modetop #stage-side-msgs.is-activity #tour-preview-hint::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--ink50);
  flex: none; margin-top: 4px;
}
.app.shell-modetop #stage-side-msgs.is-activity > .lab-status:empty,
.app.shell-modetop #stage-side-msgs.is-activity > .lab-status[data-blank],
.app.shell-modetop #stage-side-msgs.is-activity > #tour-preview-hint:empty,
.app.shell-modetop #stage-side-msgs.is-activity > #tour-preview-hint[data-blank] { display: none; }

/* ── Add affordances as EMPTY CARDS in the rail (Mark): the dashed
   "+ Add pause" / "+ Add clip" reads as the next card waiting to
   exist, full width under the cards it joins. */
/* Every dashed "+ Add" affordance sits as an equal-height sibling to the
   collapsed cards — not bigger, not smaller (Mark: all dashed buttons the
   same size as the cards, every mode). One compact size across the board. */
#previewPanel.is-tour-sequence .ghost-btn.is-add {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px;
  /* 8px, not 10 (12 Aug 2026, the button pass). The dashed add is already
     distinguished from a command by its dash and its full width; a third corner
     radius on top of that is a difference with nothing to say. It also put this
     button out of step with the dropdown directly under it in the Layouts text
     panel, which is where the mismatch was visible. */
  border-radius: 8px;
  font-size: 11.5px;
}

/* ============================================================
   SHELL REDESIGN (experiment, Mark) — capture modes in a top-left
   picker, the left rail removed for capture work, Library/Guide/
   Settings in a top-right cluster, darker work canvas. Everything is
   gated on .app.shell-modetop so it lifts cleanly if we don't like it.
   ============================================================ */

/* The rail's capture-mode groups live in the mode menu now. */
.app.shell-modetop .sidebar-group[data-group="stills"],
.app.shell-modetop .sidebar-group[data-group="video"] { display: none; }

/* While a CAPTURE MODE is open, drop the whole rail and give the row to
   main + options. Library / Guide / Settings are NOT capture modes, so the
   rail returns for them (their own sub-navs). */
.app.shell-modetop.is-capture-mode .sidebar-col { display: none; }
.app.shell-modetop.is-capture-mode .workspace { grid-template-columns: var(--caps-w, 200px) minmax(0, 1fr) minmax(280px, 360px); }
.app.shell-modetop.is-capture-mode .workspace.tour-preview-mode,
.app.shell-modetop.is-capture-mode.sidebar-collapsed .workspace.tour-preview-mode { grid-template-columns: var(--caps-w, 200px) minmax(0, 1fr) minmax(264px, 300px); }
/* Layouts and Edits both match the narrower preview-panel width of Page tour /
   Page clips on the RIGHT. The LEFT column is wide in Layouts only: that width
   was bought for the template rail — it is what lets the picker run two cards
   across (Mark, 1 Aug: "the side panel on left can be wider. same as right",
   then "i prefer the 2 columns layout for the templates, like we had before").
   Edits USED to keep the 200px strip every other mode uses ("for edits, the
   sidebar can be the normal width like in the modes. doesnt need to be so wide
   for this") — true while its strip held only captures. Since #200 the template
   rail lives there too, and it is the same rail with the same two-column
   template grid, so at 200px it was the same content in a narrower box (Mark,
   1 Aug: "the layouts left panel is wider than sequence panel. its too
   squashed, make it the same as layouts one"). The two now match, which is also
   the point of the shot step: one place, one rail, whichever way you came in.
   .mode-edits is a subset of .mode-layouts, so it must still come SECOND and
   carry its own .sidebar-collapsed pair to outrank the 5-class Layouts variant
   above it.
   Neither widens the other capture modes — those embed a live page preview in
   the middle column and cannot spare the 100px. The collapsed variant tracked
   the OLD nav width (64px), which is stale here: in the shell column 1 is the
   captures strip, not the nav, so it holds the strip width like its siblings.

   COLUMN 1 IS A PLAIN 300px, NOT minmax(264px, 300px) (#317. Mark, 4 Aug 2026:
   "Still need to fix the smooth close and open of the left side bar for layouts
   and sequence", and before that "I am telling you that the mode left side bar
   collapses nicely. the layouts and sequence doesnt. they are different
   motions. just make layouts and sequence the same as the mode ones. surely.")
   He read it as two different animations. It is one animation and one snap.
   .workspace has carried transition: grid-template-columns .25s ease since it
   was written (see :590), but grid-template-columns only INTERPOLATES when the
   two track lists have the same number of tracks and each pair of tracks is
   type-compatible. Any pair that is not — a minmax() against a plain length —
   is a DISCRETE change, and a discrete change jumps at the halfway point of the
   duration rather than easing.
   The capture modes get this right by accident: 200px -> 30px on track 1 with
   tracks 2 and 3 byte-identical, so all three pairs interpolate and the strip
   slides. Layouts and Sequence went minmax(264px, 300px) -> 30px, so track 1
   was the one incompatible pair in the list and the whole grid snapped. Same
   transition, same duration, same easing; different arithmetic.
   Nothing is lost by dropping the 264px floor. It only ever engaged below about
   1130px of workspace, and the strip auto-collapses below 1200px of window
   anyway (CAPS_AUTO_W, app.js:35671), so the floor was reachable only in the
   sliver where a rail full of templates holds itself open on a small window --
   and there the middle column absorbs the difference through its own
   minmax(0, 1fr). Track 3 keeps its minmax because it is IDENTICAL in both
   states, and identical tracks interpolate whatever their type. */
.app.shell-modetop.is-capture-mode.mode-layouts .workspace,
.app.shell-modetop.is-capture-mode.mode-layouts.sidebar-collapsed .workspace { grid-template-columns: 300px minmax(0, 1fr) minmax(264px, 300px); }
.app.shell-modetop.is-capture-mode.mode-edits .workspace,
.app.shell-modetop.is-capture-mode.mode-edits.sidebar-collapsed .workspace { grid-template-columns: 300px minmax(0, 1fr) minmax(264px, 300px); }
/* Responsiveness fixes (Mark): (1) when the captures strip auto-collapses
   (<1200px) the GENERIC caps-collapsed rule would widen the layouts panel to
   360px — pin it to the layouts width instead. */
.app.caps-collapsed.shell-modetop.is-capture-mode.mode-layouts .workspace,
.app.caps-collapsed.shell-modetop.is-capture-mode.mode-edits .workspace { grid-template-columns: 30px minmax(0, 1fr) minmax(264px, 300px); }
/* (2) In the shell the right panel IS the controls for EVERY capture mode, so it
   must never be hidden at narrow widths the way the old optional storyboard rail
   was (the @media < 1100px display:none only made sense for the classic layout). */
.app.shell-modetop.is-capture-mode .workspace > .preview-panel { display: block; }
/* Site Flow + Manual keep their wider rail (page headers / per-page markers). */
.app.shell-modetop.is-capture-mode .workspace.tour-preview-mode.rail-wide,
.app.shell-modetop.is-capture-mode.sidebar-collapsed .workspace.tour-preview-mode.rail-wide { grid-template-columns: var(--caps-w, 200px) minmax(0, 1fr) minmax(316px, 372px); }
/* Collapsing the RIGHT rail must not shrink the strip: the base .rail-collapsed
   rule pins column 1 to --nav-col (the old nav width, 64px when the sidebar is
   collapsed). In the shell column 1 is the captures strip, so hold its width. */
.app.shell-modetop.is-capture-mode .workspace.tour-preview-mode.rail-collapsed,
.app.shell-modetop.is-capture-mode.sidebar-collapsed .workspace.tour-preview-mode.rail-collapsed {
  grid-template-columns: var(--caps-w, 200px) minmax(0, 1fr) minmax(46px, 46px) !important;
}
.app.caps-collapsed.shell-modetop.is-capture-mode .workspace.tour-preview-mode.rail-collapsed,
.app.caps-collapsed.shell-modetop.is-capture-mode.sidebar-collapsed .workspace.tour-preview-mode.rail-collapsed {
  grid-template-columns: var(--caps-w, 30px) minmax(0, 1fr) minmax(46px, 46px) !important;
}
.app.shell-modetop.is-capture-mode .workspace > .caps-strip { grid-column: 1; display: flex; }
.app.shell-modetop.is-capture-mode .workspace > .main { grid-column: 2; }
.app.shell-modetop.is-capture-mode .workspace > .preview-panel { grid-column: 3; }

/* Darker grey work field around the preview. */
/* Grey work canvas ONLY for capture / layouts modes. Content views (Library,
   Saved setups, Guide, Settings, source test) read on paper like the layouts
   picker popup — not the grey work surface. (Mark) */
.app.shell-modetop.is-capture-mode .workspace > .main { background: var(--canvas); }
.app.shell-modetop:not(.is-capture-mode) .workspace > .main { background: var(--bg); }   /* the pages: paper in light, the dark black in dark (21 Sep 2026) */

/* Preview top bar like the mockup's .pv-bar: fixed 47px, traffic-light dots
   dropped, the URL a soft chip, the tools sized + spaced to match. */
.app.shell-modetop .lab-chrome > span[aria-hidden="true"] { display: none; }
.app.shell-modetop .lab-chrome { height: 47px; min-height: 47px; padding: 0 14px; gap: 0; }
/* De-browser (Mark): drop the faux-browser chrome — no traffic-light dots and
   no URL field (it's already in the Source pill) — so the bar reads as a plain
   control toolbar (Reload + device toggle + actions) with the device on the grey
   stage below, not inside a fake browser. */
.app.shell-modetop .lab-chrome > span[aria-hidden="true"] { display: none; }
.app.shell-modetop #tour-preview-chrome-url { display: none; }
/* Reload: a labelled button, not a bare glyph (Mark: "name it reload"). */
.chrome-reload-label { display: none; }
/* Reload takes the bar's one box: 26px tall, 8px radius, like the device
   buttons and the area tools beside it. Borderless because it is the only
   control in here that is not part of a cluster — the hairline after it already
   says where it ends. */
/* RELOAD LOSES ITS WORD (28 Aug 2026, Mark: "it only has the device icons and a
   quite big reload button. could maybe just have reload icon"). The glyph is a
   circular arrow — the least ambiguous icon in the bar — and the word was
   costing ~54px on the one row that now has something to say, the handoff line.
   Square again, at the bar's own 28px box; title keeps the name for a hover and
   aria-label always carried it for anyone who cannot see it. */
.app.shell-modetop #tour-preview-reload {
  width: 28px; min-width: 28px; height: 28px; margin: 0; gap: 6px;
  padding: 0; border-radius: 8px; border: 0;
  background: transparent; color: var(--ink65); font-size: 12px; font-family: var(--sans);
}
.app.shell-modetop #tour-preview-reload:hover { background: var(--ink08); color: var(--ink); }
.app.shell-modetop #tour-preview-reload .chrome-reload-label { display: none; }
/* The tools cluster's spacing comes from the shared divider rule above — a
   margin-left here would stack on top of it and put this one hairline at a
   different distance from its neighbours than the other one. */
.app.shell-modetop .lab-chrome-tools { gap: 4px; }
.app.shell-modetop .lab-chrome-tools .ghost-btn { font-size: 13px; padding: 6px 11px; border-color: transparent; }
.app.shell-modetop .lab-chrome-tools .ghost-btn:hover { background: var(--ink08); border-color: transparent; }
/* Keep the canvas-toolbar buttons on ONE line — at narrow widths "Open capture
   window" / "Quick capture" were wrapping to two lines and fattening the bar. */
.app.shell-modetop .lab-chrome-tools .ghost-btn,
.app.shell-modetop #tour-preview-capture,
.app.shell-modetop #tour-preview-capture > span { white-space: nowrap; flex: none; }
/* Responsive canvas toolbar (Mark — deferred from the shell rollout, then
   "breaks a bit early + make it look nice, centre the options"). The
   controls are CENTRED, and the bar wraps NATURALLY: #lab-chrome-tools is a
   single flex child (the whole action cluster), so it only drops to its own
   centred row UNDER the device selector when it genuinely won't fit — no
   viewport breakpoint to trip early. The bar grows to two rows then. */
.app.shell-modetop .lab-canvas .lab-chrome {
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  height: auto;
  min-height: 47px;
  row-gap: 6px;
}
.app.shell-modetop .lab-canvas .lab-chrome-tools {
  /* The action buttons themselves also wrap, so on really small widths the
     last button (e.g. Quick capture) drops to a new line instead of
     overflowing — keeps it from looking broken below the intended min. */
  flex-wrap: wrap;
  justify-content: center;
  row-gap: 4px;
}
/* ══════════════════════════════════════════════════════════════════════════
   ONE RHYTHM FOR THE STAGE TOOLBAR (Mark, 13 Aug 2026: "think about the spacing
   and the lines between the different parts of the toolbar. need consistency.")
   ──────────────────────────────────────────────────────────────────────────
   Three clusters — Reload · devices · area tools — and one set of numbers for
   all of them:

     · every control is 28px tall with an 8px radius
     · 4px between controls inside a cluster
     · 6px of bar gap, and each hairline sits 10px from the control on either
       side of it (4px margin + the 6px gap on the left, 10px padding on the
       right)
     · outline at rest, solid ink when active — device button or text button

   The one exception is the WRAPPED bar: below the min width the clusters fall
   onto their own rows and a leading hairline reads as a stray vertical line, so
   it is dropped there and only there. It used to be dropped always, which is why
   the device cluster had a line and the tools did not.
   ══════════════════════════════════════════════════════════════════════════ */
.app.shell-modetop #tour-preview-canvas.is-narrow .lab-chrome .lab-chrome-tools:has(> :not([style*="display: none"])) {
  border-left: 0;
}
/* The DEVICE cluster's hairline needs the same exemption, for the same reason
   (28 Aug 2026). It was always true on a wrapped bar and rarely seen, because
   row one used to be just "⟳ Reload"; with the handoff line in there the bar
   wraps sooner and the stray line in front of the device buttons became the
   thing you notice. Same rule, same scope: the wrapped bar and nowhere else. */
.app.shell-modetop #tour-preview-canvas.is-narrow .lab-chrome:has(#tour-preview-reload:not([style*="display: none"])) #topbar-device-slot {
  margin-left: 0; padding-left: 0; border-left: 0;
}

/* DEVICE ICONS ARE THEIR OWN BUTTONS, not segments of one control (Mark: "move
   the device icons out of a single pill and make them their own single
   buttons"). The wrapper is now nothing but a cluster: no border, no rounding,
   no overflow clip — just the bar's own 4px between peers. */
.app.shell-modetop .lab-chrome .tb-device,
.app.shell-modetop #stage-side-tools .tb-device,
.app.shell-modetop .caps-tools .tb-device {
  flex-direction: row; align-items: center; border: 0;
  border-radius: 0; overflow: visible; padding: 0; gap: 4px; background: none; box-shadow: none;
}
.app.shell-modetop .lab-chrome .tb-dev,
.app.shell-modetop #stage-side-tools .tb-dev,
.app.shell-modetop .caps-tools .tb-dev {
  width: 32px; height: 28px; min-width: 0; padding: 0;
  border: 1px solid var(--bmed); border-radius: 8px;
}
.app.shell-modetop .lab-chrome .tb-dev:hover,
.app.shell-modetop #stage-side-tools .tb-dev:hover,
.app.shell-modetop .caps-tools .tb-dev:hover { border-color: var(--ink); color: var(--ink); }
.app.shell-modetop .lab-chrome .tb-dev.is-on,
.app.shell-modetop #stage-side-tools .tb-dev.is-on,
.app.shell-modetop .caps-tools .tb-dev.is-on {
  background: var(--ink); border-color: var(--ink); color: var(--paper);
}
/* The sizes caret is a peer, narrower because it carries no device icon. */
.app.shell-modetop .lab-chrome .tb-dev-more,
.app.shell-modetop #stage-side-tools .tb-dev-more,
.app.shell-modetop .caps-tools .tb-dev-more { width: 26px; padding: 0; }

/* THE AREA TOOLS ARE OUTLINE BUTTONS, BLACK ONLY WHEN HELD (Mark). They have to
   out-specify .lab-chrome-tools .ghost-btn, which strips the border off
   everything in this cluster because the buttons that used to live here were
   quiet page actions. These two are not: they are the mode's actual controls,
   and an outline is what separates "press me" from "Reload". */
.app.shell-modetop .lab-chrome-tools .stage-tool {
  height: 28px;
  padding: 0 11px;
  font-size: 11.5px;
  gap: 6px;
  border: 1px solid var(--bmed);
  border-radius: 8px;
  background: transparent;
}
.app.shell-modetop .lab-chrome-tools .stage-tool:hover {
  background: var(--ink08); border-color: var(--ink); color: var(--ink);
}
.app.shell-modetop .lab-chrome-tools .stage-tool.is-armed,
.app.shell-modetop .lab-chrome-tools .stage-tool.is-armed:hover {
  background: var(--ink); border-color: var(--ink); color: var(--paper);
}
/* A tool you cannot start while the other is held: quiet, not invisible. */
.app.shell-modetop .lab-chrome-tools .stage-tool:disabled {
  opacity: .45; border-color: var(--bmed); background: transparent; cursor: not-allowed;
}

/* ── Station layout (loey-mobile-fixed): a narrow device gets a docked LEFT
   column carrying Capture + Latest + Activity stations; the phone centres
   between two fixed columns. Docked grid — nothing floats, nothing oscillates. */
.app.shell-modetop.is-mobile-frame.is-capture-mode .workspace,
.app.shell-modetop.is-mobile-frame.is-capture-mode .workspace.tour-preview-mode,
.app.shell-modetop.is-mobile-frame.is-capture-mode.sidebar-collapsed .workspace.tour-preview-mode {
  grid-template-columns: 320px minmax(0, 1fr) 320px !important;
}
/* The left column is #frameLeft (controls + captures + activity as stations). */
.app.shell-modetop.is-mobile-frame .workspace > #frameLeft {
  grid-column: 1; align-self: stretch; margin: 0; min-height: 0;
  display: flex; flex-direction: column;
  background: var(--bg); border-right: 1px solid var(--border); overflow-y: auto;
}
.app.shell-modetop.is-mobile-frame .workspace > .main { grid-column: 2; background: var(--canvas); }
.app.shell-modetop.is-mobile-frame .workspace > .preview-panel { grid-column: 3; }
/* Stations — sections with hairline dividers, no individual card chrome. */
.app.shell-modetop.is-mobile-frame #frameLeft > #capsTools,
.app.shell-modetop.is-mobile-frame #frameLeft > .caps-strip,
.app.shell-modetop.is-mobile-frame #frameLeft > #stage-side-msgs {
  border: 0; border-radius: 0; box-shadow: none; background: transparent;
  margin: 0; flex: none; padding: 20px 22px; width: auto; position: static;
}
.app.shell-modetop.is-mobile-frame #frameLeft > * + * { border-top: 1px solid var(--border); }
/* Latest station: just the latest (drop Recent + collapse), thumb clamped so a
   tall shot can't run the column. */
.app.shell-modetop.is-mobile-frame #frameLeft > .caps-strip { display: flex; flex-direction: column; gap: 12px; }
.app.is-mobile-frame .caps-collapse,
.app.is-mobile-frame .caps-recent { display: none; }
.app.shell-modetop.is-mobile-frame .caps-strip .page-latest-thumb { max-height: 168px; overflow: hidden; position: relative; }
.app.shell-modetop.is-mobile-frame .caps-strip .page-latest-thumb::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 46%;
  background: linear-gradient(to bottom, rgba(250, 250, 250, 0), var(--bg)); pointer-events: none;
}
/* Capture station: full-width device switcher + full-width action buttons. */
.caps-tools:empty { display: none; }
.app.shell-modetop.is-mobile-frame #capsTools { display: flex; flex-direction: column; align-items: stretch; gap: 10px; }
.app.shell-modetop.is-mobile-frame #capsTools .tb-device { width: 100%; }
.app.shell-modetop.is-mobile-frame #capsTools .tb-device .tb-dev { flex: 1 1 0; }
.app.shell-modetop.is-mobile-frame #capsTools .tb-device .tb-dev-more { flex: 0 0 auto; }
.app.shell-modetop.is-mobile-frame #capsTools .ghost-btn {
  width: 100%; justify-content: center; font-size: 13px; padding: 9px 12px;
  background: var(--btn2bg); border: 1px solid var(--border); border-radius: 9px; color: var(--ink65);
}
.app.shell-modetop.is-mobile-frame #capsTools .ghost-btn:hover { background: var(--btn2bg-hover); border-color: var(--bmed); color: var(--ink); }
/* Activity station empty-state line. */
.ssm-empty { display: none; }
#stage-side-msgs.is-activity.is-empty .ssm-empty { display: block; font-size: 12.5px; line-height: 1.5; color: var(--ink50); }
/* Save = the mockup's icon button: bordered rounded square, not a bare circle. */
.app.shell-modetop #tour-preview-savesetup.is-icon { border: 1px solid var(--border); border-radius: 9px; background: var(--bg); }
.app.shell-modetop #tour-preview-savesetup.is-icon:hover { background: var(--ink08); border-color: var(--bmed); }

/* Tighter side gutters to match the v6.1 mockup (~20px). The old shell set
   "generous margins" (topbar 42/32, main 36) for the left-nav layout; the
   strip + top picker don't need them. */
/* Compact bar to the v6.1 mockup (0 vertical padding, content-centred). Fixed
   height so the workspace height calc below stays exact (was a ~63px bar). */
.app.shell-modetop .topbar { padding: 0 20px; height: 62px; min-height: 62px; box-sizing: border-box; }
.app.shell-modetop .workspace { height: calc(100vh - 62px); }
/* Hairline divider before the Save / action cluster (v6.1 mockup). Only shown
   in the shell when the actions slot actually has something (editor panels). */
/* Quick capture: a desktop·tablet·phone icon trio so it's obvious it shoots all
   three sizes at once (Mark). */
/* Source pill: tighter to the v6.1 mockup (was 10px 10px 10px 18px). */
.app.shell-modetop .source-pill { padding: 6px 7px 6px 15px; gap: 13px; }
/* Appearance row in the mode menu — the relocated light/dark toggle. */
.mode-menu-theme .mode-toggle { display: inline-flex; gap: 4px; }
.mode-menu-theme { padding-top: 4px; }
.app.shell-modetop .workspace { padding: 0; }
.app.shell-modetop .workspace > .main { padding: 10px 20px 6px; }
/* Library / Guide / Settings pages sat hard against the edges (Mark) —
   give the reading surfaces proper breathing room. Capture mode keeps
   the tight padding (the canvas wants the space). */
.app.shell-modetop:not(.is-capture-mode) .workspace > .main { padding: 32px 44px 32px; }
/* Top pad 12 matches the caps-strip's 12 so 'Layouts' sits level with
   'Latest captures' (the uniform 20px sat it ~8px lower — the unequal
   top gap Mark arrowed, 17 July 2026). Sides/bottom keep the shell 20. */
.app.shell-modetop .workspace > .preview-panel { padding: 12px 20px 20px; }

/* ── Captures strip (left "Latest" channel; 200px in the capture modes and in
   Edits, minmax(264px, 300px) in Layouts where it carries the template rail) ── */
.caps-strip {
  display: none; flex-direction: column; gap: 16px;
  background: var(--bg); border-right: 1px solid var(--ink08);
  /* Top padding matches the preview panel's 12px so the strip's first heading
     sits level with the preview title / mode title (was 20px → it sat lower,
     the gap Mark spotted). The collapse chevron is lifted out of the flow
     (below) so it doesn't push the content down either. */
  padding: 12px 20px 20px; overflow-y: auto; position: relative;
  /* The scrollbar used to be hidden outright (scrollbar-width:none). That was
     defensible while this channel was just "Latest captures" — a convenience
     list you glance at. Since #142 it carries the template picker, i.e. the
     mode's PRIMARY navigation, and a scroller with no visible affordance is
     exactly the "setting out of view" complaint that started this work
     (Mark, 31 July 2026). So: a slim rail, invisible at rest, that fades in on
     hover/scroll. Present enough to say "there is more below", quiet enough not
     to draw a grey stripe down the side of the app. */
  scrollbar-width: thin; scrollbar-color: transparent transparent;
  transition: scrollbar-color .18s ease;
}
.caps-strip:hover, .caps-strip:focus-within { scrollbar-color: var(--ink08) transparent; }
.caps-strip::-webkit-scrollbar { width: 8px; }
.caps-strip::-webkit-scrollbar-track { background: transparent; }
.caps-strip::-webkit-scrollbar-thumb {
  background: transparent; border-radius: 8px;
  border: 2px solid transparent; background-clip: content-box;
  transition: background-color .18s ease;
}
.caps-strip:hover::-webkit-scrollbar-thumb,
.caps-strip:focus-within::-webkit-scrollbar-thumb { background: var(--ink08); background-clip: content-box; }
.caps-strip:hover::-webkit-scrollbar-thumb:hover { background: var(--ink32); background-clip: content-box; }
.caps-latest:empty { display: none; }

/* ── Template rail (#142) ────────────────────────────────────────────────────
   The Layouts template picker, at the TOP of the 200px channel, above the
   captures. Two of Mark's notes put it here: "i think recent captures shouldnt
   come above layouts and edits ... you dont open the capture and then add it to
   a layout. it is the other way round", and "scrolling fine when it is required.
   not when there is a setting out of view but a long list of templates".
   The markup is the SAME markup the right panel used (layoutsTemplateGroupsHtml)
   — same cards, same accordion, same hooks. Greys are the Mix tokens (Mark).
   HISTORY, because this block has been round the houses: #142 put the picker in
   a 200px rail, which left a 160px content box — too narrow for two cards, so it
   was reskinned as a one-column list of flat rows (thumb beside the label). #145
   widened the column to the right panel's track. #146 undoes the reskin: with
   224–260px of content the picker goes back to the PANEL's own two-up card grid
   — "i prefer the 2 columns layout for the templates, like we had before"
   (Mark, 1 Aug 2026). So the only overrides that survive below are the ones the
   RAIL genuinely needs and the panel never did: sticky group heads (the rail is
   the scroller), and a slightly tighter card so two of them clear 224px. Card
   shape, thumb-over-caption, and the outline selection are the panel's again. */
.rail-tpl { display: flex; flex-direction: column; }
.rail-tpl[hidden] { display: none; }
/* Matches .caps-recent-lbl exactly so 'Templates' and 'Sources' read as two
   headings of one list, not two different kinds of thing.
   The 18px below it is NOT that rule's — it is .layouts-panel-head's, copied so
   the first rail row and the first panel row start on the same line. The two
   headings already shared a top edge; at 11px the rows below them did not, and
   a 7px stagger at the top of two columns of identically-sized rows is the kind
   of thing you see without being able to name. */
.rail-tpl-lbl {
  font-family: var(--sans); font-size: 14px; font-weight: 500;
  letter-spacing: -0.005em; color: var(--ink); margin: 0 2px 18px;
}
/* Two up, like the panel. 10px gutter rather than the panel's 12 so the pair
   still clears the narrow end of the track (224px content → 107px cards). */
.rail-tpl .layouts-lay-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
.rail-tpl .layouts-lay-groupbody { padding: 2px 0 12px; }
/* No hairlines between the groups in the rail (Mark, 3 Aug: "lets remove the
   lines between the templates"). The lines came from the picker's old home in
   the right panel, where it sat among lined option rows and had to match them.
   In the rail it is the ONLY list, so the lines were drawing a table that has
   nothing to line up with — the heads' own weight and the gap under each open
   grid already say where one group ends. The panel's copy keeps its borders. */
.rail-tpl .layouts-lay-group,
.rail-tpl .layouts-lay-group:first-child { border-top: none; }
/* The rail's rhythm, matched to the right sidebar (Mark, 3 Aug 2026, twice:
   "make the gap between the drop downs the same as the right side bar", then
   "need to space the left side bar drop downs the same as the right side").
   It was asked twice because the first pass matched the WRONG column. It copied
   a capture panel's bands — a 35px box with 6px between them, pitch 41 — but
   the column actually sitting beside this rail in Layouts and Sequence is the
   flat option-row list in #previewPanel, which is a 43px box with NO gap and so
   a 43px pitch. Measured live, both columns shut, the two lists therefore ran
   at 35/6/41 against 43/0/43: every row was 8px shorter and every group had a
   6px void the right column does not have, so the two sides visibly drifted
   apart the further down you looked.
   11.5px of padding over the 20px icon gives the same 43px box, and no margin
   between groups gives the same 43px pitch — the same numbers the option rows
   opposite are already using, so a rail row and a panel row now sit on the same
   line all the way down the column.
   Only the metric is copied, not the grey band. The sidebar fills its bands
   because they sit among lined option rows and have to outrank them; the rail
   is the only list in its column and has nothing to outrank.
   Dropping the 6px also retires a known artefact: a stuck head and the one
   arriving below it used to show a 6px sliver of card between them during the
   hand-off. With the heads abutting there is no sliver to show. */
.rail-tpl .layouts-lay-group + .layouts-lay-group { margin-top: 0; }
/* Sticky heads: with a group open the list runs well past the rail, and the
   label telling you WHICH group you are scrolling through is the first thing to
   leave. --bg (not transparent) so rows pass underneath cleanly. */
.rail-tpl .layouts-lay-grouphead {
  position: sticky; top: 0; z-index: 2;
  background: var(--bg); padding: 11.5px 2px; gap: 11px;
}
/* A sticky head sticks to the SCROLLPORT's padding edge, so the strip's 12px
   top padding left a 12px band above it where rows scrolled past in the clear —
   a sliver of template thumbnail floating over the heading. In Layouts the top
   padding therefore moves off the scroller and onto the rail, which renders
   identically at rest and scrolls away like the content it is.
   It has to name the RAIL, not :first-child. The strip's first child is the
   collapse chevron — an absolutely-positioned button — so the old :first-child
   form put the 12px on the chevron (which already has top:12px, so it landed at
   24 and slid under the sticky group head: "the collapse icon is partly
   covered") and gave the rail nothing, which is why the left column started
   hard against the top while the right panel kept its 12 ("check the top gap of
   the left column as it is not the same as [right]"). Both, Mark, 1 Aug 2026.
   Sequence is in this too: the note that excluded it said it "has no rail and
   therefore no sticky heads", which stopped being true at #200 —
   renderLayoutsRail() renders for the editor as well, because a template is how
   you start a layout there. So it has the same rail with the same sticky heads
   and wants the same padding. */
.app.mode-layouts .caps-strip { padding-top: 0; }
.app.mode-layouts .caps-strip > .rail-tpl { margin-top: 12px; }
/* The left column of BOTH board modes is the TEMPLATE PICKER and nothing else
   (Mark, 3 Aug: "remove the sources/ latest captures so the left only has the
   templates", said first of Layouts and then of Sequence).
   The source bin below it was answering a question neither mode asks: you choose
   a template first and fill its slots from the slot itself, so a second list of
   captures underneath was a parallel route that competed with the one the mode
   is built around — and it pushed the templates, the actual subject of the
   column, into a short scrolling window. Hidden, not unmounted: the same nodes
   are the plain Latest captures channel in every capture mode, and
   renderCapturesStripRecent() keeps filling them. */
.app.mode-layouts #capsRecent,
.app.mode-layouts #capsLatest,
.app.mode-layouts #capsExpand { display: none; }
/* With the list gone the rail is the whole column, so it takes the height the
   list used to share and scrolls on its own rather than inside a 480px box. */
.app.mode-layouts .rail-tpl { flex: 1 1 auto; min-height: 0; }
/* ...and the chevron sits above the sticky heads whatever the scroll position,
   rather than tying at z-index 2 and losing on source order. */
.app.mode-layouts .caps-collapse { z-index: 4; }
/* A tighter card than the panel's 16/12/13 — two of these have to clear 224px at
   the narrow end of the track. Everything else (bordered card, thumb over
   caption, 1.5px outline on the selected one) is the panel's own. */
.rail-tpl .layouts-lay { padding: 13px 9px 11px; border-radius: 10px; }
/* zoom, not transform: scale — layoutsLayThumb returns variable markup (one
   wrapper div for some templates, several sibling .layouts-glyph spans for
   others), and only zoom shrinks the LAYOUT BOX with the glyphs. Chrome-only is
   fine here; this is a Chrome extension. */
.rail-tpl .layouts-lay-dg { height: 47px; zoom: .92; }
.rail-tpl .layouts-lay-cap { font-size: 12px; line-height: 1.3; margin-top: 8px; }
.rail-tpl .layouts-lay-check { top: 7px; right: 8px; }
/* ── Latest = hero card (loey-left-sidebar mockup): a live-dot eyebrow over a
   bordered card — shot on top, dark filename bar, then Download / Copy. ── */
.cs-latest-lab { display: flex; align-items: center; margin: 0 2px 11px; } /* no top nudge — level with the right panel's title */
.cs-dot { display: none; }
.cs-eyebrow { font-family: var(--sans); font-size: 14px; font-weight: 500; letter-spacing: -0.005em; text-transform: none; color: var(--ink); }
.cs-card {
  border: 1.5px solid var(--ink); border-radius: 10px; overflow: hidden; background: var(--bg);
  /* No drop shadow - the bordered card sits flat on the strip (Mark). */
}
.cs-shot { position: relative; overflow: hidden; line-height: 0; background: var(--ink08); max-height: 230px; }
.cs-shot.is-tall { max-height: 200px; }
.cs-shot img { width: 100%; display: block; cursor: pointer; }
.cs-shot-fade { position: absolute; left: 0; right: 0; bottom: 0; height: 44%; pointer-events: none; background: linear-gradient(to bottom, rgba(250, 250, 250, 0), var(--bg)); }
.cs-bar { background: var(--ink); color: var(--bg); padding: 7px 12px; display: flex; align-items: center; gap: 8px; }
.cs-fn { font-family: var(--mono); font-size: 9.5px; color: #cfcfcd; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cs-fd { font-family: var(--mono); font-size: 9.5px; color: #8a8a88; margin-left: auto; flex: none; }
/* Icon-only Download / Copy (reusing .capt-icon-btn), like the earlier strip. */
.cs-acts { display: flex; gap: 7px; padding: 6px 10px 8px; }
/* Dark mode: the bold black border + bright bar would glare, so soften them. */
.app.dark .cs-card { border: 1px solid var(--bmed); }
.app.dark .cs-bar { background: var(--paper); color: var(--ink); }
.app.dark .cs-fn { color: var(--ink65); }
.app.dark .cs-fd { color: var(--ink50); }

/* ── User folders — the quiet Drive row above the site grid (mockup A). ── */
/* Section label speaks the library's OWN sectioning voice (.lib-sec-title —
   mono small-caps, like "YESTERDAY 15") instead of the mockup's sans
   sentence case, which sat oddly beside it. (Mark, 18 July 2026: "fonts
   are a bit all over the place in the library".) */
.libu-sec { font-family: var(--sans); font-size: 13px; font-weight: 500; letter-spacing: -0.005em; text-transform: none; color: var(--ink); margin: 0 0 14px; }
/* Same track formula as .lib-folder-grid so folder tiles align column-for-
   column with the cards below (Mark, 19 July: folders were oversized). */
.libu-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(max(200px, (100% - 56px) / 5), 1fr)); gap: 14px; margin-bottom: 28px; }
.libu-folder { position: relative; display: flex; align-items: center; gap: 10px; border: 1px solid var(--ink08); border-radius: 10px; background: var(--paper); padding: 11px 14px; cursor: pointer; transition: border-color .12s; text-align: left; font: inherit; color: var(--ink); }
.libu-folder:hover { border-color: var(--ink32); }
.libu-glyph { flex: none; width: 17px; height: 17px; }
.libu-name { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* = .lib-folder-name / .lib-card-title */
.libu-count { font-family: var(--mono); font-size: 10px; color: var(--ink50); margin-left: auto; flex: none; } /* = .lib-folder-count */
.libu-folder.libu-new { border-style: dashed; background: transparent; color: var(--ink50); }
.libu-folder.libu-new .libu-name { font-weight: 400; color: var(--ink50); }
.libu-menuwrap { position: relative; flex: none; margin-left: 2px; }
.libu-kebab { border: 0; background: transparent; color: var(--ink50); width: 20px; height: 20px; border-radius: 6px; cursor: pointer; display: none; align-items: center; justify-content: center; padding: 0; }
.libu-kebab svg { width: 13px; height: 13px; }
.libu-folder:hover .libu-kebab { display: flex; }
.libu-kebab:hover { background: var(--ink08); color: var(--ink); }
.libu-menu { position: absolute; top: 24px; right: 0; z-index: 4; }
.libu-chk { color: var(--ink); float: right; }
/* ── Dressed selects: the app-styled face over any remaining native <select>
   (Style kit bars, picker site/mode filters, modal select fields) — the OS
   popup never shows; the hidden select keeps value + change semantics. ── */
.capt-dd-wrap { position: relative; display: inline-flex; min-width: 0; flex: 1 1 auto; }
.capt-dd-wrap > select { position: absolute !important; width: 1px; height: 1px; opacity: 0; pointer-events: none; margin: 0; }
.capt-dd { display: flex; align-items: center; gap: 7px; width: 100%; min-width: 0; font-family: var(--sans); font-size: 12px; line-height: 1.2; padding: 6px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--paper, #fff); color: var(--ink); cursor: pointer; text-align: left; }
.capt-dd:hover { border-color: var(--ink32); }
.capt-dd-lbl { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.capt-dd .lib-dd-chev { flex: none; color: var(--ink50); }
.capt-dd-menu { position: absolute; top: calc(100% + 5px); left: 0; min-width: 100%; max-width: 260px; max-height: 300px; overflow-y: auto; background: var(--bg); border: 1px solid var(--ink08); border-radius: 10px; box-shadow: 0 10px 28px rgba(14, 14, 12, .10); padding: 5px; z-index: 60; }
.capt-dd-menu .lib-dd-item { display: block; width: 100%; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The picker's compact filter keeps its 32px height + width cap. */
.layouts-pk-head .capt-dd-wrap { flex: 0 1 auto; max-width: 150px; }
.layouts-pk-head .capt-dd { height: 32px; padding: 0 8px; border-radius: 9px; border-color: var(--rule); background: var(--card-fill); font-size: 12.5px; }

/* Breadcrumb for an open user folder on the Saved-layouts shelf — same
   mono-caps trail as the captures library's crumb (.lib-crumb-link /
   .lib-crumb-here), not its own sans style. */
.libu-crumb { display: flex; align-items: center; gap: 8px; margin: 0 0 16px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; }
.libu-crumb-back { border: 0; background: transparent; padding: 0; font: inherit; letter-spacing: inherit; text-transform: inherit; color: var(--ink50); cursor: pointer; }
.libu-crumb-back:hover { color: var(--ink); }
.libu-crumb-sep { color: var(--ink32); }
.libu-crumb-here { color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* ── Recent = card list (shot + meta row: page title + relative time). ── */
/* Match the right-panel mode heading (.layouts-panel-title): 16px / -.005em. */
.caps-recent-lbl { font-family: var(--sans); font-size: 14px; font-weight: 500; letter-spacing: -0.005em; text-transform: none; color: var(--ink); margin: 0 2px 11px; }
/* "Latest capture" heading in the captures strip — same treatment. */
.caps-latest .preview-group-label { font-size: 16px; letter-spacing: -0.005em; }
.caps-recent:has(.caps-list:empty) { display: none; }
/* ── Source bins (#142): All / Captures / Styled / Layouts ──────────────────
   Shown only in Layouts and Edits, where this list stops being something you
   browse and becomes something you pull FROM. Small, quiet, and only ever
   offering a bin that has something in it. */
.caps-src { display: flex; flex-wrap: wrap; gap: 5px; margin: 0 2px 11px; }
.caps-src[hidden] { display: none; }
.caps-src-chip {
  border: 1px solid var(--border); background: transparent; color: var(--ink50);
  font-family: var(--sans); font-size: 11px; line-height: 1;
  padding: 5px 8px; border-radius: 7px; cursor: pointer;
  transition: border-color .12s ease, color .12s ease, background-color .12s ease;
}
.caps-src-chip:hover { border-color: var(--ink32); color: var(--ink); }
.caps-src-chip.is-selected { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.caps-list { display: flex; flex-direction: column; gap: 11px; }
.cs-rcard-wrap { position: relative; }
.cs-rcard-recap { position: absolute; top: 8px; right: 8px; width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: 0; border-radius: 8px; background: rgba(16, 16, 14, 0.66); color: #FBFAF8; cursor: pointer; opacity: 0; pointer-events: none; transition: background .14s ease, opacity .14s ease, transform .12s ease; -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.cs-rcard-wrap:hover .cs-rcard-recap, .cs-rcard-recap:focus-visible { opacity: 1; pointer-events: auto; }
.cs-rcard-recap:hover { background: rgba(16, 16, 14, 0.9); transform: scale(1.06); }
.cs-rcard-recap svg { width: 15px; height: 15px; }
/* Saving a capture back from the cloud: the glyph STAYS (cloudSaveFile no
   longer overwrites it with a word) and the button just goes quiet. It also
   holds itself visible, so the busy state is still readable if the pointer
   wanders off the card while the file is coming down. */
.cs-rcard-recap.is-busy { opacity: 1; pointer-events: none; cursor: default; }
.cs-rcard-recap.is-busy svg, .capt-icon-btn.is-busy svg { opacity: .5; }
.capt-icon-btn.is-busy, .capt-icon-btn:disabled { cursor: default; }
.cs-rfav { width: 14px; height: 14px; border-radius: 3px; object-fit: cover; flex: none; background: var(--ink08); }
.cs-rfav-fb { display: inline-flex; align-items: center; justify-content: center; font-size: 8px; font-weight: 600; color: var(--ink50); }
.cs-rmeta .cs-rt { flex: 1 1 auto; min-width: 0; }
.cs-rdev { flex: none; display: inline-flex; align-items: center; color: var(--ink50); }
.cs-rdev svg { width: 13px; height: 13px; }
/* The mode glyph sits with the device one — same weight, same quiet ink, so the
   pair reads as one line of metadata rather than an icon and a decoration. */
.cs-rmode { flex: none; display: inline-flex; align-items: center; color: var(--ink50); }
.cs-rmode svg { width: 13px; height: 13px; }
.cs-rmeta .cs-rd { margin-left: 4px; }
.cs-rcard { display: block; width: 100%; text-align: left; padding: 0; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--bg); cursor: pointer; transition: border-color .14s ease, box-shadow .14s ease; }
.cs-rcard:hover { border-color: var(--bmed); box-shadow: 0 4px 14px rgba(14, 14, 12, .05); }
/* The newest capture is THE highlighted card (Mark, 16 July 2026: one simple
   list, most recent highlighted) — the app's ink selection grammar. */
.cs-rcard.is-latest { border: 1.5px solid var(--ink); }
.cs-rcard.is-latest:hover { border-color: var(--ink); }
/* Its quick actions (download / copy / open) — shown when this session still
   holds the capture's pixels (lastCaptures); a small row under the card. */
.cs-latest-acts { margin: 6px 2px 2px; display: flex; }
/* Library-style thumbs (Mark): the WHOLE capture fits in the square
   (object-fit: contain), matching the library cards. Only genuinely long
   captures (.is-tall = thumbCropped: full page / long range) fill from
   the top and fade out the cut bottom — same rule as .lib-thumb. */
/* FILL THE SQUARE (Molly, via Mark, 11 Aug 2026: "the latest captures in side
   bar to fill the thumbnail space"). It was `contain`, which fitted the whole
   capture inside the 1:1 box and left --bg2 showing as bars down the sides of
   anything that was not square — which is most captures. The row read as a
   column of grey tiles with small pictures in them.
   `cover` from the top is the same call as the capture-window thumbnail earlier
   today, and for the same reason: the top of a capture is the part you
   recognise it by, and clicking opens it whole. */
.cs-rshot { display: flex; align-items: center; justify-content: center; position: relative; aspect-ratio: 1 / 1; overflow: hidden; background: var(--bg2); line-height: 0; }
.cs-rshot img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.cs-rshot.is-tall img { width: 100%; height: 100%; max-width: none; max-height: none; object-fit: cover; object-position: top; }
.cs-rshot-fade { position: absolute; left: 0; right: 0; bottom: 0; height: 38%; pointer-events: none; background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, var(--bg2) 100%); }
/* The square is sized off the COLUMN, so widening the Layouts rail to 300px
   inflated every capture card with it — a 260px tall thumbnail for a list you
   glance at ("they seem quite big in the sidebar", Mark, 1 Aug 2026). Cap the
   height and let the box go landscape; the image is `contain`-fitted and centred
   either way, so this only takes back the dead air around it. 168 is the same
   ceiling the mobile-frame strip already uses, and it lands the card within a
   few px of the 160 square it had at the old 200px width. Layouts only — Edits
   and the capture modes are back on 200px, where the square already fits. */
.app.mode-layouts:not(.mode-edits) .cs-rshot { max-height: 168px; }
/* 6px, not 8: the row gained the mode glyph on 11 Aug 2026, and at 8px the
   favicon, title, two icons and the date stopped reading as one line. */
.cs-rmeta { display: flex; align-items: center; gap: 6px; padding: 8px 11px; border-top: 1px solid var(--border); }
.cs-rt { font-size: 11.5px; color: var(--ink65); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cs-rd { font-family: var(--mono); font-size: 9px; color: var(--ink50); margin-left: auto; flex: none; }
.caps-play { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 18px; height: 18px; border-radius: 50%; background: rgba(14, 14, 12, 0.55); display: grid; place-items: center; pointer-events: none; }
.caps-play::after { content: ""; border-left: 5px solid #fff; border-top: 3px solid transparent; border-bottom: 3px solid transparent; margin-left: 1px; }

/* ── Footer: All captures · N › ── */
/* A real button, matching the recent-capture cards above it — bordered, rounded,
   with the same hover — instead of a borderless row with a separator line
   (Mark, 13 July 2026: remove the line, style it like our buttons). */
.caps-expand {
  margin-top: 4px; display: flex; align-items: center; gap: 8px;
  color: var(--ink65); cursor: pointer; background: var(--bg);
  border: 1px solid var(--border); border-radius: 10px;
  padding: 11px 13px; font-size: 13px; font-family: var(--sans);
  transition: border-color .14s ease, box-shadow .14s ease, color .12s ease;
}
.caps-expand-lbl { flex: none; }
.caps-count { font-family: var(--mono); font-size: 10px; color: var(--ink50); }
.caps-expand-chev { margin-left: auto; color: var(--ink50); }
.caps-expand:hover { color: var(--ink); border-color: var(--bmed); box-shadow: 0 4px 14px rgba(14, 14, 12, .05); }
.caps-collapse { position: absolute; top: 12px; right: 14px; z-index: 2; align-self: flex-end; color: var(--ink50); cursor: pointer; padding: 2px; background: transparent; border: 0; display: grid; place-items: center; transition: color .12s ease; }
.caps-collapse:hover { color: var(--ink); }
.app.caps-collapsed .caps-collapse { position: static; align-self: center; }

/* Hide the right-panel latest block — it now lives in the strip (Mark). */
.app.shell-modetop #tour-rail-latest { display: none; }

/* Collapsed: the strip tucks to a thin rail (just the expand handle); the
   preview reclaims the width. Persisted in localStorage. */
.app.caps-collapsed.shell-modetop.is-capture-mode .workspace { grid-template-columns: var(--caps-w, 30px) minmax(0, 1fr) minmax(280px, 360px); }
.app.caps-collapsed.shell-modetop.is-capture-mode .workspace.tour-preview-mode,
.app.caps-collapsed.shell-modetop.is-capture-mode.sidebar-collapsed .workspace.tour-preview-mode { grid-template-columns: var(--caps-w, 30px) minmax(0, 1fr) minmax(264px, 300px); }
.app.caps-collapsed.shell-modetop.is-capture-mode .workspace.tour-preview-mode.rail-wide,
.app.caps-collapsed.shell-modetop.is-capture-mode.sidebar-collapsed .workspace.tour-preview-mode.rail-wide { grid-template-columns: var(--caps-w, 30px) minmax(0, 1fr) minmax(316px, 372px); }
.app.caps-collapsed .caps-strip { gap: 0; padding: 12px 0; }
.app.caps-collapsed .caps-strip .caps-latest,
.app.caps-collapsed .caps-strip .caps-recent,
/* The template rail belongs in this list too (#142) — without it a collapsed
   30px strip would still try to lay out thirty-odd template rows inside it. */
.app.caps-collapsed .caps-strip .rail-tpl,
.app.caps-collapsed .caps-strip .caps-expand { display: none; }

/* ── Mode picker (top-left) ── */
.mode-menu { position: relative; display: none; }
.app.shell-modetop .mode-menu { display: block; }
.mode-menu-trigger {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--border); border-radius: 10px;
  padding: 5px 8px 5px 11px; background: var(--bg); cursor: pointer; color: var(--ink);
}
.mode-menu-trigger:hover { background: var(--ink08); }
.mode-menu-glyph { width: 20px; height: 20px; border-radius: 6px; background: var(--ink); color: var(--paper); display: grid; place-items: center; flex: none; }
.mode-menu-glyph svg { width: 13px; height: 13px; }
.app.dark .mode-menu-glyph { color: var(--canvas); }
/* One line now that the group label is off the trigger (Mark, 11 Aug 2026).
   The column was there to stack the eyebrow above the name; with one child it
   left the name sitting high in a two-line box. */
.mode-menu-text { display: flex; align-items: center; line-height: 1.15; text-align: left; }
/* #295/#299 — the top bar's chip names the territory above the mode; same rule. */
.mode-menu-group { font-family: var(--sans); font-size: 9.5px; font-weight: 500; letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink50); }
.mode-menu-name { font-size: 13px; color: var(--ink); font-weight: 500; }
.mode-menu-caret { color: var(--ink50); margin-left: 2px; }
.mode-menu-pop {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 90;
  min-width: 232px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 14px 40px rgba(17, 17, 17, 0.16); padding: 8px;
}
.mode-menu-pop[hidden] { display: none; }
.mode-menu-sec + .mode-menu-sec { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--ink08); }
/* #295/#299 — a group name in the body face; caps, like the nav's. See .ln-group. */
.mode-menu-eyebrow { font-family: var(--sans); font-size: 10px; font-weight: 500; letter-spacing: 0.07em; text-transform: uppercase; color: var(--ink50); padding: 6px 10px 4px; }
.mode-menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; border: 0; background: transparent;
  border-radius: 8px; padding: 8px 10px; font-size: 13px; color: var(--ink65); cursor: pointer;
}
.mode-menu-item svg { width: 15px; height: 15px; flex: none; color: var(--ink50); }
.mode-menu-item:hover { background: var(--ink08); color: var(--ink); }
.mode-menu-item:hover svg { color: var(--ink65); }
.mode-menu-item.is-active { background: var(--pill); color: var(--ink); font-weight: 500; }
.mode-menu-item.is-active svg { color: var(--ink); }

/* View switch folded into the mode menu (shell): retire the standalone
   dropdown and dress the relocated toggle as a menu row. */
.app.shell-modetop .view-switch { display: none; }
.mode-menu-view[hidden] { display: none; }
.mode-menu-view #tour-preview-toggle {
  display: block; width: 100%; text-align: left; border: 0; background: transparent;
  border-radius: 8px; padding: 8px 10px; font-size: 13px; line-height: 1.2; height: auto; min-height: 0;
  color: var(--ink65); cursor: pointer; font-family: var(--sans); font-weight: 400; letter-spacing: 0;
}
.mode-menu-view #tour-preview-toggle:hover { background: var(--ink08); color: var(--ink); }

/* ── Top-right nav cluster (Library / Guide / Settings) ── */
.topbar-nav { display: none; align-items: center; gap: 4px; margin-right: 6px; }
.app.shell-modetop .topbar-nav { display: flex; }
/* Nav now anchors on the LEFT (inside topbar-start, after the mode menu) so it
   never shifts when the Capture/Save actions appear on the right. The hairline
   that separated it from the mode chip was dropped (Mark, 9 Jul 2026) - the
   gap carries the separation. */
.app.shell-modetop .topbar-start > .topbar-nav {
  margin-right: 0;
  padding-left: 8px;
}
.topbar-nav-btn { width: 33px; height: 33px; border: 0; border-radius: 9px; background: transparent; display: grid; place-items: center; cursor: pointer; color: var(--ink50); }
.topbar-nav-btn:hover { background: var(--ink08); color: var(--ink); }
.topbar-nav-btn.is-active { background: var(--pill); color: var(--ink); }
.topbar-nav-btn svg { width: 17px; height: 17px; }

/* ── Topbar overflow (⋯) — narrow widths ──────────────────────────────────
   Below ~960px the Library/Guide/Settings nav + Save cluster fold INTO this
   menu so the primary Capture CTA always has room (Mark: "capture button cut
   off … looks poor"). Shown/hidden by updateTopbarOverflow() via [hidden]. */
.topbar-overflow { position: relative; display: flex; align-items: center; margin-left: 2px; }
.topbar-overflow[hidden] { display: none; }
.topbar-overflow-menu {
  position: absolute; top: calc(100% + 7px); right: 0; z-index: 60;
  min-width: 188px;
  background: var(--paper);
  border: 1px solid var(--bmed); border-radius: 10px;
  padding: 6px;
  box-shadow: 0 12px 32px rgba(17, 17, 17, 0.17);
  display: flex; flex-direction: column; gap: 2px;
}
.topbar-overflow-menu[hidden] { display: none; }
/* Relocated nav → vertical labelled rows (label read from aria-label). Use the
   menu id so these beat ".app.shell-modetop .topbar-nav" (0,3,0). */
#topbarOverflowMenu .topbar-nav { display: flex; flex-direction: column; align-items: stretch; gap: 2px; margin: 0; }
#topbarOverflowMenu .topbar-nav-btn {
  width: 100%; height: auto; justify-content: flex-start;
  display: flex; align-items: center; gap: 11px;
  padding: 9px 10px; border-radius: 8px;
  font-family: var(--sans); font-size: 13px; color: var(--ink);
}
#topbarOverflowMenu .topbar-nav-btn::after { content: attr(aria-label); }
#topbarOverflowMenu .topbar-nav-btn svg { width: 17px; height: 17px; flex: none; }
#topbarOverflowMenu .topbar-nav-btn:hover { background: var(--ink08); }
/* Relocated Save cluster → full-width row under a hairline. */
#topbarOverflowMenu #setup-save-wrap {
  width: 100%; box-sizing: border-box;
  padding: 9px 10px 5px; margin-top: 4px;
  border-top: 1px solid var(--bmed);
}
#topbarOverflowMenu .setup-link-indicator { max-width: none; margin-right: auto; }
/* No setup loaded → the bare disk icon earns a label like the nav rows. */
#topbarOverflowMenu #setup-link-indicator[hidden] ~ #tour-preview-savesetup::after {
  content: 'Save setup'; margin-left: 11px;
  font-family: var(--sans); font-size: 13px; color: var(--ink);
}

/* ───────── Home (launchpad — default landing) ───────── */
/* Full-width content page: drop the capture sidebar, caps strip + preview. */
.app.mode-home .workspace { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
.app.mode-home .workspace > .sidebar-col,
.app.mode-home .workspace > .preview-panel,
.app.mode-home .caps-strip { display: none; }
.app.mode-home .workspace > .main { grid-column: 1; }

/* Library is a full-width content page like Home/Guide/Settings — the old
   two-item rail (All captures / Saved setups) is now in-page tabs, so the
   sidebar column earns nothing here. Content stays capped at 1120px, centred
   (see .panel[data-panel="history-all"]). (Mark) */
.app.mode-library .workspace { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
.app.mode-library .workspace > .sidebar-col,
.app.mode-library .workspace > .preview-panel,
.app.mode-library .caps-strip { display: none; }
.app.mode-library .workspace > .main { grid-column: 1; }

/* Area capture WAS back on paper (15 Aug: "actually the grey isnt working")
   — settled for the bordered-card grid of that week, where grey under cards
   read as three stacked surfaces. Round 7 took the cards' borders off (bare
   plates), round 9 gave them a hairline, and ROUND 12 flips the ground the
   other way (Mark: "I think we should try the grey background again"):
   bare plates on the work-area grey, the same ground the editors stand on —
   which is also what makes a white capture readable without leaning on the
   hairline alone. The paper override is simply GONE, so the base
   `.workspace > .main { background: var(--work-bg) }` rule answers for
   Review the way it answers for every other mode. */
/* ── AND THE SAME RAIL WIDTH AS EVERY OTHER CAPTURE MODE ──────────────────
   Mark, 14 Aug 2026: "why is the rail so wide? why different to what it was
   before and like most other modes."

   Measured, not guessed: Viewport and Page render a 300px rail, Area rendered
   360. The 264–300 column belongs to `.workspace.tour-preview-mode`, which is
   set when a mode opens its embedded preview — and Area has no embedded
   preview any more, so it kept falling through to the 280–360 default. The
   width was never a decision about Area; it was the absence of one. */
.app.shell-modetop.is-capture-mode.mode-area .workspace,
.app.caps-collapsed.shell-modetop.is-capture-mode.mode-area .workspace {
  grid-template-columns: var(--caps-w, 200px) minmax(0, 1fr) minmax(264px, 300px);
}
.app.caps-collapsed.shell-modetop.is-capture-mode.mode-area .workspace {
  grid-template-columns: var(--caps-w, 30px) minmax(0, 1fr) minmax(264px, 300px);
}
.app.sidebar-collapsed.shell-modetop.is-capture-mode.mode-area .workspace {
  grid-template-columns: var(--caps-w, 64px) minmax(0, 1fr) minmax(264px, 300px);
}
/* (Dark-mode override removed with the light one — the default capture-mode
   tint applies in both themes.) */

/* Home keeps the centred source pill like the capture modes — the launchpad
   should say what it's pointed at, with the same Change/tab-picker (Mark). The
   mode picker stays hidden (you pick a mode from the cards below). */
.app.mode-home #modeMenu { display: none; }
/* The launchpad reads editorial, not panel — pull it up (Mark, 19 July). */
.app.mode-home .workspace > .main { padding-top: 10px; }
.home { max-width: 1080px; margin: 0 auto; padding: 0 0 20px; }
/* Headings use sans — mono is reserved for small meta (counts, timestamps). (Mark)
   Kept after #297 made .eyebrow itself sans: this is the rule that recorded the
   decision on 23 July, and the one a future edit to the base would be checked
   against. It asserts rather than overrides now. */
.home .eyebrow { font-family: var(--sans); }
.home-greeting {
  font-family: var(--serif); font-size: 24px; line-height: 1.15;
  letter-spacing: -0.01em; font-weight: 500; color: var(--ink); margin: 4px 0 0;
}
/* Recents group: more air above it now the resume chip lives in its
   header row (Mark, 24 July: "need more gap above recent captures"). */
.home-grp { margin-top: 46px; }
/* Territory labels use the TITLE voice, not caps/tracking (Mark, 23 July
   2026: "mono should not be used for titles") — same treatment as
   "Recent captures" (.home-sec-title). Whispers keep their quiet size. */
.home-grp-lbl {
  margin: 0 0 9px;
  font-family: var(--sans); font-size: 14px; font-weight: 500;
  color: var(--ink); text-transform: none; letter-spacing: -0.005em;
}
.home-modes { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.home-mode-card {
  display: flex; flex-direction: column; align-items: flex-start; text-align: left;
  gap: 3px; padding: 12px 13px 13px; border-radius: 10px;
  background: var(--paper); border: 0.5px solid var(--bmed);
  cursor: pointer; transition: border-color .12s ease, background .12s ease;
}
.home-mode-card:hover { border-color: var(--bstr); background: var(--ink08); }
.home-mode-ico { display: inline-flex; color: #F7CE46; }
.home-mode-ico svg { width: 19px; height: 19px; }
.home-mode-name { font-size: 13px; font-weight: 500; color: var(--ink); }
.home-mode-desc { font-size: 11.5px; line-height: 1.3; color: var(--ink50); }
/* ── First-run intro band (pre-launch item 7): a quiet card, not a wizard.
   Mono step numeral, one sentence, one ink button, a ✕ that ends it. Same
   edge treatment as the other home cards. ── */
.home-intro { margin-top: 10px; }
.home-intro-band {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 15px; border-radius: 10px;
  background: var(--paper); border: 1px solid var(--border);
}
.home-intro-num {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
  color: var(--ink32); flex: none;
}
.home-intro-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.home-intro-title { font-size: 13.5px; font-weight: 500; color: var(--ink); letter-spacing: -.005em; }
.home-intro-sub { font-size: 12px; color: var(--ink50); line-height: 1.45; }
.home-intro-btn {
  flex: none; border: 0; border-radius: 100px; cursor: pointer;
  background: var(--ink); color: var(--paper);
  font-family: var(--sans); font-size: 12.5px; font-weight: 500;
  padding: 8px 16px; line-height: 1;
}
.home-intro-btn:hover { opacity: .88; }
.app.dark .home-intro-btn { color: var(--canvas); }
.home-intro-x {
  flex: none; border: 0; background: none; cursor: pointer;
  color: var(--ink32); font-size: 12px; padding: 6px; border-radius: 7px; line-height: 1;
}
.home-intro-x:hover { color: var(--ink); background: var(--ink08); }
.home-intro-sub b { font-weight: 500; color: var(--ink65); }
/* Quiet inline switch — same register as the crumb links. */
.home-intro-alt {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--ink50); text-decoration: underline; text-underline-offset: 2px;
}
.home-intro-alt:hover { color: var(--ink); }

/* Resume is a compact CHIP inside the Recent captures header row (Mark,
   23 July round 2): its own strip between modes and recents still read
   as one line too many. Chip sits between the title and View library. */
.home-resume { margin-left: auto; margin-right: 14px; min-width: 0; }
.home-resume[hidden] { display: none !important; }
/* Quiet Upgrade pill, bottom-right of the home page (free tier only). */
.home-upgrade {
  position: fixed; right: 22px; bottom: 18px; z-index: 40;
  font-family: var(--sans); font-size: 11.5px; font-weight: 500;
  color: var(--ink50); background: var(--paper);
  border: 0.5px solid var(--bmed); border-radius: 100px;
  padding: 7px 14px; cursor: pointer;
  transition: color .12s ease, border-color .12s ease;
}
.home-upgrade:hover { color: var(--ink); border-color: var(--bstr); }
.home-upgrade[hidden] { display: none !important; }
/* The chip itself: square-rounded like the app's small buttons, not a
   100px pill (Mark, 24 July) — Resume · mode/site · age, whole chip is
   the button. */
.home-resume-card {
  display: inline-flex; align-items: center; gap: 8px; max-width: 100%; text-align: left;
  padding: 5px 11px; border-radius: 8px;
  background: var(--paper); border: 0.5px solid var(--bmed);
  cursor: pointer; transition: border-color .12s ease;
}
.home-resume-card:hover { border-color: var(--bstr); }
.home-resume-go { flex: none; font-size: 11.5px; font-weight: 500; color: var(--ink); }
.home-resume-sub {
  flex: 0 1 auto; min-width: 0; font-size: 11.5px; color: var(--ink50);
  border-left: 1px solid var(--border); padding-left: 8px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.home-resume-age { flex: none; font-size: 11px; color: var(--ink32); }
/* "Recent captures" is a section TITLE, not a caps label (Mark, 19 July). */
.home-sec-title { margin: 0; font-family: var(--sans); font-size: 14px; font-weight: 500; color: var(--ink); letter-spacing: -0.005em; }
/* An intro step on stage hides the resume strip + recents — one story. */
.home.has-intro .home-resume, .home.has-intro .home-grp { display: none !important; }
/* Centered, not baseline — the resume chip in the row has real height. */
.home-recent-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 9px; min-height: 27px; }
.home-recent-head .home-sec-title, .home-recent-head .home-link { flex: none; }
.home-link { background: transparent; border: 0; padding: 0; font-family: inherit; font-size: 11.5px; color: var(--ink50); cursor: pointer; }
.home-link:hover { color: var(--ink); }
.home-recent { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.home-rcard { display: flex; flex-direction: column; gap: 6px; background: transparent; border: 0; padding: 0; cursor: pointer; text-align: left; }
.home-rshot {
  position: relative; display: block; aspect-ratio: 16 / 10; border-radius: 8px;
  overflow: hidden; background: var(--ink08); border: 1px solid var(--border);
}
.home-rshot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.home-rt { font-size: 11px; color: var(--ink50); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-recent-empty {
  grid-column: 1 / -1;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 22px 16px; border: 1px dashed var(--bmed); border-radius: 10px;
  font-size: 12px; color: var(--ink50); text-align: center;
}
.home-recent-empty-ico { color: var(--ink32); display: inline-flex; }

/* ── Home source-aware states: unsupported warning, guide hint, Layouts band ── */
.home-warn {
  display: flex; align-items: center; gap: 13px;
  margin-top: 14px; padding: 14px 16px; border-radius: 10px;
  background: rgba(186, 117, 23, 0.06); border: 1px solid rgba(186, 117, 23, 0.28);
}
/* The display:flex above beats the browser's [hidden]{display:none}, so the
   empty box showed as a stray orange pill until re-rendered — respect hidden. */
.home-warn[hidden] { display: none !important; }
.home-warn-ico { flex: none; color: #F7CE46; display: inline-flex; }
.home-warn-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.home-warn-title { font-size: 13px; font-weight: 500; color: var(--ink); }
.home-warn-desc { font-size: 11.5px; line-height: 1.4; color: var(--ink50); }
.home-warn-btn {
  flex: none; background: var(--ink); color: var(--paper); border: 0;
  font-family: var(--sans); font-size: 12px; padding: 8px 15px; border-radius: 9px; cursor: pointer;
  transition: background .12s ease;
}
.home-warn-btn:hover { background: #34322D; }
/* Blocked: the current tab can't be captured → dim + disable the capture grids.
   Layouts / Recent stay usable (you can still open the library). */
.home.is-blocked .home-modes:not(#home-modes-compose) { opacity: 0.4; pointer-events: none; }

.home-guide-hint {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 26px;
  background: transparent; border: 0; padding: 0; cursor: pointer;
  font-family: var(--sans); font-size: 12px; color: var(--ink50);
}
.home-guide-hint > svg { flex: none; color: var(--ink32); }
.home-guide-hint b { color: var(--ink); font-weight: 500; }
.home-guide-hint:hover { color: var(--ink); }


/* ───────── Page Tour stop chips (icon + number) ───────── */
/* The stop-type word (Start / Pause / Slow / End) is shown as an icon; the
   number still sequences the in-between stops. (Mark) */
.tour-chip { display: inline-flex; align-items: center; gap: 5px; color: var(--ink65); vertical-align: middle; }
.tour-chip svg { width: 14px; height: 14px; flex: none; display: block; }
.tour-chip-n { font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em; color: var(--ink50); }

/* ───────── Cloud backup folder (Mark — cloud read-back) ─────────
   The library's cloud folder: a folder card on the grid, then a flat,
   month-grouped file listing inside. Rows are files, not captures — no
   thumbnails, just a type glyph, name, date · size, and a save action. */
.lib-cloud-note {
  font-size: 12px;
  color: var(--ink50);
  margin: 4px 0 18px;
}
/* Search + site filter above the cloud listing (Mark, 20 July 2026).
   The search input inherits .lib-search's compact size (220px) — same as the
   library's own "Search captures…" box, but scoped to this file list. */
.lib-cloud-tools { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
.lib-cloud-site {
  flex: none; max-width: 240px; height: 32px;
  border: 1px solid var(--bmed); background: var(--paper); border-radius: 8px;
  padding: 0 9px; font-size: 12.5px; color: var(--ink); font-family: var(--sans);
  outline: none; cursor: pointer;
}
.lib-cloud-site:focus { border-color: var(--ink); }
.lib-cloud-site:focus-visible { outline: none; }
.lib-cloud-month { margin-top: 18px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink50); }
.lib-cloud-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 2px;
  border-bottom: 1px solid var(--border);
  min-width: 0;
}
.lib-cloud-open {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.lib-cloud-open:hover .lib-cloud-name { text-decoration: underline; text-underline-offset: 3px; }
.lib-cloud-kind { flex: none; display: inline-flex; color: var(--ink50); }
.lib-cloud-kind svg { width: 15px; height: 15px; }
.lib-cloud-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12.5px;
  color: var(--ink);
}
.lib-cloud-meta { flex: none; font-size: 11.5px; color: var(--ink50); }
.lib-cloud-get { flex: none; white-space: nowrap; }

/* ───────── Cloud capture lightbox (Mark — view in place, no new tab) ─────────
   Fit-to-screen popup inside the app window: bar (name · size · save · ×)
   over a stage that CONTAINS the media — images letterbox, video plays
   inline. Same scrim treatment as the modal root. */
.cloud-lightbox {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
}
.cloud-lightbox[hidden] { display: none; }
.cloud-lightbox-scrim {
  position: absolute;
  inset: 0;
  background: rgba(14, 14, 12, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.cloud-lightbox-card {
  position: relative;
  display: flex;
  flex-direction: column;
  max-width: min(1200px, 94vw);
  max-height: 92vh;
  min-width: 320px;
  background: var(--bg, var(--paper));
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
}
.cloud-lightbox-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  flex: none;
}
.cloud-lightbox-hint {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
  padding: 9px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--acc-band);
  font-size: 12.5px;
  color: var(--ink-soft);
}
.cloud-lightbox-hint-text { flex: 1 1 auto; line-height: 1.4; }
.cloud-lightbox-hint-text b { font-weight: 500; color: var(--ink); }
.cloud-lightbox-hint .ghost-btn { flex: none; }

/* ── Lightbox head, variant A (Mark, 25 July 2026) ────────────────────
   The grey-on-grey stack read flat — grey head, grey pills, grey band,
   nothing leading. Now: paper head as one clean surface, Save is the
   ONE solid-ink primary, Style + the repair actions are outlined
   ghosts, the zoom stepper drops to a hairline capsule, and the hint
   slims to a whisper line on paper. Button SHAPE untouched (the house
   8px radius — Mark: "they shouldn't be completely rounded"). */
.cloud-lightbox-bar { background: var(--paper); }
.cloud-lightbox-name { font-weight: 500; }
.cloud-lightbox-meta { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: var(--ink32); }
.cloud-lightbox-save { background: var(--ink); color: var(--paper); font-weight: 500; }
.cloud-lightbox-save:hover { background: var(--ink); color: var(--paper); opacity: 0.85; }
.cloud-lightbox-save:disabled { opacity: 0.45; }
/* CROP JOINS THE OUTLINED SET (28 Aug 2026, Mark: "in the pop out, the crop
   button is grey. different to everything else. shall we have it outline like
   style?"). It shipped on 27 Aug as a bare .ghost-btn and so kept the base
   class's grey fill — the exact grey-on-grey this variant-A pass had already
   removed from this bar. It is a secondary action of the same rank as Style
   and Repair, so it takes their dress; the bar keeps ONE solid primary
   (Download), which is the whole point of the rule. */
.cloud-lightbox-style, .cloud-lightbox-repair, .cloud-lightbox-crop,
.cloud-lightbox-hint .ghost-btn, .cloud-lightbox-hint .ghost-btn.is-choice {
  background: transparent; border: 1px solid var(--bmed); color: var(--ink);
}
.cloud-lightbox-style:hover, .cloud-lightbox-repair:hover, .cloud-lightbox-crop:hover,
.cloud-lightbox-hint .ghost-btn:hover { background: transparent; border-color: var(--bstr); color: var(--ink); }
.cloud-lightbox-hint { background: var(--paper); padding: 7px 16px; font-size: 12px; color: var(--ink50); }
.cloud-lightbox-hint .ghost-btn { font-size: 12px; padding: 6px 13px; }
.cloud-lightbox-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12.5px;
  color: var(--ink);
}
.cloud-lightbox-meta { flex: none; font-size: 11.5px; color: var(--ink50); }
.cloud-lightbox-save { flex: none; white-space: nowrap; }
.cloud-lightbox-style { flex: none; white-space: nowrap; }
.cloud-lightbox-crop, .cloud-lightbox-repair { flex: none; white-space: nowrap; }
.cloud-lightbox-x {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--ink50);
  font-size: 20px;
  line-height: 1;
  padding: 2px 6px;
}
.cloud-lightbox-x:hover { color: var(--ink); }
.cloud-lightbox-stage {
  flex: 1 1 auto;
  min-height: 0; /* let flex shrink the stage — a fixed floor overflowed and clipped tall captures (Mark) */
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--bg2, var(--paper));
}
.cloud-lightbox-stage img,
.cloud-lightbox-stage video {
  /* Bind the max to the VIEWPORT, not the flex parent (its height is
     indefinite, so max-height:100% was ignored — tall phone captures then
     rendered at natural size clamped only by width: a wide, clipped
     "desktop-shaped" slice). Viewport units keep every capture's true
     aspect and let the card hug it — phone reads phone, desktop desktop. */
  max-width: min(1160px, 90vw);
  max-height: calc(92vh - 64px);
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
}
.cloud-lightbox-stage video { background: #000; }
/* Zoom to inspect long / tall captures. When zoomed the stage scrolls and
   the image can be dragged to pan (Mark). */
.cloud-lightbox-stage.is-zoomed { display: block; overflow: auto; cursor: grab; }
.cloud-lightbox-stage.is-zoomed img { margin: auto; }
.cloud-lightbox-stage.is-grabbing { cursor: grabbing; }
/* Hairline capsule at the house radius (Mark, 25 July 2026: the grey pill
   read wrong against the new paper head — and nothing here is fully round). */
.cloud-lightbox-zoom { flex: none; display: inline-flex; align-items: center; gap: 2px; margin-left: 4px; padding: 2px; background: transparent; border: 1px solid var(--bmed); border-radius: 8px; }
/* display:inline-flex above would override the `hidden` attribute — reassert it
   so the zoom controls truly disappear for video and non-shrunk images. */
.cloud-lightbox-zoom[hidden] { display: none; }
.cloud-lightbox-zbtn { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 24px; padding: 0; background: transparent; border: 0; border-radius: 6px; color: var(--ink50); cursor: pointer; }
.cloud-lightbox-zbtn:hover { background: var(--ink08); color: var(--ink); }
.cloud-lightbox-zlevel { min-width: 40px; height: 24px; padding: 0 8px; background: transparent; border: 0; border-radius: 6px; font-family: var(--mono); font-size: 10.5px; letter-spacing: .04em; color: var(--ink50); cursor: pointer; }
.cloud-lightbox-zlevel:hover { background: var(--ink08); color: var(--ink); }
.cloud-lightbox-state {
  padding: 60px 40px;
  font-size: 12.5px;
  color: var(--ink50);
  text-align: center;
}

/* Layouts picker — video clip length chip + over-cap (>10s) cards greyed
   out and inert, so a too-long clip can't be picked and error after the
   cloud fetch. (Mark) */
.lpk-dur {
  position: absolute;
  right: 6px;
  bottom: 6px;
  padding: 2px 7px;
  border-radius: 100px;
  background: rgba(14, 14, 12, 0.66);
  color: #FBFAF8;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  pointer-events: none;
}
.lib-card.lpk-toolong { opacity: 0.4; cursor: default; }
.lib-card.lpk-toolong .lib-thumb { cursor: default; }

/* Super smooth locks its redundant companions (Reveal animations / Reduce
   motion): the switch dims + goes inert, and the row label softens so the
   lock reads as intentional, not broken. (Mark) */
.opt-switch.is-disabled { opacity: 0.35; pointer-events: none; }
.option-row.is-super-locked .option-label { opacity: 0.55; }



/* ── Home, direction B: two territories (Mark) ─────────────────────────
   The stills/video decision leads; rows not cards; PRO chips only on
   locked rows; Manual under a dashed rule at full strength; ghost
   Resume; library-style square recents; no-source hero. */
.home-cols { display: flex; flex-wrap: wrap; gap: 44px; align-items: stretch; margin-top: 26px; }
/* Borderless territories (Mark, 19 July): whitespace separates, not card
   edges — only the resume strip (a control) keeps a border. */
.home-col { flex: 1; min-width: 230px; }
.home-col .home-grp-lbl { margin: 0 0 10px 0; }
.home-cols .home-modes { display: block; }
.home-cols .home-mode-card.home-row {
  display: flex; flex-direction: row; align-items: center; justify-content: flex-start;
  gap: 12px; width: calc(100% + 8px); min-height: 0;
  padding: 10px 10px 10px 8px; margin: 0 0 2px -8px; border: 0; border-radius: 8px;
  background: transparent; text-align: left; cursor: pointer;
}
.home-cols .home-mode-card.home-row:hover { background: var(--ink08); transform: none; box-shadow: none; border: 0; }
/* Monochrome rows — the amber mode-icon accent stays on the old card look,
   not here (Mark's screenshot: orange icons, centred stack = base card CSS
   bleeding through). */
.home-cols .home-row .home-mode-ico { width: 18px; height: 18px; flex: none; display: inline-flex; margin: 0; color: var(--ink); }
.home-cols .home-row .home-mode-ico svg { width: 17px; height: 17px; }
.home-cols .home-row .home-row-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.home-cols .home-row .home-mode-name { font-size: 13.5px; }
.home-cols .home-row .home-mode-desc { font-size: 11.5px; color: var(--ink50); margin: 0; }
.home-pro {
  margin-left: auto; flex: none; font-family: var(--mono); font-size: 9px;
  letter-spacing: 0.12em; color: var(--ink50); border: 1px solid var(--border);
  border-radius: 100px; padding: 2px 7px;
}
/* Manual separation removed (Mark: 'why is manual separated. maybe dont') — class kept in markup, rule neutralised. */
.home-row-byhand { }
/* (Resume cue styled once, above — quiet label to match the Layouts "Open".) */
/* Recents: library treatment — square, whole capture fitted, tall = fade. */
.home-rshot { aspect-ratio: 1 / 1; height: auto; display: flex; align-items: center; justify-content: center; background: var(--bg2); position: relative; overflow: hidden; }
.home-rshot img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.home-rshot.is-tall img { width: 100%; height: 100%; max-width: none; max-height: none; object-fit: cover; object-position: top; }
.home-rshot .home-rshot-fade { position: absolute; left: 0; right: 0; bottom: 0; height: 38%; pointer-events: none; background: linear-gradient(to bottom, rgba(0,0,0,0), var(--bg2)); }
/* ── First-run V1 (Molly's board, translated to brand): a soft light BLOOM
   opens the dim around the target — a feathered radial mask, no rings —
   with a grounded EDITORIAL CARD: paper, mono step numeral, Söhne title,
   one quiet line, honey dot. Literal colours/stacks (these nodes append
   to <body>, outside the .app var scope). ── */
.capt-bloom-dim {
  position: fixed; inset: 0; z-index: 4000; pointer-events: none;
  background: rgba(14, 14, 12, 0.58);
  opacity: 0; transition: opacity .35s ease;
}
.capt-bloom-dim.is-on { opacity: 1; }
.capt-bloom-glow {
  position: fixed; z-index: 4000; pointer-events: none; transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 253, 245, 0.30) 0%, rgba(255, 253, 245, 0.10) 42%, transparent 68%);
  opacity: 0; transition: opacity .5s ease;
}
.capt-bloom-glow.is-on { opacity: 1; }
.capt-intro-card {
  position: fixed; z-index: 4001; pointer-events: none;
  background: #FAFAF8; border-radius: 10px;
  padding: 15px 18px 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35), 0 2px 10px rgba(0, 0, 0, 0.12);
  opacity: 0; transform: translateY(6px);
  transition: opacity .35s ease .12s, transform .35s ease .12s;
}
.capt-intro-card.is-on { opacity: 1; transform: translateY(0); }
.cic-num { font-family: "Söhne Mono", ui-monospace, Menlo, monospace; font-size: 9.5px; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(14, 14, 12, 0.38); margin-bottom: 7px; }
.cic-title { font-family: "Söhne", -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 15px; font-weight: 500; letter-spacing: -0.01em; color: #0E0E0C; }
.cic-sub { font-family: "Söhne", -apple-system, "Helvetica Neue", Arial, sans-serif; font-size: 12px; line-height: 1.45; color: rgba(14, 14, 12, 0.52); margin-top: 5px; padding-right: 12px; }
.cic-dot { position: absolute; right: 13px; bottom: 13px; width: 6px; height: 6px; border-radius: 50%; background: #F7CE46; }
.intro-press { transform: scale(0.94); transition: transform .12s ease; }
/* (First-capture card glow uses the hole-punch spotlight — one system.) */
/* First-run sidebar: no captures yet. */
.cs-empty { font-size: 11.5px; line-height: 1.5; color: var(--ink50); padding: 2px 0 8px; }
.caps-expand[hidden] { display: none !important; }

/* ── Home, three territories (Molly's design, 19 July 2026) ─────────────
   Rehearse whisper on the Video label; Layouts as a peer column with two
   rows + a stacked-boards vignette; the first-run hero. No Pro banner. */
.home-whisper {
  margin-left: 8px; font-family: var(--sans); font-size: 10.5px;
  text-transform: none; letter-spacing: 0.01em; color: var(--ink32);
}
.home-whisper[hidden] { display: none !important; }

.hla-m > span { background: var(--ink08); border-radius: 3px; min-height: 0; }

/* First-run hero */
.home-hero[hidden] { display: none !important; }
.home-hero { margin-top: 6px; }
/* Steps 1 + 2 wear a CARD (Mark, 23 July 2026): the free-floating hero ran
   straight into the mode grids below — the box separates the intro story
   from the working surface. Solid grey fill, no border (Mark, 24 July). */
.home-hero-in {
  position: relative; display: flex; align-items: center; gap: 30px;
  padding: 20px 26px 22px;
  background: var(--ink08); border: 0; border-radius: 14px;
}
.hh-txt { flex: 1; min-width: 0; }
.hh-title {
  font-family: var(--serif); font-size: 29px; line-height: 1.12;
  letter-spacing: -0.01em; font-weight: 500; color: var(--ink); margin: 2px 0 8px;
}
.hh-sub { font-size: 13px; line-height: 1.5; color: var(--ink50); margin: 0 0 7px; max-width: 400px; }
.hh-src { font-size: 12px; color: var(--ink50); margin: 0 0 16px; }
.hh-src b { font-weight: 500; color: var(--ink65); }
.hh-actions { display: flex; align-items: center; gap: 16px; }
.hh-go { padding: 10px 20px; font-size: 13px; }
.hh-x { position: absolute; top: 10px; right: 10px; }
.hh-demo {
  display: inline-flex; align-items: center; gap: 8px; background: none; border: 0;
  padding: 0; cursor: pointer; font-family: var(--sans); font-size: 12.5px; color: var(--ink65);
}
.hh-demo:hover { color: var(--ink); }
.hh-demo-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--bmed); font-size: 8px;
}
/* Illustration: mini browser + the mode popover — teaches the capture model
   before any click, with the REAL mode glyphs. Static, decorative. */
.hh-art { flex: none; position: relative; width: 320px; height: 188px; }
.hh-browser {
  position: absolute; left: 0; top: 10px; width: 246px; height: 168px;
  background: var(--paper); border: 1px solid var(--bmed); border-radius: 10px;
  overflow: hidden; box-shadow: 0 14px 30px rgba(20, 20, 16, 0.07);
}
.hh-bdots { display: flex; gap: 4px; align-items: center; padding: 9px 11px 7px; }
.hh-bdots span { width: 6px; height: 6px; border-radius: 50%; background: var(--ink08); }
.hh-bdots .hh-d1 { background: #F7CE46; }
.hh-bdots i { flex: 1; height: 6px; margin-left: 8px; border-radius: 3px; background: var(--ink08); max-width: 90px; }
.hh-bbody { padding: 8px 16px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.hh-bt { grid-column: 1 / -1; height: 9px; width: 56%; margin: 2px auto 5px; border-radius: 3px; background: var(--ink08); }
.hh-bimg { height: 62px; border-radius: 4px; background: var(--ink08); }
.hh-bl { grid-column: 1 / -1; height: 5px; border-radius: 2.5px; background: var(--ink08); }
.hh-bl.hh-short { width: 68%; }
.hh-pop {
  position: absolute; right: 0; top: 26px; width: 158px;
  background: var(--paper); border: 1px solid var(--border); border-radius: 10px;
  padding: 5px; box-shadow: 0 16px 34px rgba(20, 20, 16, 0.12);
  display: flex; flex-direction: column;
}
.hh-pop-row { display: flex; align-items: center; gap: 9px; padding: 8px 9px; border-radius: 8px; }
.hh-pop-row:first-child { background: var(--ink08); }
.hh-pop-ico {
  width: 26px; height: 26px; flex: none; border-radius: 7px; background: var(--ink08);
  display: inline-flex; align-items: center; justify-content: center; color: var(--ink);
}
.hh-pop-ico svg { width: 14px; height: 14px; }
.hh-pop-t { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.hh-pop-t b { font-size: 11.5px; font-weight: 500; color: var(--ink); }
.hh-pop-t i { font-style: normal; font-size: 10px; color: var(--ink50); }
/* Step-2 variant (See it move): no popover — centred browser + play badge. */
.hh-art-play .hh-browser { left: 37px; }
.hh-play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--ink); color: var(--paper);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; padding-left: 3px;
  box-shadow: 0 10px 24px rgba(20, 20, 16, 0.25);
}
.app.dark .hh-play { color: var(--canvas); }
/* Narrow window: the art drops, the hero reads as a text card. */
@media (max-width: 860px) {
  .hh-art { display: none; }
}
/* No-source hero */
.home-choose[hidden] { display: none !important; }
.home-choose {
  display: block; margin: 2px auto 22px; border: 1px solid var(--ink);
  background: transparent; color: var(--ink); border-radius: 100px;
  padding: 12px 24px; font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.13em; text-transform: uppercase; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.home-choose:hover { background: var(--ink); color: var(--paper); }
/* Layouts column stays live when blocked — it composes from EXISTING captures. */
.home.is-blocked .home-col:not(.home-col-layouts) { opacity: 0.38; pointer-events: none; }

/* Top rhythm: with a source set the greeting is gone — don't keep its
   air (Mark: 'quite a lot of gap at the top'). */
.home-greeting[hidden] { display: none !important; }

/* ── Visual Editor: blank no-source stage ─────────────────────────────
   Classic never surfaces (Mark): with no source picked the VE still opens,
   and the stage shows a device-shaped blank page with a centred prompt.
   Lives inside the scaled #tour-preview-scaler, so sizes are device-CSS px
   (they read ~half size on screen at typical fit). */
.ve-nosource {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--paper);
}
.ve-nosource-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
  transform: translateY(-4%); /* optical centre, slightly high */
}
.ve-nosource-title {
  font-size: 30px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.ve-nosource-sub {
  font-size: 19px;
  color: var(--ink-mute);
  max-width: 30em;
}
.ve-nosource-btn {
  margin-top: 18px;
  font-size: 18px;
  padding: 14px 30px;
}

/* The hidden #saveSettings ghost button is kept in the DOM only so wired
   event handlers resolve — but .ghost-btn's display:inline-flex BEATS the
   UA's [hidden]{display:none} (author styles always win over UA styles), so
   it rendered as an empty 18px strip across the bottom of some panels
   (Mark's DevTools screenshot). Never visible, ever. */
#saveSettings { display: none !important; }

/* Layouts toolbar toggle (Overlap on Phone + desktop) — the app's ghost-btn
   look (sans, sentence case; mono-uppercase is reserved for eyebrows), sized
   down to the toolbar's height. Ink-filled when on. */
.layouts-toolbar .lt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.01em;
  background: var(--btn2bg);
  color: var(--ink65);
  border: 0;
  border-radius: 8px;
  padding: 5px 12px;
  cursor: pointer;
}
.layouts-toolbar .lt-btn:hover { color: var(--ink); background: var(--btn2bg-hover); }
.layouts-toolbar .lt-btn.is-on { background: var(--ink); color: var(--paper); }
.layouts-toolbar .lt-btn[disabled] { opacity: .45; cursor: default; }
.layouts-toolbar .lt-btn[disabled]:hover { color: var(--ink65); background: var(--btn2bg); }

/* ONE Animate button everywhere (Mark, 16 July 2026): multi-motion layouts get
   a chevron + this dropdown of motions + Off, anchored under the button. */
.layouts-toolbar .lt-anim-wrap { position: relative; display: inline-flex; }
.lt-anim-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  min-width: 122px; background: var(--paper); border: 1px solid var(--rule-faint);
  border-radius: 10px; padding: 5px; box-shadow: 0 10px 28px rgba(14, 14, 12, .16);
  display: flex; flex-direction: column; gap: 1px; }
.lt-anim-menu[hidden] { display: none; }
.lt-anim-item { display: flex; align-items: center; width: 100%; text-align: left;
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.01em; color: var(--ink65);
  background: none; border: 0; border-radius: 7px; padding: 6px 10px; cursor: pointer; }
.lt-anim-item:hover { background: var(--btn2bg); color: var(--ink); }
.lt-anim-item.is-on { background: var(--ink); color: var(--paper); }
.lt-anim-item[disabled] { opacity: .38; cursor: default; }
.lt-anim-item[disabled]:hover { background: none; color: var(--ink65); }

/* ── Page Capture: Range card (Mark's pick — mockup 01 hybrid) ──────────
   The range is the panel's one composite object: Start / End / ratio chips
   live inside a bordered card and the resolved span is the card's FOOTER
   strip — part of the object, not floating text. Everything after stays
   ruled (.panel-sect), so the panel keeps the app's flat grammar. */
/* ══ RANGE RAIL — the summary ════════════════════════════════════════════════
   The bordered Range card and its grey footer strip are gone (Mark, 11 Aug
   2026: "remove the rounded boxes… out grey box, maybe make mono font as it is
   meta"). What is left is metadata, so it wears the numerals face and sits as
   plain text: what the range is, then how big it is, on two lines. */
.page-range-summary {
  display: block;
  margin: 14px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.65;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
}
.page-range-summary-label { display: none; }   /* the section names it already */
.page-range-summary-value { display: block; }
.page-range-summary-size { display: block; color: var(--ink-mute); }
#canvas-settings .canvas-flat .page-range-summary { display: block; margin: 13px 0 0; }
#canvas-settings .canvas-flat .page-range-summary-label { display: none; }

/* Section heading — sans, same voice as the canvas accordion bars
   (Mark, 19 July: mono caps here fought Styling / Hide Elements). */
.panel-sect {
  margin: 20px 0 10px;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--ink);
}

/* Mode seg stays IN the control cell (Mark: same position as every row) — but
   it is sized by its own two words, NOT by the cell (Mark, 7 Aug 2026: "the
   page clips selector is way too long"). `.tour-field-control` is flex:1 with a
   260px floor, so `width: 100%` + `flex: 1` on the pills stretched "Static" and
   "Scroll" across the whole card — a 500px control for eleven characters, and
   the only seg in the panel that did it. The Speed row beside it was already
   content-sized, which is what made this one read as broken. */
[data-clip-mode].pill-select.seg { display: inline-flex; width: fit-content; }
[data-clip-mode].pill-select.seg .pill { white-space: nowrap; text-align: center; justify-content: center; }


/* ======== Unified Guide (guide-overview) — mockup structure, app palette ======== */
.guide2-panel { position: relative; max-width: 1120px; margin: 0 auto; padding-bottom: 60px; }
/* Guide + Settings are self-contained pages — no sidebar rail. Hide it, collapse
   the grid to one column, and move .main into it (it's pinned to column 2, so
   without this it sits in a phantom right column — Mark: "all over the right"). */
.app:has(.sidebar-group[data-group="guide"].is-active) .sidebar-col,
.app:has(.sidebar-group[data-group="settings"].is-active) .sidebar-col { display: none; }
.app:has(.sidebar-group[data-group="guide"].is-active) .workspace.no-preview,
.app:has(.sidebar-group[data-group="settings"].is-active) .workspace.no-preview { grid-template-columns: minmax(0, 1fr); }
.app:has(.sidebar-group[data-group="guide"].is-active) .workspace > .main,
.app:has(.sidebar-group[data-group="settings"].is-active) .workspace > .main { grid-column: 1; }

/* Settings: two columns — capture defaults left, Licence (account) right. */
.settings-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; align-items: start; margin-top: 8px; }
.settings-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.settings-col .settings-section { margin-bottom: 0; }
@media (max-width: 820px) { .settings-cols { grid-template-columns: minmax(0, 1fr); } }
.g2-hero { max-width: 640px; margin: 2px 0 40px; }
.g2-eyebrow { font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em; text-transform: none; color: var(--ink50); margin: 0 0 12px; font-weight: 500;}
.g2-title { font-family: var(--sans); font-weight: 500; font-size: 30px; line-height: 1.12; letter-spacing: -.015em; color: var(--ink); margin: 0 0 14px; }
.g2-lede { font-size: 15.5px; line-height: 1.55; color: var(--ink50); margin: 0 0 22px; max-width: 540px; }
.g2-search { position: relative; display: flex; align-items: center; max-width: 380px; color: var(--ink50); }
.g2-search svg { position: absolute; left: 12px; pointer-events: none; }
.g2-search input { width: 100%; height: 38px; padding: 0 12px 0 34px; background: var(--paper); border: 1px solid var(--bmed); border-radius: 9px; font-family: inherit; font-size: 13.5px; color: var(--ink); outline: none; }
.g2-search input:focus { border-color: var(--bstr); }
.g2-start { display: flex; align-items: center; flex-wrap: wrap; gap: 16px; margin-top: 20px; }
.g2-start-lbl { font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink50); }
.g2-chip { font-family: inherit; padding: 0 0 1px; background: transparent; border: 0; border-bottom: 1px solid var(--bmed); font-size: 13.5px; color: var(--ink); cursor: pointer; text-decoration: none; }
.g2-chip:hover { border-color: var(--ink); }
.g2-gallery { display: flex; flex-direction: column; gap: 40px; }
.g2-group-title { font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em; text-transform: none; color: var(--ink50); margin: 0 0 14px; font-weight: 500;}
.g2-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: 14px; }
.g2-card { display: flex; flex-direction: column; text-align: left; background: var(--paper); border: 1px solid var(--ink08); border-radius: 10px; padding: 0; overflow: hidden; cursor: pointer; transition: border-color .12s ease, transform .12s ease; }
.g2-card:hover { border-color: var(--bstr); transform: translateY(-1px); }
/* SQUARE (Mark, 9 Aug 2026: "we need to make the guide image sections square
   so we can fit in long pages as well as wide desktop images. they are very
   narrow and not useful at all"). 96px of a full-width card is a letterbox: a
   page capture showed a sliver of its header and a desktop shot lost its top
   and bottom. A square is the one shape that gives BOTH something to be — a
   tall page shows its opening screen, a wide one shows its full width. */
.g2-card-thumb { position: relative; display: block; aspect-ratio: 1 / 1; height: auto; padding: 16px 16px 0; background: var(--pill); border-bottom: 1px solid var(--ink08); overflow: hidden; }
/* …but the DRAWN placeholder is a wireframe of a page, not a photo, and it was
   composed for a letterbox. It keeps its own height at the top of the square so
   it does not stretch into a caricature of itself. */
.g2-card-thumb:not(.is-shot) > .g2-bar,
.g2-card-thumb:not(.is-shot) > .g2-thumb-area { max-height: 96px; }
.g2-bar { display: block; height: 6px; border-radius: 3px; background: var(--bmed); margin-bottom: 7px; }
.g2-bar-lite { background: var(--ink08); }
.g2-thumb-area { position: absolute; top: 30px; left: 16px; width: 58%; height: 42px; border: 1.5px dashed var(--ink); opacity: .45; border-radius: 4px; }
.g2-card-meta { display: flex; flex-direction: column; gap: 3px; padding: 12px 15px 15px; }
.g2-card-name { font-size: 14.5px; font-weight: 500; color: var(--ink); }
.g2-card-line { font-size: 12.5px; color: var(--ink50); line-height: 1.4; }
.g2-card-ref { min-height: 92px; justify-content: center; }
.g2-card-ref .g2-card-meta { padding: 18px; }
.g2-empty { text-align: center; padding: 60px 0; }
.g2-empty-h { font-size: 15px; color: var(--ink); margin: 0 0 4px; }
.g2-empty-s { font-size: 13px; color: var(--ink50); margin: 0; }
.g2-empty-s a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--bmed); }
.g2-empty-s a:hover { border-color: var(--ink); }
/* Support foot — mirrors the hero's "Getting started" row (mono label +
   chip), under a hairline at the page's end. The mono address is a
   click-to-copy button, quiet until hovered. */
.g2-support { display: flex; align-items: baseline; flex-wrap: wrap; gap: 16px; margin-top: 56px; padding-top: 22px; border-top: 1px solid var(--ink08); }
.g2-support-line { font-size: 13.5px; color: var(--ink50); margin: 0; }
.g2-support-addr { font-family: var(--mono); font-size: 12.5px; letter-spacing: .02em; color: var(--ink50); background: transparent; border: 0; padding: 0; cursor: pointer; }
.g2-support-addr:hover, .g2-support-addr.is-copied { color: var(--ink); }
/* slide-in detail */
.g2-detail { position: fixed; inset: 0; z-index: 2600; display: flex; align-items: center; justify-content: center; padding: 40px 24px; }
/* display:flex above would defeat the [hidden] attribute (UA display:none),
   leaving an invisible full-screen overlay that eats every click/scroll. */
.g2-detail[hidden] { display: none; }
.g2-detail-scrim { position: absolute; inset: 0; background: rgba(14,14,12,.34); opacity: 0; transition: opacity .2s ease; }
.g2-detail.is-open .g2-detail-scrim { opacity: 1; }
/* Centered modal (matches the mockup) — not a side sheet. */
.g2-detail-sheet { position: relative; width: min(560px, 100%); max-height: min(86vh, 720px); background: var(--bg); border: 1px solid var(--bmed); border-radius: 16px; box-shadow: 0 24px 70px rgba(0,0,0,.22); display: flex; flex-direction: column; overflow: hidden; opacity: 0; transform: translateY(8px) scale(.985); transition: opacity .2s ease, transform .22s cubic-bezier(.3,0,.15,1); }
.g2-detail.is-open .g2-detail-sheet { opacity: 1; transform: none; }
.g2-detail-head { flex: 0 0 auto; display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 22px 22px 16px; border-bottom: 1px solid var(--ink08); }
.g2-detail-eyebrow { font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em; text-transform: none; color: var(--ink50); font-weight: 500;}
.g2-detail-name { font-family: var(--sans); font-weight: 500; font-size: 20px; line-height: 1.2; letter-spacing: -.01em; color: var(--ink); margin: 4px 0 0; }
.g2-detail-close { flex: 0 0 auto; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; background: transparent; border: 1px solid transparent; border-radius: 8px; color: var(--ink50); cursor: pointer; }
.g2-detail-close:hover { background: var(--pill); border-color: var(--ink08); color: var(--ink); }
.g2-detail-body { flex: 1 1 auto; overflow-y: auto; padding: 22px; }
.g2-demo { aspect-ratio: 16/9; background: var(--pill); border: 1px solid var(--ink08); border-radius: 10px; padding: 18px 18px 0; margin-bottom: 22px; }
/* With real media in it the frame stops being a drawing of a browser: no inner
   plate, no padding. It keeps 16:9 rather than squaring up like the cards,
   because what goes in here is nearly always a RECORDING (Mark, 9 Aug 2026:
   "maybe inside the modal, it should be 16:9 as they will all probably be
   videos") — and a video in a square frame is a video with its sides cut off.
   Cover, not contain, for the same reason: at 16:9 in a 16:9 box there is
   nothing to crop, and a mat around a clip would be a mat around nothing. */
.g2-demo.is-shot { aspect-ratio: 16 / 9; padding: 0; background: var(--ink08); overflow: hidden; position: relative; }
.g2-demo.is-shot img, .g2-demo.is-shot video { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
/* The author's way in, on the media itself. Only drawn in author mode. */
.g2-demo-set {
  position: absolute; right: 10px; bottom: 10px; z-index: 2;
  font-family: var(--sans); font-size: 11.5px; padding: 6px 11px; border-radius: 100px;
  background: rgba(22, 21, 15, .78); color: #F7F5EF; border: 0; cursor: pointer;
}
.g2-demo-set:hover { background: rgba(22, 21, 15, .92); }
.g2-demo-frame { height: 100%; background: var(--paper); border: 1px solid var(--ink08); border-bottom: 0; border-radius: 7px 7px 0 0; padding: 16px; overflow: hidden; }
.g2-what { font-size: 14.5px; line-height: 1.6; color: var(--ink); margin: 0 0 4px; }
.g2-sect { margin-top: 22px; }
.g2-sect-title { font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em; text-transform: none; color: var(--ink50); margin: 0 0 12px; font-weight: 500;}
.g2-notes, .g2-fixes { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 12px; }
.g2-notes li, .g2-fixes li { position: relative; font-size: 13.5px; line-height: 1.55; color: var(--ink); padding-left: 20px; }
.g2-notes li::before { content: ''; position: absolute; left: 3px; top: 8px; width: 5px; height: 5px; border-radius: 50%; background: var(--bstr); }
.g2-fixes { counter-reset: g2f; }
.g2-fixes li { counter-increment: g2f; }
.g2-fixes li::before { content: counter(g2f); position: absolute; left: 2px; top: 0; font-family: var(--mono); font-size: 11px; color: var(--ink50); }

/* Look-aware frosted glass + tone-step greys (Mark: no cool grey bits in the
   warm/soft looks). The frosted washes were hardcoded to cool paper
   rgba(250,250,250) and the Site Flow connector to #E0E0E0; tint them to the
   active look's paper family. Dark mode has its own values already. */
/* :not(.is-error) — these look tints out-specify the error rule below them
   (4 classes + id vs 3), so on Warm/Mix an ERROR toast was getting the
   light paper wash under its pale error text — cream on white, unreadable
   (Mark: "Recording failed" pill after closing the window mid-take). The
   error card keeps its designed madder-red frosted surface in every look. */
.app.warm:not(.dark).shell-modetop #canvas-status-toast:not(.is-error),
.app.warm:not(.dark) #stage-side-msgs.is-over-canvas > *,
.app.warm:not(.dark).shell-modetop .tour-preview-banner,
.app.warm:not(.dark).shell-modetop.canvas-mode #tour-preview-hint {
  background: rgba(246, 246, 244, 0.74);
}
.app.soft:not(.dark).shell-modetop #canvas-status-toast:not(.is-error),
.app.soft:not(.dark) #stage-side-msgs.is-over-canvas > *,
.app.soft:not(.dark).shell-modetop .tour-preview-banner,
.app.soft:not(.dark).shell-modetop.canvas-mode #tour-preview-hint {
  background: rgba(248, 248, 244, 0.74);
}
.app.warm:not(.dark) #tour-multi-pages .tour-flow-connector-pill, .app.warm:not(.dark) #manrec-markers .tour-flow-connector-pill { background: #E0E0DB; }
.app.soft:not(.dark) #tour-multi-pages .tour-flow-connector-pill, .app.soft:not(.dark) #manrec-markers .tour-flow-connector-pill { background: #E2E2DE; }

/* ── Style set: batch styling + export. App-shaped: ONE large paged preview
   on the LEFT (the canvas), the sidebar's own option rows on the RIGHT.
   The window is FIXED-height so opening accordions scrolls the sidebar and
   never resizes the frame. House components throughout. ────────────────────── */
.styleset-overlay { position: fixed; inset: 0; z-index: 9998; background: rgba(14, 14, 12, .45); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; padding: 28px; }
/* capt-modal-card caps at 460px for confirm dialogs — this surface is a
   workspace, so lift the cap and own the size outright. */
.styleset-modal { width: min(1240px, calc(100vw - 64px)); max-width: none; height: min(780px, calc(100vh - 56px)); max-height: none; overflow: hidden; display: flex; flex-direction: column; border-radius: 10px; }
.styleset-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; flex: none; }
.styleset-title { margin: 0; font-family: var(--sans); font-size: 14px; font-weight: 500; letter-spacing: -.005em; color: var(--ink); }
.styleset-sub { font-size: 12px; color: var(--ink50); }
.styleset-x { margin-left: auto; background: none; border: none; cursor: pointer; color: var(--ink50); font-size: 16px; line-height: 1; padding: 4px 8px; }
.styleset-x:hover { color: var(--ink); }
.styleset-body { display: flex; gap: 28px; flex: 1; min-height: 0; }
.styleset-canvas { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 10px; }
/* Bare canvas (Mark, 16 Aug round 8: 'Remove border and box shadow on the canvas') — same direction as Review's bare plates. */
.styleset-stage { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; background: var(--canvas, #f0efec); overflow: hidden; padding: 20px; }
.styleset-stage canvas { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; }
.styleset-pager { display: flex; align-items: center; gap: 10px; flex: none; min-width: 0; }
/* The one pager button in the product (#307). Style set the shape and the
   review screen now wears it — listed together so a change to one cannot leave
   the other behind, which is exactly what had happened. */
.styleset-pgbtn,
.iso-caps-arrow { width: 26px; height: 26px; border-radius: 8px; border: 1px solid var(--ink08); background: none; color: var(--ink); cursor: pointer; font-size: 15px; line-height: 1; display: flex; align-items: center; justify-content: center; flex: none; }
.styleset-pgbtn:hover { border-color: var(--ink32); }
.styleset-pgtxt { font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em; color: var(--ink50); flex: none; }
.styleset-pgname { font-size: 11.5px; color: var(--ink50); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; margin-left: 4px; }
.styleset-side { width: 308px; flex: none; overflow: auto; padding: 0 4px; }
/* Backdrop picker now borrows the layouts library picker (.layouts-picker). */
/* The sidebar's compact pill size (the layouts panel uses the same). */
.styleset-side .pill-select .pill,
[data-iso-rail] .pill-select .pill { font-size: 11px; padding: 5px 12px; white-space: nowrap; }
.styleset-side .option-row:first-child .option-summary { padding-top: 6px; }
/* Style-kit bar — a saved house style, applied to this capture / set. */
/* Style + Export kit picker — now a standard collapsible .option-row like the
   other options (Mark); the picker + buttons live in its .option-expand. */
.styleset-kits:empty { display: none; }
.styleset-kits .styleset-kit-sel { width: 100%; }
.styleset-kit-row { display: flex; align-items: center; gap: 10px; }
.styleset-kit-row .option-label { flex: none; }
.styleset-kit-sel { flex: 1; min-width: 0; font: inherit; font-size: 12px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--paper, #fff); color: var(--ink); }
.styleset-kit-btns { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.styleset-kit-btns .ghost-btn { font-size: 12px; padding: 6px 12px; }
.styleset-kit-btns .ghost-btn:disabled { opacity: .45; cursor: default; }
.styleset-kit-del, .styleset-kit-btns [data-ss-kit-delyes] { color: #b23; }
.styleset-kit-name { flex: 1; min-width: 0; font: inherit; font-size: 12px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--paper, #fff); color: var(--ink); }
.styleset-kit-confirm-q { font-size: 12px; color: var(--muted); flex: 1; min-width: 0; }
/* Pre-capture style kit — rendered as a standard collapsible .option-row so it
   sits neatly among the sibling styling options (Background, Frame, Shadow…).
   The container is transparent; the picker + buttons live in the .option-expand. */
.capture-kits:empty { display: none; }
.capture-kits .styleset-kit-sel { width: 100%; } /* look inherits the shared custom-select styling */
.capture-kits .styleset-kit-btns { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.capture-kits .styleset-kit-btns .ghost-btn { font-size: 12px; padding: 6px 12px; }
.capture-kits .styleset-kit-btns .ghost-btn:disabled { opacity: .45; cursor: default; }
.capture-kits .styleset-kit-del { color: #b23; }
.capture-kits .styleset-kit-name { width: 100%; font: inherit; font-size: 12px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--paper, #fff); color: var(--ink); margin-bottom: 8px; }
.capture-kits .styleset-kit-confirm-q { font-size: 12px; color: var(--muted); flex: 1 1 100%; min-width: 0; margin-bottom: 6px; }
/* Backdrop framing box — drag-to-reposition preview of the chosen capture
   with a zoom slider. Shown in the Layouts panel + Style + Export. (Molly) */
.bg-framer-box { position: relative; width: 100%; aspect-ratio: 3 / 2; border-radius: 10px; overflow: hidden; background: #ecebe7; cursor: grab; touch-action: none; user-select: none; border: 1px solid var(--border); }
.bg-framer-box.is-dragging { cursor: grabbing; }
.bg-framer-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; -webkit-user-drag: none; }
.bg-framer-scrim { position: absolute; inset: 0; pointer-events: none; }
.bg-framer-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 12px; color: var(--muted); }
.bg-framer-hint { position: absolute; left: 0; right: 0; bottom: 0; padding: 5px 8px; font-size: 11px; font-weight: 600; letter-spacing: .01em; color: #fff; text-align: center; background: linear-gradient(to top, rgba(0,0,0,.42), rgba(0,0,0,0)); pointer-events: none; opacity: .9; }
.bg-framer-box.is-dragging .bg-framer-hint { opacity: 0; }
.styleset-foot { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex: none; }
.styleset-note { font-size: 11.5px; color: var(--ink50); line-height: 1.4; }
.styleset-foot .capt-modal-btn { margin-left: auto; flex: none; }
/* Save to library sits WITH the export, right-grouped — the note keeps the
   left. (Two auto margins would otherwise split the gap between them.) */
.styleset-foot [data-ss-save] { margin-left: auto; flex: none; white-space: nowrap; }
.styleset-foot [data-ss-save] ~ .capt-modal-btn { margin-left: 0; }

/* ── On-brand dropdowns (toolbar device/Length, library filters, picker) ──────
   Kill the native arrow and draw Loey's ink chevron; where Chrome supports
   customisable selects, the OPEN LIST becomes a paper card too — mono, ink,
   soft radius — instead of the OS default. Degrades gracefully. */
.layouts-toolbar .lt-dev-sel, .history-select, .layouts-pk-site, .styleset-kit-sel {
  appearance: none;
  -webkit-appearance: none;
  /* Sans throughout, not mono (Mark) — and sized so the text sits centred. */
  font-family: var(--sans);
  font-size: 12px;
  line-height: 1.2;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' fill='none' stroke='%238a8a88' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'><path d='M2.5 4l2.5 2.5L7.5 4'/></svg>");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 9px 9px;
  padding-right: 26px;
}
@supports (appearance: base-select) {
  .layouts-toolbar .lt-dev-sel, .history-select, .layouts-pk-site, .styleset-kit-sel { appearance: base-select; }
  /* base-select brings its own picker triangle — we already draw the house
     chevron, and two arrows is one too many (Mark). */
  .layouts-toolbar .lt-dev-sel::picker-icon, .history-select::picker-icon, .layouts-pk-site::picker-icon, .styleset-kit-sel::picker-icon { display: none; }
  .layouts-toolbar .lt-dev-sel::picker(select), .history-select::picker(select), .layouts-pk-site::picker(select), .styleset-kit-sel::picker(select) {
    appearance: base-select;
    background: var(--bg, var(--paper, #fff));
    border: 1px solid var(--bmed);
    border-radius: 10px;
    box-shadow: 0 14px 44px rgba(14, 14, 12, 0.16);
    padding: 4px;
    margin-top: 4px;
  }
  .layouts-toolbar .lt-dev-sel, .history-select, .layouts-pk-site, .styleset-kit-sel { display: inline-flex; align-items: center; }
  .layouts-toolbar .lt-dev-sel option, .history-select option, .layouts-pk-site option, .styleset-kit-sel option {
    font-family: var(--sans);
    font-size: 12px;
    /* Options INHERIT the optgroup eyebrow's uppercase + tracking — undo it
       (the capture-mode names were rendering ALL CAPS; Mark). */
    text-transform: none;
    letter-spacing: 0;
    display: flex;
    align-items: center;
    color: var(--ink);
    padding: 6px 10px;
    border-radius: 6px;
    cursor: pointer;
  }
  .layouts-toolbar .lt-dev-sel option:hover, .history-select option:hover, .layouts-pk-site option:hover, .styleset-kit-sel option:hover {
    background: rgba(14, 14, 12, 0.06);
  }
  .layouts-toolbar .lt-dev-sel option:checked, .history-select option:checked, .layouts-pk-site option:checked, .styleset-kit-sel option:checked {
    font-weight: 500;
  }
  .history-select optgroup, .layouts-pk-site optgroup {
    font-family: var(--mono);
    font-size: 9.5px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-mute, #8a8a88);
    padding: 6px 10px 2px;
  }
}

/* Library device filter: the SAME icons and selector shape as the modes'
   topbar device control (Mark) — one connected segment, icon buttons. */
.lib-seg-btn.lib-dev-btn { display: inline-flex; align-items: center; justify-content: center; padding: 4px 10px; }
.lib-seg-btn.lib-dev-btn svg { display: block; }
.lib-seg-btn.lib-dev-btn.is-empty { opacity: .32; pointer-events: none; }
/* Loey — Repair splice tool. Appended to styles.css. Mounts inside .app so it
   inherits the design tokens; mirrors the cloud-lightbox shell. Monochrome +
   the one honey accent (#F7CE46, tracks the app accent). */
.repair-overlay{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;padding:28px;font-family:var(--sans)}
.repair-scrim{position:absolute;inset:0;background:rgba(14,14,12,.55);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
.repair-card{position:relative;display:flex;flex-direction:column;width:min(1000px,94vw);height:min(880px,92vh);
  background:var(--bg,var(--paper));border:1px solid var(--border);border-radius:10px;overflow:hidden;box-shadow:0 18px 60px rgba(0,0,0,.35)}
.repair-bar{display:flex;align-items:center;gap:12px;padding:10px 14px;border-bottom:1px solid var(--border);flex:none}
.repair-title{font-family:var(--sans);font-size:13px;font-weight:500;letter-spacing:-.01em;color:var(--ink)}
.repair-sub{font-size:11.5px;color:var(--ink50)}
.repair-spacer{flex:1}
.repair-save{flex:none;white-space:nowrap}
.repair-x{flex:none;border:0;background:none;cursor:pointer;color:var(--ink50);font-size:20px;line-height:1;padding:2px 6px}
.repair-x:hover{color:var(--ink)}
.repair-explain{padding:12px 16px;background:var(--acc-band);border-bottom:1px solid var(--border);flex:none}
.repair-explain-main{font-size:13px;line-height:1.5;color:var(--ink-soft)}
.repair-explain-main b{font-weight:500;color:var(--ink)}
.repair-explain-tips{margin-top:7px;font-size:11.5px;line-height:1.5;color:var(--ink-mute);display:flex;flex-wrap:wrap;gap:2px 4px}
.repair-explain-tips b{font-weight:500;color:var(--ink-soft)}
.repair-body{flex:1;display:flex;gap:22px;padding:18px;overflow:hidden;align-items:stretch;min-height:0}
.repair-srcs{display:flex;gap:18px;min-height:0;align-items:stretch}
.repair-col{display:flex;flex-direction:column;align-items:center;gap:8px;min-height:0}
.repair-col-h{font-family:var(--sans);font-size:13px;font-weight:500;letter-spacing:-.01em;text-transform:none;color:var(--ink);display:flex;gap:8px;align-items:baseline}
.repair-col-h b{font-weight:500;color:var(--ink)}
/* header doubles as a drag-start zone once its capture is loaded */
.repair-col:has(.repair-src:not(.is-empty)) .repair-col-h{cursor:crosshair;user-select:none;-webkit-user-select:none}
.repair-col-tag{letter-spacing:0;font-size:11px;font-weight:400;color:var(--ink-mute)}
.repair-col-change{margin-left:6px;border:0;background:transparent;cursor:pointer;font-family:var(--sans);font-size:11px;letter-spacing:0;text-transform:none;color:var(--ink-mute);text-decoration:underline;text-underline-offset:2px;padding:2px 4px}
.repair-col-change:hover{color:var(--ink)}
.repair-col-change[hidden]{display:none}
/* source capture = scrollable viewport (pages are long) */
.repair-src{position:relative;width:300px;flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;
  border-radius:0;background:var(--card-fill);border:1px solid var(--border);cursor:crosshair;user-select:none;touch-action:pan-y}
.repair-src.is-empty{display:flex;align-items:center;justify-content:center;width:300px;flex:1;min-height:0;cursor:default;border-style:dashed;padding:16px}
.repair-src-empty{font-size:12px;color:var(--ink-mute);text-align:center;padding:20px}
/* empty-slot options — dashed pills, matching the app's + Capture / + Library */
.repair-slot-empty{display:flex;flex-direction:column;align-items:stretch;gap:10px;width:100%;max-width:230px;text-align:center}
.repair-slot-hint{font-size:12px;line-height:1.45;color:var(--ink-mute);margin-bottom:4px}
.repair-slot-hint b{color:var(--ink-soft);font-weight:400}
.repair-slot-add{justify-content:center}
.repair-slot-status{font-size:11.5px;color:var(--ink-mute);min-height:14px}
/* thin, unobtrusive scrollbars so they don't sit under the drag handles (Mark) */
.repair-src,.repair-preview{scrollbar-width:thin;scrollbar-color:rgba(14,14,12,.28) transparent}
.repair-src::-webkit-scrollbar,.repair-preview::-webkit-scrollbar{width:8px}
.repair-src::-webkit-scrollbar-thumb,.repair-preview::-webkit-scrollbar-thumb{background:rgba(14,14,12,.26);border-radius:8px;border:2px solid transparent;background-clip:padding-box}
.repair-src::-webkit-scrollbar-track,.repair-preview::-webkit-scrollbar-track{background:transparent}
.repair-src-inner{position:relative}
.repair-src-inner canvas{display:block}
/* a taken range on a source, with draggable ⇕ edge handles */
.repair-sov{position:absolute;left:0;right:0;pointer-events:none;background:rgba(247,206,70,.14);
  box-shadow:inset 0 0 0 9999px rgba(247,206,70,.10);border-top:1.5px solid var(--ink);border-bottom:1.5px solid var(--ink)}
.repair-sov.live{background:rgba(247,206,70,.22);border-top:2px dashed var(--ink);border-bottom:2px dashed var(--ink)}
/* Level guide on the OTHER capture during a drag (Mark, 25 July 2026):
   dashed while free, solid amber when the edge has snapped level. */
.repair-level{position:absolute;left:0;right:0;height:0;border-top:1.5px dashed rgba(247,206,70,.85);z-index:4;pointer-events:none}
.repair-level.is-snap{border-top:2px solid #F7CE46;box-shadow:0 0 0 1px rgba(14,14,12,.25)}
.repair-sov-tag{position:absolute;top:2px;left:4px;font-family:var(--mono);font-size:9px;letter-spacing:.06em;color:var(--paper);background:var(--ink);padding:1px 5px;border-radius:3px;z-index:3}
.repair-sov-del{position:absolute;top:3px;left:50%;transform:translateX(-50%);z-index:5;pointer-events:auto;cursor:pointer;border:0;border-radius:3px;
  background:rgba(14,14,12,.62);color:var(--paper);font-size:10px;line-height:1;padding:3px 5px}
.repair-sov-del:hover{background:var(--ink)}
.repair-sov-h{position:absolute;left:0;right:0;height:18px;cursor:ns-resize;pointer-events:auto;z-index:2}
.repair-sov-h.top{top:-9px} .repair-sov-h.bot{bottom:-9px}
.repair-sov-h::before{content:'';position:absolute;left:0;right:0;top:50%;height:0;border-top:2px solid var(--ink)}
.repair-sov-grip{position:absolute;right:15px;top:50%;transform:translateY(-50%);display:flex;align-items:center;justify-content:center;color:var(--ink);background:var(--rep-accent,#F7CE46);border:1px solid var(--ink);border-radius:4px;padding:2px 5px;pointer-events:none}
.repair-sov-grip svg{display:block}
.repair-preview-wrap{display:flex;flex-direction:column;gap:8px;min-height:0}
.repair-preview{border-radius:0;overflow-y:auto;overflow-x:hidden;background:var(--card-fill);border:1px solid var(--border);position:relative;width:300px;flex:1;min-height:0}
.repair-preview canvas{display:block;touch-action:none}
.repair-pv-empty{width:300px;min-height:300px;display:flex;align-items:center;justify-content:center;text-align:center;color:var(--ink-mute);font-size:12px;padding:24px}
.repair-mag{position:fixed;z-index:10000;width:356px;background:var(--bg,var(--paper));border:1px solid var(--border);border-radius:10px;
  box-shadow:0 18px 60px rgba(0,0,0,.35);padding:8px;display:none}
.repair-mag.on{display:block}
.repair-mc{width:340px;border-radius:6px;overflow:hidden;background:var(--ink);position:relative}
.repair-mc canvas{display:block;width:340px}
.repair-mlbl{font-family:var(--sans);font-size:11px;letter-spacing:0;text-transform:none;color:var(--ink-mute);margin-top:6px;display:flex;justify-content:space-between;gap:8px}
.repair-mlbl b{font-weight:400;color:var(--ink)}
/* + Library picker — borrows the Layouts library picker shell (.layouts-picker
   / .layouts-pk / .lib-grid / .lib-card) so it's identical to the app's other
   picker. Only the card fetch states are bespoke here. (Mark) */
#repair-lib-picker .lib-card{cursor:pointer}
#repair-lib-picker .lib-card.is-loading{opacity:.5;pointer-events:none}
#repair-lib-picker .lib-card.is-error{box-shadow:0 0 0 1px var(--ink)}
/* Reflow backdrop reuses the styling background swatch grid (.swatch-grid /
   .swatch / .swatch-popover) — no bespoke CSS needed. (Mark) */

/* Loey — Reel playground (experiment), its own Compose mode (stills-reel
   panel). Sized like the Layouts stage so the canvas gets the full mode
   area. Strip or promote after launch. */
.mode-menu-beta{margin-left:6px;font-size:9px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ink-mute);border:1px solid var(--border);border-radius:5px;padding:1px 5px}
/* 78px, not 122 (Mark, 3 Aug 2026: "use some of the gap at the bottom under the
   cards"). 78 is the chrome, measured and not guessed: 62px of topbar plus the
   main's own 10px top and 6px bottom padding. 122 left 44px of work area that
   nothing could reach — .reel-stage is flex:1 but it lives INSIDE this box, so
   the slack sat outside it and no child could grow into it. Every other mode
   centres a canvas in its stage, so the same 44px reads there as slightly wider
   margins and nobody notices; this is the one mode whose column ENDS in
   content, and a row of cards floating 54px above the bottom edge is the one
   place it shows. At 16:9 the canvas is limited by the column's width, so the
   reclaimed height goes to the margins; at 9:16 — the phone aspect this is
   mostly used at — the canvas is limited by HEIGHT and grows by the whole of
   it. Paired with .reel-side below: the two must move together. */
.reel-host{min-height:calc(100vh - 78px);max-height:calc(100vh - 78px);display:flex;flex-direction:column}
/* Integrated mode (Mark, 26 July 2026): no card shell — the stage fills the
   main column like the Layouts board; controls live in the right panel. */
.reel-panel{flex:1;display:flex;flex-direction:column;min-height:0}
/* right-panel controls */
/* 22px between sections, not 14 (Mark, 28 July 2026: "they are all a bit
   close"). 14 was barely more than the 6px a section puts between its own
   label and its control, so every label read as floating between two things
   instead of belonging to the one under it. 22 against ~6-11 inside a section
   is a clear enough ratio that the grouping does the work. */
.reel-side{display:flex;flex-direction:column;gap:0;padding:4px 2px}   /* #214 — .reel-sec carries its own air now */
/* ── Retired 3 Aug, Variant A ──────────────────────────────────────────────
   .reel-side-head, .reel-side-sec, .reel-side-row, .reel-row-2up, .reel-sec,
   .reel-sec-h, .reel-side-gap and .reel-actions2 were this panel's own private
   layout vocabulary — eight classes doing what .canvas-acc and .option-row
   already do everywhere else in the app, which is exactly why the editor read
   as a different product from the capture modes (Mark: "the sequence side bar
   is so messy and not styled like the layouts one"). They are gone rather than
   left orphaned: dead selectors are the reason a stylesheet stops being
   readable, and every one of them would have kept matching if anything ever
   emitted the class again by accident. */
.reel-side .pill-select.seg{align-self:flex-start}
.reel-side .reel-tot{margin-left:0;white-space:normal;max-width:none;line-height:1.6}
/* the engine's decision readout for the playing clip — was a dark pill over
   the canvas (Mark: move it) */
.reel-side-note{font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:10.5px;line-height:1.65;color:var(--ink-mute);
  border-top:1px solid var(--ink08);padding-top:10px}
.reel-side-note[hidden]{display:none}
/* saved reels — library cards (Library › Saved reels) + the panel's
   transient naming input */
.reel-bgrow{display:flex;gap:8px;align-items:center}
/* Backdrop colour is a .swatch disc now, not a native colour well. Its
   popover has to open to the RIGHT edge of the panel rather than the width of
   the one disc it hangs off, or the hex field runs past the panel. */
/* IN LINE with the Auto/Colour segment (Mark, 12 Aug 2026: "can we move the
   background colour picker for the sequence settings in line with the
   toggle"). It used to drop to its own line, which was the only way found at
   the time to give the popover something the width of the section to open
   against — `left:0; right:0` measured the 32px disc otherwise, and the hex
   field ran off the panel.
   The popover does not need to hang off the DISC to be that wide, though: it
   needs a positioned ancestor that is already section-width. So the row takes
   the `position: relative` and the grid gives it up. One control on one line,
   and a popover that still opens edge to edge.
   nowrap because a wrapped flex line is filled from hypothetical sizes — the
   same rule that broke the hide-elements rows on 11 Aug. */
.reel-bgrow{flex-wrap:nowrap}
.reel-bgsw{flex:0 0 auto;position:static}
.reel-side .reel-bgrow{position:relative}
.reel-side .swatch-grid{display:flex}
/* AFTER the display:flex above, and matching its specificity — written before
   it, the two tied on specificity and source order handed the win to flex, so
   the backdrop disc stayed on screen under Auto with nothing to do. That is
   the whole reason `hidden` is set on it in syncBgUI. */
.reel-side .swatch-grid[hidden]{display:none}
.reel-side .swatch-popover{left:0;right:0;min-width:0;padding:10px;gap:8px;z-index:12}
.reel-side .swatch-popover .swatch-hex{font-size:12px;padding:7px 9px}
.reel-side .swatch-popover .swatch-native{width:32px;height:32px}
.reels-card{position:relative}
.reels-del{position:absolute;top:6px;right:6px;z-index:2;border:0;border-radius:6px;background:rgba(14,14,12,.55);color:#fff;
  font-size:11px;width:22px;height:22px;cursor:pointer;opacity:0;transition:opacity .12s;line-height:1;padding:0}
.reels-move{position:absolute;top:6px;right:6px;z-index:2;border:0;border-radius:6px;background:rgba(14,14,12,.55);color:#fff;
  font-size:11px;width:22px;height:22px;cursor:pointer;opacity:0;transition:opacity .12s;line-height:1;padding:0;right:34px}
.reels-card:hover .reels-del,.reels-card:hover .reels-move{opacity:1}
.reels-move{display:inline-flex;align-items:center;justify-content:center}
.reels-del:hover{background:rgba(14,14,12,.8)}

.reel-tot{margin-left:auto;font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:11px;color:var(--ink-mute);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:52%}
.reel-check{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--ink-soft);cursor:pointer;user-select:none}
.reel-check input{accent-color:var(--ink)}
/* .reel-seg is GONE (Mark, 1 August 2026: "why would you make sliders, colours
   pickers etc different to the rest of the app"). It was a bespoke 28px
   bordered strip that existed alongside .pill-select.seg — the app's own
   segmented control — for no reason other than that I wrote it without
   looking. Every segment in this panel is .pill-select.seg now, at the app's
   own size, so Backdrop / Alignment / Timing read as the same control the
   styled-capture options use. The 28 July height-match note it carried is
   preserved on .reel-dd-btn, which is the control it had to line up with. */
/* Same pill, tighter gutters — 18px of side padding is sized for the options
   column, and at 270px it pushed Backdrop off the row it has shared with
   Aspect since 28 July. Narrowing a measurement is not inventing a control;
   .canvas-acc-body does the same to .opt-slider. */
.reel-side .pill-select.seg{flex-wrap:wrap}
.reel-side .pill-select.seg .pill{padding:8px 14px}
/* The palette here runs from #0E0E0C to #FAFAFA, and a flat --ink ring on a
   near-black disc is a black ring on black. The paper gap makes the app's
   selected ring survive both ends of its own palette — the same reasoning the
   old reel swatch carried, now applied to the app's swatch instead of used as
   an excuse to draw a different one. */
.reel-side .swatch.is-selected{box-shadow:0 0 0 2px var(--paper),0 0 0 4px var(--ink)}
/* Pace: a stepped slider (Molly) — faster toward the right, Full at the left
   end, Strobe at the right. Tick dots mark the six stops. */
/* --pace-inset was 18px, and then 12px inside the sequence panel: the rail was
   held off both edges so the two END labels could centre on their own ticks the
   way the middle four do, instead of flushing to the edge and dragging their
   centres 9.5px and 16.5px off.
   It is 0 now (Mark, 3 Aug 2026: "the pace slider is not full width for some
   reason"). He is right, and the reason it looked wrong is that it WAS wrong:
   every other control in this panel — every swatch grid, every pill row, the
   scrim slider — runs the full width of the row's content box, and this one
   alone stopped 12px short at each end, which reads as a mistake rather than as
   a decision.
   The end labels keep centring on their ticks. They simply overhang into the
   row's own 12px of padding to do it, which is space no other row's content
   uses and nothing paints in. Absolutely positioned, so the overhang costs the
   layout nothing; measured, "Strobe" reaches the panel's outer edge and stops
   there, with the preview panel's 20px gutter beyond it. */
/* No floating value in the label row: it sat right-aligned and MOVED as you
   dragged, so the eye tracked the number instead of the track it was setting. */
/* Negative top margin: the 22px input box carries ~9px of dead air above its
   3px track, so a positive margin put "Pace" further from its own slider than
   the slider was from the section above it. */
/* --pace-th is the thumb's diameter, and it is a variable because THREE things
   have to agree on it: the thumb itself, the tick dots below, and the inline
   left reel.js writes on each stop label. A range thumb's centre travels the
   rail MINUS one thumb — half in at each end — so anything pinned to a plain
   percentage of the rail drifts up to half a thumb away from the value it is
   marking. That is what made this control read as messy (Mark, 3 Aug 2026:
   "the pace slider is a bit messy"): measured, the Full label sat 5px left of
   where the thumb parks on Full, Strobe 5px right, and the middle four bowed
   between. One axis now: calc(--pace-th/2 + (100% - --pace-th) * k). */
.reel-pace-stops,.reel-pace-slider{--pace-inset:0px;--pace-th:10px}
/* width is explicit because a range input is REPLACED: `width:auto` does not
   fill its block the way the stops row above it does, it collapses to the UA's
   intrinsic 129px. That is why the sequence panel used to carry a width of its
   own; now the base rule owns it, and the calc keeps the rail honest if
   --pace-inset is ever set back above 0. */
.reel-pace-slider{-webkit-appearance:none;appearance:none;width:calc(100% - var(--pace-inset) * 2);height:22px;background:transparent;cursor:pointer;display:block;margin:-4px var(--pace-inset) 0}
/* A PLAIN rail — no tick dots (Mark, 4 Aug 2026: "lets remove the dots on the
   pace line"). The six dots were doing the labels' job twice: the names sit
   directly under their stops already, so the dots added a second, fainter set
   of marks to read and made the one slider in this panel look unlike every
   other slider in the app. The stops themselves have not moved — the thumb
   still snaps to the six, and the names are still the target you click.
   --bmed and 999px: the same 3px rail with the same 10px ink thumb as
   .opt-slider everywhere else. */
.reel-pace-slider::-webkit-slider-runnable-track{height:3px;border-radius:999px;
  background:var(--bmed)}
/* The same bare 10px ink dot as .opt-slider (Mark, 28 July 2026) — the paper
   ring and its 1px halo made this one thumb look like a different control.
   margin-top = -(10 - 3) / 2, centring it on the 3px track. */
.reel-pace-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:10px;height:10px;border-radius:50%;
  background:var(--ink);margin-top:-3.5px;border:none;box-shadow:none}
.reel-pace-slider::-moz-range-thumb{width:10px;height:10px;border-radius:50%;background:var(--ink);border:none;box-shadow:none}
/* Named stops, one per tick (Mark, 28 July 2026). Replaces the Full/Rush end
   words, which named two of the five paces and left Punch invisible until you
   dragged onto it. The current one goes solid; the rest are quiet. Negative
   top margin because the 22px slider box leaves ~9px of air under its 3px
   track before .reel-side-sec's own 6px gap even starts. They're buttons, not
   spans — once a name is the thing you read, it's the thing you'll click. */
/* Each name is CENTRED ON ITS TICK, not distributed by space-between — the
   track's six dots sit at fifths, and space-between only lands the middle ones
   near them (Punch was 15px adrift, Rush 24px). Absolute + the left% reel.js
   writes from PACE_ORDER puts each label under the stop it names — ALL SIX,
   including the ends, which is why the row shares the slider's inset: the end
   labels hang half their width into it rather than flushing to the edge and
   dragging their centres off their ticks. */
.reel-pace-stops{position:relative;height:14px;margin:-8px var(--pace-inset) 0}
.reel-pace-stops button{position:absolute;top:0;transform:translateX(-50%);
  border:0;background:transparent;padding:0 2px;cursor:pointer;font-family:var(--sans);
  font-size:10.5px;line-height:1.2;color:var(--ink-mute);white-space:nowrap}
.reel-pace-stops button:hover{color:var(--ink-soft)}
.reel-pace-stops button.on{color:var(--ink);font-weight:500}
/* Transport: filled icon buttons, visually apart from the ghost text actions.
   (The `.reel-transport` CLASS rule that used to sit here is gone — the
   transport is `#reel-transport.reel-tbar` under the stage now, and nothing
   has carried that class since. Its own gap is set in .reel-tbar below.) */
.reel-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;
  border:0;background:var(--ink);color:var(--paper, #fff);cursor:pointer;padding:0}   /* the fill's opposite, so dark mode's light disc keeps its mark (21 Sep 2026) */
.reel-icon-btn:hover{opacity:.82}
.reel-icon-btn svg{display:block}
/* Status pill over the BOARD while preparing/rendering for the Reel — the
   topbar narration was easy to miss (Mark). */
.layouts-prep-pill{position:fixed;bottom:26px;left:50%;transform:translateX(-50%);z-index:200;
  background:rgba(14,14,12,.85);color:#fff;font-family:var(--sans);font-size:12px;font-weight:500;
  padding:7px 15px;border-radius:999px;pointer-events:none;white-space:nowrap}
/* Bottom of the right panel (Mark's layout, 28 July): the PLAY TOOLBAR
   (restart · prev · play · next · clip N of M · progress · time), then
   Auto arrange + Clear, then the SELECTED-CLIP CARD carrying the info the
   strip cards no longer show, plus reorder controls that don't fit on the
   cards themselves. */

/* ══════ VARIANT A — the editor panel wears the capture panel's clothes ══════
   3 Aug. Mark, on the two panels side by side: "the side bar for layouts is
   great and really clean. the sequence side bar is so messy and not styled
   like the layouts one" — then, choosing between the mockups, "I think we
   should go with A. keep consistent."

   Nothing new is invented here. The band + option-row treatment already exists
   and is the app's answer to "a column of settings"; it just happened to live
   under #canvas-settings, which is the SIDEBAR's id and therefore unavailable
   to a second column. So the editor's root carries .seq-settings and this
   block re-points the rules that matter at it.

   On specificity: .seq-settings .canvas-acc > summary is 0-2-1 against the
   unscoped .canvas-acc > summary's 0-1-1, which is the only thing it has to
   beat. It never competes with #canvas-settings itself — the sidebar and the
   preview column are different elements in different places and no node is
   ever inside both — so there is no id-vs-class fight to lose. Keep it that
   way: if a rule here ever needs !important, the real bug is that the two
   panels have started sharing a container. */
/* NO HORIZONTAL PADDING. This shipped as `4px 2px` and that 2px was the whole
   of a 2px indent difference against the sidebar: #canvas-settings IS the
   column (x 1161, w 259, no padding of its own), whereas .seq-settings padded
   itself and handed its bands 255px starting at 1163. Every label downstream
   inherited the offset — capture labels measured at x=1175, these at x=1177 —
   and the grey bands were 4px narrower than the sidebar's for the same reason.
   The vertical 4px stays; it is air under the mode title, not an inset. */
.seq-settings{display:flex;flex-direction:column;gap:6px;padding:4px 0}
.seq-settings > .rail-mode-title{margin-bottom:10px}
.seq-settings .canvas-acc{border:0;background:transparent}
/* The tonal BAND (Option B, Mark, 16 July 2026): a filled bar with the title
   in full ink, so a section header reads as a header and not as faint
   metadata. No divider between bands — the fill plus the 6px gap already
   separate them, and a hairline on top of that read as a stray underline. */
.seq-settings .canvas-acc > summary{
  list-style:none;cursor:pointer;padding:10px 12px;
  background:var(--acc-band);border-radius:8px;
  font-family:var(--sans);font-size:13px;font-weight:500;
  letter-spacing:0;text-transform:none;color:var(--ink);
  display:flex;align-items:center;justify-content:space-between}
.seq-settings .canvas-acc > summary:hover{background:var(--acc-band-hover);color:var(--ink);opacity:1}
.seq-settings .canvas-acc > summary::-webkit-details-marker{display:none}
.seq-settings .canvas-acc-body{padding:16px 2px 20px;font-size:12.5px;min-width:0}
.seq-settings .canvas-acc[hidden]{display:none}
/* INNER rows are subordinate to the band: smaller sans label, quiet mono
   value, smaller chevron, indented. Flat — no per-row box and no hairline
   between rows (Mark: the boxes read "way too cardy", then the lines between
   them were "thin lines" to drop too). The bands alone carry the structure,
   which is the same call he made again today for the Layouts panel.

   MARGIN IS ZERO, not the 2px this shipped with. A diff of the two columns'
   computed styles (every property of the band, the row, the label, the value,
   the chevron, the expand and the pill) came back with exactly one difference
   between a capture panel's row and this one, and it was that 2px. There is no
   argument for it that is not also an argument for changing the capture panels
   — which is Mark's point about consistency, so it went. */
.seq-settings .canvas-acc-body .option-row{
  background:transparent;border:0;border-radius:0;padding:0;margin:0;min-width:0}   /* no inset of its own: the rail's edge is every rail's (21 Sep 2026, Mark: "all rails should have consistent spacing") */
.seq-settings .canvas-acc-body .option-row .opt-toggle-row,
.seq-settings .canvas-acc-body .option-row .option-summary{padding:14px 0}   /* 48px rows, the rail's (21 Sep 2026) */
.seq-settings .canvas-acc-body .opt-toggle-row{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  border-top:0;flex-wrap:nowrap}
/* THE BASE LABEL, which the summary rule below then quietens. Both panels have
   two species of row: the collapsing kind, whose label is a summary and reads
   subordinate at 12px/--ink-soft, and the ALWAYS-OPEN kind — "Padding" in a
   capture panel, "Pace" and "Length" here — whose label is a heading for the
   control under it and reads at full ink. That second species had no rule in
   this scope, so it fell through to the global .option-label at 13px while the
   sidebar's fell to its own 12.5px. Half a pixel, but it is visible: Pace and
   Length sat a size above every other label in the same column, which is the
   exact fault Mark named. Mirrors #canvas-settings' rule at ~7934. */
.seq-settings .canvas-acc-body .field-label,
.seq-settings .canvas-acc-body .label,
.seq-settings .canvas-acc-body .option-label{
  font-family:var(--sans);font-size:12.5px;font-weight:400;letter-spacing:0;text-transform:none;color:var(--ink)}
.seq-settings .canvas-acc-body .option-summary .option-label{
  font-family:var(--sans);font-size:12.5px;letter-spacing:0;text-transform:none;color:var(--ink-soft)}
.seq-settings .canvas-acc-body .option-summary:hover .option-label{color:var(--ink)}
.seq-settings .canvas-acc-body .option-value{
  font-family:var(--mono);font-size:11px;letter-spacing:.04em;color:var(--ink50)}
.seq-settings .canvas-acc-body .option-summary .option-chevron{width:8px;height:8px;color:var(--ink32)}
/* Expanded body: the hairline sits BELOW the controls — dividing the open row
   from the next one, not the summary from its own settings (Mark, 16 July
   2026: "the hairline border is above the actual options buttons/settings, it
   should probably be below"). */
.seq-settings .canvas-acc-body .option-row .option-expand{
  margin:0;padding:4px 0 22px;background:transparent;
  border-bottom:0;border-radius:0}   /* no line under an opened row, air instead (21 Sep 2026, Mark: "lots of hairline borders in the drop downs") */
.seq-settings .canvas-acc-body .option-row{border-bottom:0}   /* nor between the rows: the rail's rule since the Studio bands lost theirs */
.seq-settings .canvas-acc-body .option-expand[hidden]{display:none}
/* Controls sized for a 300px column: pills wrap and shrink rather than forcing
   the panel to scroll sideways. */
.seq-settings .pill-select{display:flex;flex-wrap:wrap;gap:3px;max-width:100%;width:fit-content}
.seq-settings .pill-select .pill{font-size:11px;padding:5px 12px;white-space:nowrap}
.seq-settings .num-stepper{transform:scale(.92);transform-origin:right center;width:fit-content}
.seq-settings .field{display:flex;flex-direction:column;gap:7px;margin-bottom:12px}
.seq-settings .field:last-child{margin-bottom:0}
.seq-settings .field-label{font-family:var(--sans);font-size:12.5px;color:var(--ink-soft)}

/* PACE keeps the slider under its own label row — it is the one always-open
   row here, so the row is a label line plus a control rather than a summary
   plus a drawer. The stops need their inset room back after the row's 12px
   padding, hence the negative margin.

   WIDTH IS EXPLICIT. The base rule says width:auto, which fills for the stops
   (a div) and does NOT for the slider: an <input type=range> is a replaced
   element, so auto resolves to its INTRINSIC width — 129px, measured, against
   the stops' 203px. The old wrapper hid that by putting the slider in a flex
   row that stretched it; the wrapper is gone, so the width has to be said out
   loud. The 24px is the two 12px margins, and % resolves against the row's
   content box, so the two rails line up to the pixel and every stop label sits
   over its own tick.

   And the stops drop 4px rather than 8. The base -8px was tuned when a section
   label sat above the slider; here the track's centreline measured 1.5px above
   the label text, which is a collision, not a gap. */
/* Mark, 3 Aug 2026: "need more space below pace settings, too close to aspect.
   make same gap for all."
   NOTE THE SELECTOR. This used to read `.seq-settings .reel-pace-row` — two
   classes, against the `.seq-settings .canvas-acc-body .option-row{padding:0
   12px}` three classes above it. Later in the file but lower in specificity, so
   it lost, and the 6px it asked for was never once applied. Same story for the
   `.opt-toggle-row{padding-bottom:6px}` that sat beside it; that one is deleted
   rather than rescued, because 11px is what every other row in the band uses
   between its label and its control and there is no reason for Pace to be the
   exception. Anything that wants to beat that base rule has to match it.
   The 9px is measured, not reasoned. Every other row here puts 22px of white
   between its last glyph and the next row's label. Pace was giving 13: the
   next row's own 11px, plus about 1.4px of slack under 10.5px type in a 14px
   box. 9 + 13 = 22, checked on screen, matching Aspect to Backdrop exactly. */
.seq-settings .canvas-acc-body .option-row.reel-pace-row{padding-bottom:9px}
/* The sequence panel used to narrow the rail further still (width:100% - 24px,
   with a negative-6px pull on each margin). Both are gone: the rail runs the
   row's full content box here exactly as it does everywhere else. Only the
   -4px pull of the stops row up under the rail survives, and that is spacing,
   not width. */
.seq-settings .reel-pace-stops{margin-top:-4px}

/* Auto arrange / Clear. Loose at the foot of the Sequence settings band since
   #298, so the padding an .option-expand used to give them has to come from
   here: the same 12px side indent every row in the band keeps, and the same
   6/14 above and below that the block band's loose actions row uses. */
/* at the foot of the rail, whatever stands above (21 Sep 2026, Mark: "maybe move auto arrange and clear to the bottom of the rail") */
/* the rail's column is left alone: a rule on the panel's id outranked the shell's, which hides the rail behind the door
   (Mark: "sequence rail is on the sequence start page"); the side already stands the column's height */
.reel-side.has-rtabs[data-rtab="seq"] > #reel-seqsec, .reel-side.has-rtabs[data-rtab="seq"] > #reel-seqsec::details-content, .reel-side.has-rtabs[data-rtab="seq"] > #reel-seqsec > .canvas-acc-body { flex: 1 1 auto; display: flex; flex-direction: column; }   /* the details' own content box stands between the section and its body, so it is a column too */
.reel-arr-acts{display:flex;gap:8px;flex-wrap:wrap;padding:24px 0 0;margin-top:auto}
.reel-arr-acts .ghost-btn{flex:1 1 auto}
/* ── #308b: EDIT LAYOUT, an outlined primary at the top of the band ────────
   Mark, 4 August 2026: "Edit layout probably needs to be more prominent?" then,
   choosing between a filled and an outlined treatment, "I think outlined one."
   Full width because it is the only thing on its line and a 112px button in a
   231px row would leave a hole beside it. calc(100% - 24px) with a 12px margin
   lands the button's box on exactly the same left and right edges as every
   .option-row summary under it — measured 1175→1406 in a 1440px window, the
   same 231px — so it reads as part of the panel's spine rather than a slab
   dropped on top of it.

   The mockup asked for a 1.5px rule. Chromium FLOORS fractional border widths
   to whole pixels — a probe of `border:1.5px` computes back as `1px` — so the
   choice is really 1px or 2px, and 2px is a heavy stroke to put on a 231px
   button in a panel whose every other line is a hairline (Mark, 4 August 2026:
   "Flodesk style simplicity"). 1px is enough here because the CONTRAST is
   carried by the colour, not the thickness: this is --ink at full opacity where
   the three secondary toggles at the foot wear --bmed, which is the same ink at
   16%. Six times the weight at the same width is not a distinction anyone has
   to squint at.
   The corner was 9px "matching the .ghost-btn family this is a heavier cousin
   of" — which .ghost-btn is not: it is 8px, and has been since it was written.
   9px against 8px is not a distinction, it is a miss, and this button sits
   directly above rows of 8px controls. 8px, and 12px type rather than 12.5 for
   the same reason (12 Aug 2026, the button pass). */
.reel-cc-primary{display:block;width:calc(100% - 24px);margin:2px 12px 10px;background:none;
  color:var(--ink);border:1px solid var(--ink);border-radius:8px;padding:11px;cursor:pointer;
  font-family:var(--sans);font-size:12px;font-weight:500;letter-spacing:.01em;line-height:1;
  transition:background .15s ease,color .15s ease}
.reel-cc-primary:hover{background:var(--ink);color:var(--paper)}
/* display:block above overrides the UA [hidden] rule, exactly as .ghost-btn
   has to — and this one is hidden on every block that has no board behind it,
   which is most of them. */
.reel-cc-primary[hidden]{display:none}
/* The three conditional toggles at the FOOT (#308b), below Text and above the
   remove line. They need the air a row's padding would otherwise give them on
   both sides; 14px above because the Text row's expand ends flush. */
.reel-cc-acts-foot{padding:14px 0 4px}
.reel-cc-acts-foot:empty{display:none}
/* :empty is not enough: the row is never literally empty, because its three
   buttons are hidden rather than absent. reel.js hides the ROW when the last of
   them goes, and this has to beat the .reel-cc-acts.reel-cc-acts-foot
   display:grid below — hence BOTH classes plus the attribute. One class and
   [hidden] only ties, and a tie is lost to whichever comes last. */
.reel-cc-acts.reel-cc-acts-foot[hidden]{display:none}
/* #276's measurement still stands, and now with one fewer button to place.
   The band gives this row 231px of inside width and all THREE toggles can be
   live at once — Reset length + one of Fill frame / Play full, which the
   predicates make mutually exclusive, plus Reset length's own condition. Three
   natural-width buttons need 243-263px and have never fitted on one line. A
   fixed pair of columns takes the label lengths out of it: two 112.5px cells,
   every label clears with room, and the three make a tidy 2+1 block on a shared
   spine instead of three different widths breaking wherever the text runs out.
   Compound selector on purpose: .reel-cc-acts sets display:flex 150 lines below
   this, so a single class here would lose on source order. */
.reel-cc-acts.reel-cc-acts-foot{display:grid;grid-template-columns:1fr 1fr;gap:8px;align-items:start}
/* ── #308b: the ORDER arrows, on their own labelled row ────────────────────
   They are no longer inside .reel-cc-acts, so they no longer inherit its button
   treatment and carry their own — the same 26px square, the same --bmed outline
   and the same 8px radius they had in the grid, because nothing about the
   control changed, only where it sits. Paired in one flex box with the grid's
   own 6px gap: they read as the one control they are, earlier and later. */
.reel-cc-nudge{display:flex;gap:6px;align-items:center}
.reel-cc-nudge button{width:26px;height:26px;padding:0;border:1px solid var(--bmed);background:transparent;
  border-radius:8px;font-family:var(--sans);font-size:12px;color:var(--ink);line-height:1;cursor:pointer;flex:none}
.reel-cc-nudge button:hover:not(:disabled){border-color:var(--ink50)}
.reel-cc-nudge button:disabled{opacity:.3;cursor:default;border-color:var(--bmed);color:var(--ink50)}
/* ── THE LAYOUT-SETTINGS SECTION: BAND OUTSIDE, FLAT INSIDE (#290, #292) ───
   Mark, 3 Aug 2026: "I dont think the card and layout settings should be in
   drop downs actually" — so Motion, Slides in from, Length, Which part plays,
   Background and Order are all always open. Then, the same day: "'layout
   settings' as a main drop down still needs to be there, why did you remove
   that. it was only the drop downs inside this."
   #290 read the first instruction one level too far up and flattened the
   containing section too, swapping the <details> band for a bare <section>
   heading. It is a <details class="canvas-acc"> again, wearing exactly the
   band Sequence settings wears, and the two collapse against each other: this
   panel has two subjects and a 950px window fits one of them whole. Only the
   rows INSIDE stayed flat. Nothing here overrides the band — it inherits
   .seq-settings .canvas-acc > summary and .canvas-acc-body from the rhythm
   block above, which is the point. */
/* The rows that now carry their control as a SIBLING of the toggle row rather
   than inside an .option-expand need that expand's spacing by hand: 4px above
   the control, 14px below it. Without this the control butts straight into the
   next row's label and the column loses the rhythm every other row keeps. */
.seq-settings .reel-ccsec .option-row > .reel-slipmap{padding:4px 0 14px}
/* Starts at states its own case in the map: the label sits opposite a value but
   the TRACK follows underneath, so the row header does not need the 11px of
   bottom padding that a self-contained row is given. Order is a self-contained
   row again since #308b (label opposite control, nothing under it) and is
   deliberately NOT in this selector. */
.seq-settings .reel-ccsec #reel-cc-slipf > .opt-toggle-row{padding-bottom:2px}
/* The clip's name line and its 12px indent are gone with the line itself
   (#308b) — the band opens on Edit layout now, which carries the same indent
   from .reel-cc-primary's own margin. */
/* A slip that would cut mid-motion is amber on the map; the closed row has to
   carry the same warning or collapsing the row hides the problem. Same hue as
   .rsm-warn — one warning colour in this panel, not two. */
.seq-settings .option-value.is-warn{color:#c8791f}

/* The wrap override that used to live here is gone with the caption it was
   propping up (#307/#308). It existed because a 166px caption and an 80px
   warning could not share a 227px row, so the warning dropped to a second line
   — which is exactly the mess Mark called out. Removing two of the three
   numbers from the caption and moving the third onto the row header left the
   warning alone on a full-width line, so it needs no override at all. */
/* No shots: the Shot settings band is not on screen at all, so this stands in
   for it rather than letting the panel end mid-thought. */
/* 14px, not 12. This line is a sibling of the bands rather than a child, so it
   does not get the body's 2px — measured at x=1173 against every label's 1175.
   It is the only text in the column outside a band, and the whole point of it
   is to sit where the shot rows will appear once there is a shot. */
.reel-seq-empty{margin:10px 14px 0;font-family:var(--sans);font-size:12px;line-height:1.5;color:var(--ink-mute)}
.reel-seq-empty[hidden]{display:none}
/* Remove borrows the Layouts panel's quiet destructive line — same class, so
   it is literally the same control, which is the whole point of today. */
.seq-settings .layouts-reset{margin:14px 0 0;width:auto;display:inline-flex;align-items:center;height:34px;padding:0 14px;border:1px solid var(--bmed);border-radius:8px;color:var(--ink);font-size:12.5px;text-align:center}   /* a button like Fill frame above it (21 Sep 2026, Mark: "the text in the block settings needs to be a button") */
.seq-settings .layouts-reset:hover{border-color:var(--ink)}
/* Always .reel-host's number + 6 — the strip's own 4px bottom padding plus the
   2px the panel starts lower. Moved 128 → 84 with the host; if one of these two
   is ever edited alone the right column stops bottoming out level with the
   cards, which is the exact complaint this rule exists to answer. */
.reel-side{min-height:calc(100vh - 84px)}  /* bottoms out level with the strip cards (Mark: "nothing is lined up") */
/* The flexible spacer went with the footer it was holding down: Auto arrange
   and Clear are inside the Sequence settings band now, so there is no pinned
   row left to push away from and the column is simply top-aligned. */
/* ── THE TRANSPORT, UNDER THE STAGE ────────────────────────────────────────
   Mark, 1 August 2026: "reloaded and transport controls still in sidebar
   etc." — and before that, on the redesign: "The timeline bar needs to be in
   the section with the play button not at the bottom." Both halves of that
   are one row: it sits in the main column between the picture and the ruler,
   so the controls are against the thing they control, and the progress bar
   runs in the same row as the play button that drives it rather than across
   the room from it.

   No border and no card. Inside the panel it needed a frame to say "this is a
   device, not another setting"; under the stage the picture above it and the
   ruler below already do that job, and a second bordered box in the main
   column would fight the stage's own rounded rectangle. */
.reel-tbar{flex:none;display:flex;align-items:center;gap:9px;padding:10px 2px 0}
.reel-tbar.is-empty{opacity:.45;pointer-events:none}
/* Play LEADS as the only filled disc (Mark, 28 July 2026, from the Option 3
   mockup): four same-sized circles in a line made you read all four glyphs to
   find the one you wanted. 34px here rather than the 42px it took inside the
   panel — it is no longer the largest thing in a narrow column, and beside a
   3px progress bar 42px reads as a button that has broken loose. */
.reel-tbar .reel-icon-btn{width:34px;height:34px}
.reel-tbtn{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:0;border-radius:50%;
  background:transparent;color:var(--ink);cursor:pointer;padding:0}
.reel-tbtn:hover{background:var(--btn2bg-hover, rgba(14,14,12,.06))}
/* Restart TRAILS the stepping pair rather than opening the row: prev/next
   step by one block, restart jumps to the top of the reel, so it is not a
   third member of that group. The extra margin is the seam between them. */
.reel-tbtn-restart{margin-left:3px;color:var(--ink-mute)}
.reel-tbtn-restart:hover{color:var(--ink-soft)}
/* The bar takes all the slack — it is the one element in the row whose LENGTH
   carries meaning, so it gets the width and the readouts trail it at their
   natural size. A little more left margin than the row gap, so it reads as a
   separate zone from the buttons rather than a fifth control. */
.reel-tprog{flex:1;min-width:60px;margin-left:5px;height:3px;border-radius:2px;background:var(--border);cursor:pointer;position:relative}
.reel-tprog i{position:absolute;left:0;top:0;bottom:0;border-radius:2px;background:var(--ink);width:0}
.reel-ttime{font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:10px;color:var(--ink-soft);white-space:nowrap;flex:none}
/* No auto margin any more: in the panel this was pinned right because the row
   was otherwise empty, but here the progress bar is the flexible element and
   an auto margin on top of that just doubled the gap. */
.reel-tclip{font-family:var(--sans);font-size:11px;color:var(--ink-soft);white-space:nowrap;flex:none}
/* Aspect dropdown — the six-ratio seg outgrew the panel (Mark) */
.reel-dd{position:relative}
/* 28px, level with the Backdrop segment beside it (Mark, 28 July 2026). The
   `#reel-asp button` rule that used to sit here is gone: it was left over from
   the days when Aspect was a six-button segment, and now that #reel-asp is the
   dropdown WRAPPER it was landing on this button AND on every item in the open
   menu — shrinking both to 10.5px and overriding the menu's own padding by id.
   That mismatched font is what made the two controls different heights. */
.reel-dd-btn{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);border-radius:8px;
  background:var(--bg,var(--paper));font-family:var(--sans);font-size:11.5px;color:var(--ink);
  height:28px;padding:0 10px;line-height:1;cursor:pointer}
.reel-dd-btn:hover{border-color:var(--ink50)}
.reel-dd-menu{position:absolute;top:calc(100% + 4px);left:0;z-index:40;min-width:150px;
  border:1px solid var(--border);border-radius:9px;background:var(--bg,var(--paper));box-shadow:0 8px 26px rgba(14,14,12,.14);
  padding:4px;display:flex;flex-direction:column}
.reel-dd-menu[hidden]{display:none}
.reel-dd-menu button{border:0;background:transparent;text-align:left;font-family:var(--sans);font-size:11.5px;
  color:var(--ink);padding:6px 9px;border-radius:6px;cursor:pointer;white-space:nowrap}
.reel-dd-menu button:hover{background:var(--btn2bg-hover, rgba(14,14,12,.06))}
.reel-dd-menu button.is-on{background:var(--ink);color:#fff}
/* Font and Weight (Mark, 1 August 2026) — the last two native <select>s in the
   app, in the one panel that had been rebuilt most recently. They are .reel-dd
   now, the same dropdown as Aspect above them. Full width because they sit in
   a stacked .field rather than beside another control, and scrollable because
   the curated font list is longer than a menu should be tall. */
.reel-side .reel-dd{align-self:flex-start}
/* 8px, not 100px (Mark, 6 Aug 2026 — "too round pill shape. Make consistent
   with the rest in the app."). These two ARE the only .reel-dd in .reel-side,
   so the rule reaches exactly the Font and Weight controls and nothing else;
   the matching pair in the Layouts text drawer moved with them. */
.reel-side .reel-dd .reel-dd-btn{font-size:13px;height:39px;padding:0 14px;border-radius:8px;
  border-color:var(--bmed);gap:10px}
.reel-side .reel-dd .reel-dd-btn:hover{border-color:var(--bstr)}
.reel-side .reel-dd-menu{max-height:250px;overflow:auto;min-width:100%;border-color:var(--bmed);border-radius:10px;padding:5px}
.reel-side .reel-dd-menu button{font-size:13px;padding:8px 11px;border-radius:8px}
.reel-dd-cur{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Group heading inside a menu — Curated / Uploaded. Mono caps, matching the
   library dropdown's own section rule, so it cannot be mistaken for an item. */
.reel-dd-sec{font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:9px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--ink-mute);
  padding:8px 9px 4px;line-height:1}
.reel-dd-sec:first-child{padding-top:4px}
/* Top-level in the panel now that the transport it used to sit inside has
   moved under the stage (#184). The negative side margins are gone with it:
   they existed to cancel the transport card's 11px padding, and left in place
   they would drag this 11px outside the panel's own. Still a rule rather than
   a frame — it separates the block being edited from the reel-wide settings
   above without re-drawing a box. No min-height: its absence is what lets the
   panel stay still when the readout appears. */
/* #reel-clipcard's own hidden guard used to live here as .reel-clipcard[hidden]
   — a CLASS selector for an element that only ever had that word as an ID, so
   it matched nothing for its whole life. What actually hides the block at zero
   layouts, and did all along, is .seq-settings .canvas-acc[hidden] up in the
   rhythm block. Removed rather than corrected: one rule, one place. */
/* The #214 section spine (.reel-sec / .reel-sec-h — hairline, 15px of air,
   small-caps name) is retired with Variant A. It solved the right problem in
   the wrong currency: the panel did need sections, but it invented a fourth
   kind of section header for an app that already had one. The grey band is
   that header now, and it is the same object a capture panel uses. */
/* .reel-cc-title is gone (#308b). It was the five-fact line at the head of the
   band — "Grid · Still · 5 beats · 2.8s" — and at 11px it was also the smallest
   non-mono type in the panel, which is the wrong place to spend the smallest
   size in a column being pared back. .reel-cc-note below is a different
   element and stays. */
.reel-cc-note{font-family:var(--sans);font-size:10.5px;color:var(--ink-soft);line-height:1.5;min-height:28px}
/* The ROW breaks, never the labels (Mark, 28 July 2026). "Reset length" only
   appears once you've changed a clip's length, and its arrival was pushing
   five buttons into a width that fits three — every label split onto two lines
   and the two arrows stretched to match, which is the mess in the screenshot.
   Now: nowrap labels, a fixed 23px height so nothing can stretch, and wrap so
   the fifth button drops to a tidy second row. */
.reel-cc-acts{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:1px}
/* #276. Two things were off the app's grammar here and both were small
   numbers doing loud work. 10.5px made these the smallest type in the panel
   by two points — below the 12.5px labels they sit under, below even the
   10.5px mono READOUTS, which is the wrong way round: a readout may whisper,
   a button you press may not. And the border was var(--border), which is
   var(--rule-faint) — a HAIRLINE token, used for dividing lines. Every other
   bordered control in this panel takes --bmed, so these five read washed out
   next to their own neighbours.
   The height still holds at a fixed value and the labels still nowrap, which
   is what stopped the five-button row shredding on 28 July; 26 and 11.5 keep
   that behaviour with the wrap doing the same job one notch up. */
.reel-cc-acts button{border:1px solid var(--bmed);background:transparent;border-radius:8px;font-family:var(--sans);
  font-size:11.5px;color:var(--ink);padding:0 10px;cursor:pointer;line-height:1;white-space:nowrap;height:26px;flex:none}
.reel-cc-acts button:hover:not(:disabled){border-color:var(--ink50)}
/* A move button at the end of the sequence, or any of them on a lone layout,
   is spent — same contract and the same .3/quiet treatment the length steppers
   use twenty lines down, because a control that looks live and swallows the
   press is worse than one that admits it. */
.reel-cc-acts button:disabled{opacity:.3;cursor:default;border-color:var(--bmed);color:var(--ink50)}
/* The nudge arrows left this row for their own labelled Order row (#308b) and
   carry their square 26px treatment with them — see .reel-cc-nudge button. */
/* LENGTH reuses the app's .num-stepper (the same pill as every other ± in the
   app) with a READOUT instead of a text input: a beat count is chosen from
   what the fitter allows rather than typed, so there is nothing to type into.
   The readout is mono and tabular so the pill does not resize as the number
   changes — a control that twitches when you press it feels loose. */
.reel-cc-len .reel-cc-lenv{display:inline-flex;align-items:center;justify-content:center;min-width:34px;
  font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:11px;font-variant-numeric:tabular-nums;
  color:var(--ink);line-height:1;padding:6px 2px;user-select:none}
.reel-cc-len .num-step{font-family:var(--sans);font-size:13px;line-height:1}
.reel-cc-len .num-step:disabled{opacity:.3;cursor:default;background:transparent;color:var(--ink50)}
/* The four slide arrows are one glyph each, so the segment sizes to its
   content rather than stretching across the panel like the six-stop ones.
   ONE ICON EACH since #314 (Mark, 17 Aug 2026: "fix the slide selector"). They
   were the keyboard characters, set at the panel's 15px in word-pills padded
   8px 14px, which measured 41 / 41 / 34.5 / 34.5 — a ragged run of four whose
   group came to 177px beside an 85px label in a 264px rail. 262 of 264: it fit
   on paper and wrapped in practice. As `.is-icon` they are four 34px squares,
   120px in total, drawn rather than typed, and matching the alignment and layer
   icons two rows down. `nowrap` because .reel-side lets segments wrap by
   default and there is nothing here worth wrapping. */
#reel-cc-dir,.reel-cc-len{align-self:flex-start}
#reel-cc-dir{flex-wrap:nowrap}
/* ── a card's controls ──────────────────────────────────────────────────
   A card is a colour and a length. The length already has the same ± the
   rest of the rail uses, so all that is left here is the colour, and the
   honest control for choosing one of six known colours is six colours —
   not a dropdown naming them. Six swatches plus a custom well fit on one
   26px row at the panel's narrowest, which is why the row cannot wrap and
   does not need to. */
/* A card's thumbnail is a flat colour field, and two of the six palette
   colours are paler than the clip card it sits on — so it gets an edge. The
   ring is an ::after rather than a border so it costs no layout, and it is
   drawn over the image rather than under it (an inset box-shadow on the
   wrapper would sit beneath the <img> and never be seen). */
.reel-clip-th.is-card::after{content:'';position:absolute;inset:0;border-radius:inherit;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.16);pointer-events:none}
/* The card's colour row is a .field wrapping the app's .swatch-grid. The 20px
   rounded-square .reel-sw and the text-labelled .reel-cc-cust that used to
   live here are gone: the app has had 32px round .swatch discs with a Custom
   disc and a hex popover since the styled-capture options, and a colour picker
   that looks different in one panel is exactly the inconsistency Mark called
   out on 1 August. The discs are allowed to WRAP to a second row rather than
   being shrunk to fit the 270px panel — a wrong-sized swatch would be the same
   mistake in a smaller font. */
.reel-cc-card[hidden]{display:none}
/* #294 — the card's picture is a BACKDROP now, chosen in the upload modal and
   framed in the app's own drag box, so the named-row-with-a-thumbnail that
   #213 built here is gone along with the file input that fed it. The framer
   IS the thumbnail: it shows the picture at the size and position it will
   actually print at, which the 26px chip never could. */
.reel-cc-bd[hidden]{display:none}

/* ── the + menu ─────────────────────────────────────────────────────────
   Mark's three-option +. position:fixed so the scrolling rail cannot clip it,
   with JS placement — the same trade every popover in this panel makes; opens
   upward, because the rail is the bottom row.

   It is mounted INSIDE .app, and that is load-bearing. The design tokens are
   declared on .app, not on :root, so while this was appended to <body> every
   var(--paper), var(--border) and var(--ink) below resolved to nothing: the
   background fell back to transparent and the menu painted as an unstyled
   see-through slab with the side panel's text showing through it (Mark,
   1 August 2026: "transparent card picker thing"). position:fixed is
   viewport-relative whatever the parent, so re-parenting cost nothing in
   placement and bought the whole palette back. Nothing in reel.js should
   append a styled node to <body> again.

   Surface matched to .setup-save-menu, the app's own small popover. */
.reel-addmenu{position:fixed;z-index:60;display:flex;flex-direction:column;gap:1px;
  min-width:186px;padding:4px;border-radius:10px;border:1px solid var(--bmed);
  background:var(--paper);box-shadow:0 8px 24px rgba(17,17,17,.14)}
.reel-addmenu button{display:flex;flex-direction:column;align-items:flex-start;gap:2px;
  padding:7px 10px;border:0;border-radius:7px;background:transparent;cursor:pointer;text-align:left;width:100%}
.reel-addmenu button:hover{background:var(--pill, rgba(14,14,12,.06))}
.reel-addmenu b{font-family:var(--sans);font-size:12.5px;font-weight:400;color:var(--ink);line-height:1.2}
/* The second line is the one that stops "Card" being a guess. It is styled
   as description, not as a label, so the two lines never read as two items. */
.reel-addmenu i{font-family:var(--sans);font-size:11px;font-style:normal;color:var(--ink-mute);line-height:1.3}
.reel-stage{position:relative;flex:1;min-height:0;display:flex;align-items:center;justify-content:center;overflow:hidden;border-radius:10px}
/* Canvas is sized by reel.js (sizeCanvas): stage measured, aspect fitted,
   backing store at display px × dpr for a crisp render. CSS stays out of it —
   both height:100% and max-height:100% failed against the flexed stage
   (9:16 rendered at intrinsic size and overflowed; Mark ×2). */
.reel-stage canvas{display:block}
/* empty = a quiet paper drop-zone, not a black void (Mark, 26 July 2026) */
.reel-stage.is-empty{border:1.5px dashed var(--border)}
.reel-stage.is-empty canvas{display:none}
/* ── THE EMPTY STATE: TWO DOORS (#212) ────────────────────────────────────
   Mark, 1 August 2026: "need to sort out the empty state as it is now the
   only place for composing. what do we do here?" — answered by him as "two
   doors, rail stays the picker".
   THREE things, in reading order, each a different weight so the eye sorts
   them without being told: what a sequence is, the two doors, and a pointer
   at the third. (It was four until 4 Aug 2026 — a sub-line between the title
   and the doors explained what each kind of block does on the beat, which is
   three answers to a question nobody has asked on arrival. Mark: "keep all
   wording concise.") It sits above .reel-tx (z-index 4) and
   .reel-tguide (5) because it is the only thing on the stage when it shows;
   the text layer is pointer-events:none until you are typing, but stacking
   order is not something to leave to DOM position. */
/* THE DOOR PAGE (1 Sep 2026): the empty stage is a paper page in the layouts
   start-page grammar — top-anchored, scrolling, left-aligned — carrying the
   demo, the serif title + sub, the remembered-draft banner, the two quiet
   chips and the inline picker list. */
.reel-empty{position:absolute;inset:0;z-index:6;overflow-y:auto;background:var(--bg);
  text-align:left;color:var(--ink);padding:26px 34px 40px;box-sizing:border-box}
.reel-empty[hidden]{display:none}
.reel-empty > div{max-width:1240px;margin-left:auto;margin-right:auto}
/* Split head: words + ways-in lead on the left, the demo illustrates on the
   right (1 Sep 2026 — Mark). Wraps under ~760px so neither half crushes. */
.reel-door-head{display:flex;align-items:center;justify-content:space-between;gap:44px;flex-wrap:wrap;margin-bottom:10px}
.reel-door-lead{flex:1 1 380px;min-width:300px}
.reel-door-lead .lay-gal-sub{max-width:52ch}
.reel-door-head .reel-empty-demo{flex:none;margin:0;pointer-events:none}
/* While the door is up the surrounding chrome follows it: paper instead of
   the canvas wash, no dashed drop-zone frame, and no transport — there is
   nothing to play yet. */
.app.seq-door-open .main{background:var(--bg)}
.app.shell-modetop.is-capture-mode.seq-door-open .workspace > .main{background:var(--bg)}
.app.seq-door-open .reel-stage.is-empty{border:0;border-radius:0}
.app.seq-door-open .reel-tbar{display:none}
.reel-door-banner{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;
  border:1px solid var(--bmed);border-radius:12px;padding:12px 16px;margin:0 auto 18px;background:transparent}
.reel-door-banner[hidden]{display:none}
.reel-door-banner-t{font-size:13px;color:var(--ink)}
.reel-door-banner-a{display:flex;gap:8px;align-items:center}
.reel-door-banner-a .btn-primary{width:auto;margin:0;padding:0 18px;min-height:36px}
.reel-door-banner-a .ghost-btn{min-height:36px;padding:0 16px;border-radius:100px}
.reel-door-acts{display:flex;gap:10px;margin:0 auto 6px}
.reel-door-acts .ghost-btn{padding:0 18px;min-height:36px;border-radius:100px}
.reel-door-pick{width:100%}
/* One short line, so it can afford to be the largest thing here. balance
   rather than a hard measure: a two-word orphan on line two is worse than a
   slightly short first line. (.reel-empty-s, the sub-line that used to follow,
   was deleted with its markup on 4 Aug 2026 — see the block comment above.) */
.reel-empty-t{font-size:15px;line-height:1.45;color:var(--ink);max-width:42ch;text-wrap:balance}

/* ==== EMPTY SEQUENCE DEMO BEGIN =============================================
   Mark, 9 Aug 2026: "empty demo would be good."

   Four abstract layouts, hard-cutting on a beat. HARD cuts, not fades: a cut is
   the thing being demonstrated, and a cross-fade would be showing the one
   transition Sequence does not do. steps(1, end) is what makes that literal.

   All four cards run the SAME keyframes and are separated by delay, so they can
   never drift apart the way four independent animations would. The loop is
   2.8s: four beats of 0.7s, near enough to Quick that the demo moves at a speed
   the app can actually produce.

   Deleting from this fence to EMPTY SEQUENCE DEMO END, and the .reel-empty-demo
   block from reel.js, is the whole revert.
   --------------------------------------------------------------------------- */
.reel-empty-demo{display:flex;flex-direction:column;align-items:center;gap:10px;margin-bottom:22px;pointer-events:none}
.red-stage{position:relative;width:212px;height:120px;border-radius:10px;overflow:hidden;
  background:var(--paper);border:1px solid var(--ink08)}
.red-card{position:absolute;inset:0;display:grid;gap:5px;padding:10px;opacity:0;
  animation:red-cut 2.8s steps(1,end) infinite}
.red-card i{display:block;border-radius:3px;background:var(--ink08)}
/* Four shapes that read as four different layouts at a glance: a page over a
   footer, a three-up row, a four-up grid, a wide pair. Nothing here is a real
   template — it is the IDEA of a cut between arrangements. */
.red-1{grid-template-rows:1fr 22px}
.red-2{grid-template-columns:repeat(3,1fr)}
.red-3{grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr}
.red-4{grid-template-columns:1.6fr 1fr}
/* The first block of each card is the "content" one, so a card has a subject
   rather than reading as an even field of grey. */
.red-card i:first-child{background:rgba(14,14,12,.14)}
.red-2{animation-delay:.7s}
.red-3{animation-delay:1.4s}
.red-4{animation-delay:2.1s}
@keyframes red-cut{0%,24.99%{opacity:1}25%,100%{opacity:0}}
/* The ticks step with the cuts, so the cut is visibly ON something. */
.red-beats{display:flex;gap:5px}
.red-beats i{width:16px;height:2px;border-radius:2px;background:var(--ink08);
  animation:red-tick 2.8s steps(1,end) infinite}
.red-beats i:nth-child(2){animation-delay:.7s}
.red-beats i:nth-child(3){animation-delay:1.4s}
.red-beats i:nth-child(4){animation-delay:2.1s}
@keyframes red-tick{0%,24.99%{background:var(--ink32)}25%,100%{background:var(--ink08)}}
/* Reduced motion: the demo becomes a still of the first arrangement. Freezing
   rather than hiding, because the frame is also doing layout work here — the
   empty state would jump upward without it. */
@media (prefers-reduced-motion:reduce){
  .red-card,.red-beats i{animation:none}
  .red-1{opacity:1}
  .red-beats i:first-child{background:var(--ink32)}
}
/* ==== EMPTY SEQUENCE DEMO END ============================================== */

/* The two doors sit on one line: they are alternatives, not a sequence of
   steps. Primary first because the library is where nearly everything comes
   from; the card is second because it is the rarer, blanker thing. Both need
   their house defaults undone — .btn-primary is a full-width footer button
   by default (see .viewport-actions for the same override). */
.reel-empty-doors{display:flex;align-items:stretch;gap:10px;margin-top:24px;pointer-events:auto}
/* ONE SILHOUETTE, TWO WEIGHTS (Mark, 4 Aug 2026: "need to sort out the empty
   sequence screen. styling and wording").
   Both doors are now outlined pills at the same height and the same radius.
   They still differ, and the difference is still the point — the lead carries
   an ink border and mono caps, the second a hairline border and sans — but
   they differ in WEIGHT alone, not in shape. Before this the card button was a
   plain filled .ghost-btn: an 8px grey slab against a 100px outlined pill, so
   the pair read as two unrelated controls that happened to be adjacent, and
   the solid grey was the heaviest mark on a stage that is otherwise bare
   paper, pulling the eye to the rarer of the two routes.
   The radius is set here rather than on .ghost-btn.is-choice globally: the
   chip is an 8px lozenge everywhere else in the app (the method rows, the
   ratio crops) and it should stay one, because everywhere else it sits in a
   row of chips rather than beside a primary. This is the only place the two
   tiers stand side by side at the same size. */
.reel-empty-doors .btn-primary{width:auto;margin-top:0;padding:0 22px;min-height:42px}
.reel-empty-doors .ghost-btn{padding:0 22px;min-height:42px;border-radius:100px}
/* The third route is not a button, because pressing it would do nothing —
   the control it names lives in another column. It is a caption with a
   direction, using the transport's own back chevron so the arrow is a glyph
   the app already owns rather than a new one drawn for one line. */
.reel-empty-rail{display:flex;align-items:center;gap:6px;margin-top:16px;
  font-size:11.5px;color:var(--ink-mute)}
.reel-empty-rail svg{flex:none;opacity:.7}
/* ── the proportional rail (Mark, 1 Aug 2026: "Is timeline not proportional
   then? they are all the same size.") ───────────────────────────────────────
   The ruler is no longer one bordered box with segments inside it. It is the
   SAME column grid as the strip below — same lefts, same widths, same gaps —
   so the two rows read as one ruler rather than two rows that happen to be
   near each other. Every left and width here comes from layoutRail() in px;
   the percentages are gone because the gaps carry no time. */
/* 8/4 rather than 10/5 — see .reel-ruler's height, which came down with it. */
.reel-ruler-row{flex:none;padding:8px 2px 4px;overflow:hidden}
.reel-ruler-row[hidden]{display:none}   /* author display beats the UA [hidden] rule */

/* The .reel-seqmake block that used to live here — the "Create sequence" offer
   standing where the rail would be at exactly one layout — was removed on
   3 Aug 2026 along with its markup, because the timeline now shows from the
   first layout and there is nothing left for the offer to stand in for. */
/* ── THE SEQUENCE'S SECTIONS ─────────────────────────────
   These are permanently on screen now. There is no card in the rail to select
   the sequence with and no ring round the ruler to say you have, because the
   sequence is not somewhere you go. */
.reel-seqsec{display:flex;flex-direction:column;gap:0}
.reel-seqsec[hidden]{display:none}   /* author display:flex beats the UA [hidden] rule */
/* 24px, down from 28 (Mark, 3 Aug 2026: "slightly smaller beat timeline").
   The bar holds one 9px label and one 9px beat count, both vertically centred,
   so 24 still clears them with 3px to spare top and bottom — this is a trim,
   not a squeeze. With the row's padding down 2px as well the whole ruler band
   goes 43 → 36, which keeps it visibly subordinate to the cards below it now
   that those are shorter too. */
.reel-ruler{position:relative;height:24px;cursor:pointer}
.reel-ruler.is-empty{opacity:.4;cursor:default}
.reel-segb{position:absolute;top:0;bottom:0;border-radius:6px;overflow:hidden}
.reel-segb span{position:absolute;left:6px;top:50%;transform:translateY(-50%);font-size:9px;font-weight:600;letter-spacing:.02em;
  color:rgba(14,14,12,.6);white-space:nowrap;overflow:hidden;max-width:calc(100% - 26px);pointer-events:none}
.reel-segb i{position:absolute;right:5px;top:50%;transform:translateY(-50%);font-style:normal;
  font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:9px;color:rgba(14,14,12,.66);pointer-events:none}
/* Block ↔ card link (Mark: "tricky to know what section of the timeline
   corresponds with the capture"): hovering either lights BOTH, and the
   playing pair share the now-highlight. */
.reel-segb.hl,.reel-segb.now{box-shadow:inset 0 0 0 2px var(--ink)}
/* (The old is-first / is-last corner-radius hack is gone: it existed because
   the segments were flush inside one rounded, clipped box. Every block is its
   own rounded bar now, so the ring meets its own border everywhere.) */
/* ── slip ── (Mark, 30 July 2026: "would it be possible to choose where that
   beat length is in the animation"). A loop's block can be dragged sideways to
   move its window through the source. The block does not move — so the cursor
   has to carry the whole promise, and the state has to show without a legend:
   a hairline underscore means "placed by hand", amber means "placed past the
   rest, this cuts mid-move". */
/* #304 (Mark, 3 August 2026: "Right now the double arrow appears for slipping
   a loop. If on edge to drag out, needs a different icon?"). Two gestures, two
   cursors, each carrying the more standard meaning: slip is a GRAB — you have
   hold of the content and are sliding it under a window that does not move —
   and the edge grip is col-resize, the bar-with-arrows that everything from a
   spreadsheet to a video editor uses for "move this boundary". Sharing
   ew-resize between them was the ambiguity Mark spotted. */
/* (.can-slip and .slipping left the ruler 30 Aug 2026 — the slip gesture
   lives on the panel's own track now; the ruler's whole width scrubs.) */
/* ── #304 · the edge grips ────────────────────────────────────────────────
   A 10px strip on the right edge of every block and every card. It is INVISIBLE
   until the thing it belongs to is hovered, and then it is a hairline, not a
   handle: the rail is a picture of the edit and a row of permanent grab-tabs
   would be reading as content. The hit area is the full 10px regardless — the
   mark is the smaller thing, the way a column divider works. */
.reel-segb-grip,.reel-clip-grip{position:absolute;top:0;bottom:0;right:0;width:10px;z-index:3;
  cursor:col-resize;touch-action:none}
.reel-segb-grip::before,.reel-clip-grip::before{content:'';position:absolute;top:20%;bottom:20%;right:3px;
  width:2px;border-radius:1px;background:var(--ink);opacity:0;transition:opacity .12s ease}
.reel-segb:hover .reel-segb-grip::before,.reel-clip:hover .reel-clip-grip::before{opacity:.45}
.reel-segb-grip:hover::before,.reel-clip-grip:hover::before{opacity:.9}
/* The card's × lives at top:3px/right:3px, so the card grip starts below it —
   otherwise the grip's 10px column swallows the only control left on a card. */
.reel-clip-grip{top:24px}
/* A thin block or card is 40-odd pixels wide; a 10px grip would be a quarter of
   it and the body would have nothing left to slip or press. */
.reel-clip.t-thin .reel-clip-grip,.reel-segb.t-thin .reel-segb-grip{width:7px}
/* Whole-document cursor for the duration of the drag. The pointer leaves the
   10px grip on the very first move, and without this it would flicker back to
   the arrow while still resizing. Selection off for the same reason it is off
   on the slip track. */
body.reel-resizing{cursor:col-resize !important;user-select:none}
body.reel-resizing *{cursor:col-resize !important}
.reel-segb.has-slip::after{content:'';position:absolute;left:4px;right:4px;bottom:2px;height:2px;
  border-radius:1px;background:rgba(14,14,12,.55);pointer-events:none}
.reel-segb.slipping{box-shadow:inset 0 0 0 2px var(--ink)}
.reel-segb.slip-warn::after{background:#c8791f}
.reel-segb.slip-warn{box-shadow:inset 0 0 0 1.5px #c8791f}
.reel-segb.slip-warn.slipping,.reel-segb.slip-warn.now{box-shadow:inset 0 0 0 2px #c8791f}
/* The slip MAP (Mark, 30 July 2026: "its hard to know the start and end of
   the clip") — one cycle of the loop, drawn end to end. A calm recessed field
   is the loop; the AUTHORED RESTS sit on it as soft solid pills (the
   prototype's hatch for movement was tried here and pulled the same day —
   Mark: "not a fan of the lines"); the excerpt window is a translucent wash
   between two solid ticks. Amber = the cut lands mid-move.

   1 AUGUST 2026 — it is a PANEL BLOCK now, not an overlay pinned to the
   bottom of the stage (Mark: "I think the slip bit should be in the side
   panel like you had in v2"). Three things change with it. It is in normal
   flow, so no position/left/right/bottom/z-index and no floating pill
   chrome — the field it sits in already frames it, exactly like the swatch
   grid two fields down. It takes POINTER EVENTS, because it is now the
   primary surface for the gesture rather than a readout of one happening
   somewhere else. And it uses TOKENS: on the stage the pill was forced light
   in both themes so literal #0E0E0C marks were correct, but in the panel the
   surface flips with the theme and hardcoded ink would go invisible on a dark
   one. The amber stays literal — it is a state colour, and the app has no
   token for it. */
.reel-slipmap{border-radius:9px}
/* THE NOTE UNDER THE TRACK (#308). There is no caption row above the track any
   more — the start position moved onto the option row's own value slot — so
   this is the map's only type, it is always amber, and it is empty most of the
   time. :empty collapses it rather than reserving a line for a message that is
   not there; a 6px gap that appears and disappears under a 20px track is less
   noticeable than a permanent hole in the stack. nowrap now FITS: one short
   phrase across the full 227px instead of a caption and a warning fighting for
   it (Mark, 4 Aug 2026: "the 'cuts mid move' breaks on to another line"). */
.reel-slipmap .rsm-v{margin-top:5px;
  font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:9.5px;color:#c8791f;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.reel-slipmap .rsm-v:empty{display:none}
/* grab, and pointer-events back ON: the track is the grab surface. The note
   below it is not — dragging a line of type selects it instead, which is the
   sort of small betrayal that makes a control feel broken. The cursor matches
   the ruler block's, because it is literally the same gesture at another scale
   (#304) — col-resize now means one thing only, and it isn't this. */
.reel-slipmap .rsm-track{position:relative;height:20px;border-radius:6px;overflow:hidden;
  background:var(--canvas);border:1px solid var(--border);box-sizing:border-box;
  cursor:grab;touch-action:none;user-select:none}
.reel-slipmap .rsm-track:active{cursor:grabbing}
.reel-slipmap .rsm-track:hover{border-color:var(--bmed)}
.reel-slipmap.rsm-live .rsm-track{border-color:var(--ink)}
.reel-slipmap .rsm-rest{position:absolute;top:4px;bottom:4px;border-radius:3px;background:var(--bmed);z-index:2}
/* The window is a TRANSLUCENT wash, not a solid box — at Full pace it spans
   the whole cycle and a solid fill buried the very rest story the map exists
   to tell. The in/out TICKS are the actual answer to "where does it start
   and end": solid in-point, and the out tick goes amber with the wash when
   the cut lands mid-move. */
/* The window is the LIT part of the well and the rests sit ON TOP of it,
   which is the other way round from the stage pill. There the window was a
   dark wash over a pale track and the rests showed through it; here the track
   is a recessed --canvas well inside a --paper panel, so the readable move is
   to BRIGHTEN what plays rather than shade it. The z-order is the whole
   trick: wash underneath, rest pills over it, ticks over everything — paint
   them in DOM order instead and a full-cycle window covers every rest, which
   is exactly the case Full pace produces. */
.reel-slipmap .rsm-win{position:absolute;top:0;bottom:0;background:var(--paper);z-index:1}
.reel-slipmap.rsm-warn .rsm-win{background:rgba(200,121,31,.22)}
.reel-slipmap .rsm-in,.reel-slipmap .rsm-out{position:absolute;top:0;bottom:0;width:2.5px;background:var(--ink);border-radius:1px;z-index:3}
.reel-slipmap.rsm-warn .rsm-out{background:#c8791f}
.reel-clip.hl{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)} /* 2px ring — the 1px colour swap was too quiet to spot at a distance (Mark, 30 July 2026) */
.reel-tick{position:absolute;top:0;bottom:0;width:1px;background:rgba(14,14,12,.10);pointer-events:none}
.reel-ph{position:absolute;top:0;bottom:0;width:2px;background:var(--ink);pointer-events:none}
/* compact clip cards — drag to reorder (grab cursor), no arrow buttons */
.reel-strip{flex:none;display:flex;gap:8px;padding:10px 2px 4px;overflow-x:auto;align-items:stretch}
.reel-strip[hidden]{display:none}
/* width comes from layoutRail() — beats x unit. 126px is only the fallback
   for the instant before the first measure. */
.reel-clip{flex:none;position:relative;width:126px;border:1px solid var(--border);border-radius:8px;background:var(--bg,var(--paper));
  padding:6px;cursor:grab;overflow:hidden;transition:border-color .15s, box-shadow .15s, opacity .15s;
  /* A column: PICTURE then name. It became a flex column the day the controls
     left it (#185) — with the chip row and the button row gone, a 44px fixed
     thumbnail left roughly half the card as blank paper, and a rail of mostly
     blank cards is worse than the rail that was too busy. The picture takes
     the slack instead, so a rail reads as a row of pictures.

     The HEIGHT is stated here and nowhere else (#188). `flex:1` on the
     thumbnail with no definite height on this card is content sizing wearing a
     flexbox costume: flex-basis:0% against an indefinite parent falls back to
     the item's max-content contribution, which is the <img>'s own aspect — so
     a rail of portrait captures grew to 294px and a rail of landscape ones did
     not, and adding a block changed the height of every card in the row (Mark,
     1 August 2026: "the clip cards are scaling depending on how many clips
     there are i think. they are so tall"). A card's WIDTH is its length and
     must vary; its height carries no information at all and must not. With a
     definite height here, flex-basis:0 resolves against a real number and the
     thumbnail gets exactly the leftover.

     That height is now 96px, down from 112 (Mark, 3 Aug 2026: "smaller
     cards"). The card carries a picture and a name and nothing else since
     #185, and 112 was sized when it still held a chip row and a button row
     as well. The floor is 74 — 12 of padding, a
     13px name line, the thumbnail's own 44px minimum and the 5px between them —
     so 96 keeps a 66px picture, still the tallest thing on the card and still
     comfortably above the point where a thumbnail stops reading as an image.
     Going lower would start eating the picture to save space the mode has
     plenty of. */
  display:flex;flex-direction:column;height:96px;box-sizing:border-box}
/* Three tiers, because a proportional row means narrow columns are normal, not
   an edge case. A short block must still say WHICH block it is and how long it
   holds — it just says it with fewer words. */
.reel-clip.t-mid{padding:5px}
/* A mid column has spare height and no spare width — the one place a second
   line is free. On one
   line a 77px column rendered "Captu…"; on two it gets roughly double the
   title before the ellipsis. The full title is still on hover either way.
   (Shortening a name is not the v3 failure: that one destroyed a whole field,
   dropping the beat count off the end of "03 · Motion ·". Nothing here can
   lose a field — kind and beats live in the bar above, the index in the
   thin tier.) */
.reel-clip.t-mid .reel-clip-name{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;line-height:1.25}
.reel-clip.t-thin{padding:4px}
.reel-clip.t-thin .reel-clip-th{display:none}
/* A thin column shows its INDEX on its own tint, not a truncated name. This
   is the v3 lesson applied to the horizontal rail: "03 · Motion · 6b" in a
   68px slot rendered as "03 · Motion ·", and a 26px slot renders "C…". The
   number is short enough to always be whole, and it is the one thing that
   answers "which block is this" — the name is one hover away in the title
   attribute, and the bar above still carries the beat count. */
.reel-clip.t-thin{background:var(--tint,var(--paper))}
.reel-clip.t-thin .reel-clip-name{font-size:0;text-align:center;line-height:1}
.reel-clip.t-thin .reel-clip-name::after{content:attr(data-ix);font-size:10px;font-weight:600;
  letter-spacing:.02em;color:var(--ink-soft);font-variant-numeric:tabular-nums}
/* A thin block keeps its beat count in the bar above it, where the eye is
   already reading length off the ticks — the same move the rotated rail
   needed, for the same reason. */
.reel-segb.t-thin span{display:none}
.reel-segb.t-thin i{left:0;right:0;text-align:center;transform:translateY(-50%)}
.reel-clip:active{cursor:grabbing}
.reel-clip.now{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
.reel-clip.dragging{opacity:.45}
.reel-strip{position:relative}
.reel-drop-ind{position:absolute;top:12px;bottom:6px;width:3px;border-radius:2px;background:var(--ink);pointer-events:none;z-index:3}
.reel-drop-ind[hidden]{display:none}
.reel-clip-th{position:relative;flex:1;min-height:44px;border-radius:5px;overflow:hidden;background:var(--acc-band);margin-bottom:5px;pointer-events:none}
/* FIT THE PICTURE IN, DON'T FILL THE BOX WITH A SLICE OF IT (#316. Mark, 4 Aug
   2026: "Can they 'fit' better so you can see more of the image. Plus it
   probably needs to show the middle of the image.")
   This was cover/top, which is the most destructive pair available: the block's
   picture box is a wide, short band, so cover on a portrait capture kept a
   horizontal strip about a fifth of the page, and `top` chose WHICH strip — the
   one above the fold, which on a website is the nav bar and on a padded board
   is plate. You could have three blocks on the rail showing three near-
   identical grey bands and no way to tell them apart.
   contain shows the whole capture instead. It costs the sides of the box on a
   portrait source, but that space is the card's own mount rather than a hole,
   and a small whole picture identifies a block where a large fragment does not
   — which is the only job a strip thumbnail has. The centred bake in makeThumb
   is the other half: on the long full-page strips, where nothing could be
   fitted whole and still be visible, the middle is what gets baked in. */
.reel-clip-th img{width:100%;height:100%;object-fit:contain;object-position:center;display:block}
/* A CARD STILL FILLS. Its thumbnail is not a picture placed in a frame, it is
   the frame — a flat field, a gradient, or a backdrop that runs edge to edge on
   the stage. Contained, the 16x16 field SVG would render as a colour SQUARE
   floating in the middle of the box with mount either side, which describes
   nothing that happens when the card plays. Cover is the honest one here, and
   it costs nothing to crop a field that is the same everywhere. */
.reel-clip-th.is-card img{object-fit:cover}
.reel-clip-play{position:absolute;right:4px;bottom:3px;font-size:8px;color:#fff;background:rgba(0,0,0,.55);
  border-radius:4px;padding:1px 4px;line-height:1.4;pointer-events:none}
.reel-clip-name{font-size:10.5px;font-weight:500;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}
/* The ONE control back on the card (Mark, 1 Aug: "I think the cards need an x
   button on to clear them"). This does not reopen #185 — the motion chip, the
   direction, the length stepper and the rest stay in the panel. Removing a
   block is a rail gesture: you are looking at the row, you want that one gone,
   and going to the panel to do it means selecting the thing first, which is a
   move you only make in order to undo it. Quiet until you are on the card, so
   a rail at rest is still a row of pictures. Never on the thin tier, where the
   card is 40-odd pixels of tint and a number and a cross would BE the card. */
.reel-clip-x{position:absolute;top:3px;right:3px;z-index:2;width:18px;height:18px;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--sans);font-size:13px;line-height:1;color:var(--paper);
  background:rgba(0,0,0,.5);border:0;border-radius:50%;padding:0;cursor:pointer;
  opacity:0;pointer-events:none;transition:opacity .12s ease,background .12s ease}
.reel-clip:hover .reel-clip-x,
.reel-clip-x:focus-visible{opacity:1;pointer-events:auto}
.reel-clip-x:hover{background:rgba(0,0,0,.78)}
.reel-clip.t-thin .reel-clip-x{display:none}
/* THE CARD CARRIES NO CONTROLS (Mark, 1 August 2026: "your mockup had all the
   clip controls in the side bar?"). .reel-clip-row, .reel-clip-kind,
   .reel-clip-info and .reel-clip-acts used to style a motion chip, a beat
   readout and a row of square icon buttons riding on every column; the motion
   dropdown, the slide direction, the length stepper and Remove all live in the
   side panel now, where his original instruction put them: "All info and
   controls for clips, blanks, text will be in the side panel." Those rules are
   deleted rather than left orphaned — a stylesheet that still describes a
   control nobody renders is how the next person rebuilds it by accident.

   It also retires the tier problem those rules created: the mid and thin tiers
   had to HIDE the chip and the buttons to fit, so a short block simply had no
   controls at all. A column is a picture, a name and a width you can read as
   length, at every width. */
/* The empty stage's "Add captures" wears the SAME dashed card as the strip's
   trailing one (Mark, 28 July 2026) — two buttons that do the identical thing
   shouldn't look like different offers. */
.reel-add{flex:none;width:92px;border:1.5px dashed var(--border);border-radius:8px;background:transparent;cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;font-family:var(--sans);
  font-size:10.5px;color:var(--ink-mute);min-height:80px}
.reel-add span{font-size:17px;line-height:1;color:var(--ink50)}
.reel-add:hover{border-color:var(--ink50);color:var(--ink)}

/* ==== HOME V3 BEGIN ==== */
/* The home redesign (Mark + Molly, 29 July 2026). Every rule below is scoped
   under `.app.home-v3`, which app.js only puts on the shell when the new
   renderer runs — so deleting from this fence to HOME V3 END, and flipping
   HOME_DESIGN back to 'current' in app.js, is a complete revert with nothing
   else to unpick. Nothing above this line was edited.

   Tokens are the app's own (--paper / --ink / --ink-mute / --rule-faint /
   --work-bg). The mock invented flat hexes and they were wrong the moment you
   clicked through to the board: the shipping model is ink-over-paper, one
   surface with tints of the SAME ink stacked on it. Light shell, tinted work
   area, sidebar one step deeper, panels back UP to paper. */

/* ---------- shell ---------- */
/* Home shipped as a full-width content page with the rail hidden. The
   redesign puts the rail back, because the rail IS the design: it lists every
   mode, which is what lets the primary button be a shutter instead of a
   second copy of the same menu. 252px matches the mock. */
.app.home-v3.mode-home .workspace {
  grid-template-columns: 252px minmax(0, 1fr);
  /* NO COLUMN GAP. The shell sets `column-gap: 0` (styles.css ~7490) and the
     rail is separated from the field by its own 1px border, not by air —
     because the rail and the field both carry a tint and the workspace behind
     them is paper, so any gap between the two shows the shell straight through
     as a white stripe running the full height (Mark, 30 July: "the main
     section on the home page has a column gap which is creating a white
     stripe. remove this"). The 40px is not lost, it moves INSIDE the field as
     padding below, so the content sits exactly where it did and only the
     stripe goes. */
  column-gap: 0;
}
.app.home-v3.mode-home .workspace > .sidebar-col { display: block; grid-column: 1; }
/* The shell gives .main 24px of breathing room at the top and home has no
   reason to want less — 4px read as the content crashing into the topbar
   (Mark, 29 July: "top gap needs fixing"). 20px here sits the hero's top
   edge a hair above the rail's first label, which is the alignment the eye
   actually checks.
   The sides are left to the shell's own 44px, which means the content is
   CENTRED IN THE FIELD. The first attempt at killing the white stripe carried
   the deleted 40px gap over into padding-left so nothing would move — but that
   left 84px of air on the left against 44px on the right, and once the stripe
   was gone there was nothing to disguise it (Mark, 30 July: "its not centred
   though. the gap on the left is bigger than on the right"). 44px both sides is
   also exactly what every capture panel uses (styles.css ~9826), so home stops
   being the one route that insets differently. */
.app.home-v3.mode-home .workspace > .main { grid-column: 2; padding-top: 20px; }
/* The work field takes the work tint back. `.workspace > .main` (styles.css
   ~7515) sets `background: var(--work-bg)`, and the shell then overrides it to
   paper for every non-capture route (9668) — right for Library, Guide and
   Settings, which are documents, and right for the OLD home, which ran with
   this whole sidebar hidden. It is wrong for this one: the rail is back, the
   rail carries a tint on the Mix and Warm-grey looks (styles.css 156), and a
   grey rail against a white field is the only place in the app where those two
   disagree — every capture panel already pairs the tinted rail with a tinted
   main (Mark, 29 July: "seems a bit odd that the side bar is a grey but the
   header and main section are just white"). --work-bg is per-look, so this is
   a whisper of ink on Paper and the warm grey on Mix/Warm, and the hero and
   cards go back to being paper FLOATING on a field instead of white on white.
   The topbar stays paper on purpose — it's the shell, not the work. */
.app.home-v3.mode-home .workspace > .main { background: var(--work-bg); }
.app.home-v3.mode-home .workspace > .preview-panel,
.app.home-v3.mode-home .caps-strip { display: none; }
.app.sidebar-collapsed.home-v3.mode-home .workspace { grid-template-columns: 252px minmax(0, 1fr); }
/* The launchpad's 1080px cap was sized for a page with no rail beside it. */
.app.home-v3 .home { max-width: none; padding-bottom: 0; }
/* The shipping markup stays in the DOM and is hidden, not removed — 'current'
   comes straight back with nothing to rebuild. The .main keeps its own
   --work-bg wash: paper shell, tinted middle, panels back UP to paper is the
   hierarchy the rest of the app uses, and home has no business breaking it. */
.app.home-v3 .home > :not(.hv3) { display: none !important; }

/* ---------- rail ---------- */
/* THE RAIL CANNOT SCROLL (Mark, 29 July: "The sidebar cannot scroll. if it
   cant fit, then remove the library from here"). The app opens in a browser
   tab, so this column gets the viewport's height and nothing more — every
   measurement below is a budget, not a preference. Nine mode rows, three
   group labels and the Pro card come to ~597px; the box gets ~704px at a
   780px window, which leaves room for a shorter one.
   Two things leave to buy that room, and neither is a loss:
   · the collapse toggle, because home has nothing to collapse INTO — the
     rail IS the layout here, and collapsing it breaks the shutter button's
     whole premise (it fires the mode the rail has selected);
   · the light/dark + Paper/Mix/Warm-grey pills, which today are ALREADY
     unreachable from home — the shipping build hides this entire sidebar on
     the home route. Hiding them here restores the status quo rather than
     taking anything away. They stay on every capture panel. */
/* NO max-height here. The shell already gives .sidebar-col the full column
   height and .sidebar `height: 100%` to fill it; capping the rail at
   100vh-100px left the tint stopping ~40px short of the window and a band of
   bare paper under it (Mark, 29 July: "why is there a white space at bottom
   on left?"). The scroll container is .sidebar-col, not .sidebar — so the
   no-scroll rule is checked against that, and the budget below is what keeps
   it honest. */
.app.home-v3.mode-home .sidebar { padding: 16px 10px 20px; }
.app.home-v3.mode-home .sidebar > .sidebar-head,
.app.home-v3.mode-home .sidebar > .sidebar-utils { display: none; }
/* The rail is shown by the ROUTE, not by an .is-active flag. renderHomeV3Rail
   used to claim is-active for its own group and clear everyone else's, which
   collided with showTab() on the way out of home and left the rail standing in
   Settings and Guide (Mark, 29 July). So: on the home route, show the home
   group and hide every other one whatever its flag says. Off the home route
   the base rule (.sidebar-group { display: none }) hides the rail on its own,
   because nothing ever puts .is-active on it. showHome() already clears all
   the flags, so no other group can leak in the other direction either. */
.app.home-v3.mode-home .sidebar > .sidebar-group[data-group="home"] { display: block; }
.app.home-v3.mode-home .sidebar > .sidebar-group:not([data-group="home"]) { display: none; }
.app.home-v3 .hv3-item { padding: 7px 8px; }
/* #295 — the home rail's territory heads. Hidden under the permanent left nav
   today, but fixed with the others: a rule applied to three of four places is
   how this came back in the first place. */
.app.home-v3 .hv3-grp {
  font-family: var(--sans); font-size: 10.5px; font-weight: 500; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--ink-mute);
  padding: 12px 8px 6px; display: flex; align-items: center; gap: 6px;
}
.app.home-v3 .hv3-grp:first-child { padding-top: 2px; }
/* The Pro marker sits on the GROUP, not the row — it answers "what do I get
   if I upgrade?" against the whole territory rather than repeating itself
   four times down a list. It's `.home-pro`, the app's own chip, character for
   character; only the line-height is restated because the group label's mono
   tracking would otherwise squash it. */
.app.home-v3 .hv3-grp .home-pro { margin-left: 0; line-height: 1.25; }
.app.home-v3 .hv3-item {
  display: flex; align-items: flex-start; gap: 11px; width: 100%;
  padding: 7px 8px; border-radius: 9px; cursor: pointer; text-align: left;
  background: none; border: 0; transition: background .12s ease;
}
.app.home-v3 .hv3-item:hover { background: rgba(14, 14, 12, .04); }
.app.home-v3 .hv3-item.is-on { background: rgba(14, 14, 12, .06); }
.app.home-v3 .hv3-ico { color: var(--ink); margin-top: 1px; flex: 0 0 auto; display: flex; }
.app.home-v3 .hv3-ico svg { width: 17px; height: 17px; display: block; }
.app.home-v3 .hv3-txt { min-width: 0; display: block; }
.app.home-v3 .hv3-nm { display: block; font-size: 13.5px; color: var(--ink); line-height: 1.25; }
.app.home-v3 .hv3-sb { display: block; font-size: 11.5px; color: var(--ink-mute); line-height: 1.3; margin-top: 1px; }
/* Names what you actually get. "Upgrade for advanced tools" names nothing —
   it's the shape of a value proposition with the value left out. */
.app.home-v3 .hv3-prox {
  display: block; width: 100%; text-align: left; margin-top: 12px;
  background: var(--paper); border: 1px solid var(--ink08); border-radius: 10px;
  padding: 11px 13px; cursor: pointer; transition: border-color .12s ease;
}
.app.home-v3 .hv3-prox:hover { border-color: var(--ink18); }
.app.home-v3 .hv3-prox-r { display: flex; align-items: center; justify-content: space-between; }
.app.home-v3 .hv3-prox-lab {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink); display: flex; align-items: center; gap: 7px;
}
.app.home-v3 .hv3-prox-lab .home-pro { margin-left: 0; line-height: 1.25; }
.app.home-v3 .hv3-prox-r svg { width: 15px; height: 15px; color: var(--ink-mute); flex: none; }
.app.home-v3 .hv3-prox-d { display: block; font-size: 11.5px; color: var(--ink-mute); margin-top: 5px; line-height: 1.35; }

/* ---------- main column ---------- */
.app.home-v3 .hv3 { display: flex; flex-direction: column; max-width: 1230px; padding-bottom: 24px; }
/* No .hv3-top / .hv3-newbtn any more. A second button above the hero that
   followed the rail's selection opened, for seven of the nine modes, exactly
   the panel the rail row already opens — and in the empty state it duplicated
   the hero's own label 80px higher up (Mark, 29 July: "if you click a mode in
   the nav, you get taken to that mode so not sure this button is useful").
   The rail is the launcher; the hero carries the one action. */
.app.home-v3 .hv3-pl {
  width: 19px; height: 19px; border-radius: 50%; background: var(--ink08);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink); flex: 0 0 auto;
}
.app.home-v3 .hv3-btn.is-dark .hv3-pl { background: rgba(250, 250, 250, .2); color: var(--paper); }
.app.home-v3 .hv3-pl svg { width: 11px; height: 11px; display: block; }


/* ---------- author mode (Mark's machine only) ----------
   Deliberately utilitarian: this is a tool for composing the marketing artwork,
   not part of the product, and it should never be mistaken for one. A dashed
   rule and mono labels put it visibly outside the design. */
.uia-bar {
  border: 1px dashed var(--ink32); border-radius: 12px;
  padding: 12px 14px; margin-bottom: 18px; background: var(--paper);
}
.uia-row { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.uia-row:last-of-type { margin-bottom: 0; }
.uia-lbl {
  font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em; text-transform: none;
  color: var(--ink50); margin-right: 8px; flex: none; min-width: 46px; font-weight: 500;}
.uia-slot { display: inline-flex; align-items: center; }
.uia-slot > button {
  border: 1px solid var(--bmed); background: var(--paper); color: var(--ink-soft);
  font-family: var(--sans); font-size: 12px; padding: 4px 9px; border-radius: 7px; cursor: pointer;
  text-transform: capitalize;
}
.uia-slot > button:hover { border-color: var(--ink); color: var(--ink); }
/* Set slots read as filled, so the bar doubles as the checklist of what is
   still missing before an export is worth doing. */
.uia-slot.is-set > button:first-child { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.uia-slot .uia-x {
  border-radius: 0 7px 7px 0; border-left: 0; padding: 4px 7px; font-size: 13px; line-height: 1;
}
.uia-slot.is-set > button:first-child { border-radius: 7px 0 0 7px; }
.uia-note { font-size: 11.5px; color: var(--ink50); margin-top: 10px; }
/* Settings row: the two buttons sit together on the right, like every other
   action pair in Settings. */
.lib-import-actions { display: flex; align-items: center; gap: 8px; }

/* ---------- hero ---------- */
.app.home-v3 .hv3-hero {
  position: relative; display: grid; grid-template-columns: 1fr 1.02fr; gap: 24px;
  padding: 34px 36px; align-items: center; min-height: 272px; margin-bottom: 20px;
  background: var(--paper); border: 1px solid var(--ink08); border-radius: 16px;
}
/* The page-heading scale, and it is not home's own. Settings and Guide already
   set one (.g2-eyebrow / .g2-title / .g2-lede, styles.css ~10875) and home was
   running 6px larger at a heavier weight — so the first screen of the app
   didn't match the two screens either side of it (Mark, 29 July: "lets make
   the main big heading like we have it in settings and guide … lets make
   everything consistent!"). These four rules mirror those three exactly, with
   one deliberate difference: the lede keeps home's narrower 370px measure,
   because it sits beside the hero art rather than across a full-width page.
   If the g2 scale moves, move these with it. */
.app.home-v3 .hv3-h1 {
  font-family: var(--sans); font-size: 30px; font-weight: 500; color: var(--ink);
  letter-spacing: -.015em; line-height: 1.12; margin: 0 0 14px;
}
.app.home-v3 .hv3-sub { font-size: 15.5px; line-height: 1.55; color: var(--ink50); max-width: 370px; margin: 0 0 22px; }
.app.home-v3 .hv3-lbl {
  font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em;
  text-transform: none; color: var(--ink50); display: block; margin-bottom: 12px; font-weight: 500;}
.app.home-v3 .hv3-lbl.is-quiet { opacity: .7; margin-bottom: 0; }
.app.home-v3 .hv3-meta { font-size: 13.5px; line-height: 1.55; color: var(--ink50); margin: 0 0 20px; }
/* Two steps you can see the end of is a tour; an unnumbered sequence is an
   ambush. The spotlight already counts "1 of 2" — the hero should say the
   same thing before the lights go down. */
.app.home-v3 .hv3-step {
  display: inline-flex; align-items: center; gap: 8px; margin-bottom: 12px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em; color: var(--ink50);
}
.app.home-v3 .hv3-dot { width: 14px; height: 2px; border-radius: 2px; background: var(--rule-faint); display: block; }
.app.home-v3 .hv3-dot + .hv3-dot { margin-left: -4px; }
.app.home-v3 .hv3-dot.is-on { background: var(--ink); }
.app.home-v3 .hv3-hsrc { font-size: 12.5px; color: var(--ink-mute); margin: -10px 0 20px; }
.app.home-v3 .hv3-hsrc b { color: var(--ink-soft); font-weight: 500; }
.app.home-v3 .hv3-alt {
  color: var(--ink); border: 0; border-bottom: 1px solid var(--rule-faint);
  background: none; margin-left: 7px; cursor: pointer; font-size: 12.5px; padding: 0;
}
.app.home-v3 .hv3-cta { display: flex; gap: 10px; flex-wrap: wrap; }
.app.home-v3 .hv3-btn {
  display: inline-flex; align-items: center; gap: 9px; font-size: 13.5px; font-weight: 500;
  padding: 11px 17px; border-radius: 9px; cursor: pointer; white-space: nowrap; border: 0;
}
.app.home-v3 .hv3-btn.is-dark { background: var(--ink); color: var(--paper); }
.app.home-v3 .hv3-btn.is-ghost { background: var(--paper); color: var(--ink); border: 1px solid var(--ink18); }
/* An intro you can't leave is a wall, and this one interrupts the home page
   rather than living on its own screen. */
.app.home-v3 .hv3-x {
  position: absolute; right: 14px; top: 12px; width: 26px; height: 26px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center; color: var(--ink-mute);
  font-size: 13px; line-height: 1; cursor: pointer; background: none; border: 0;
}
.app.home-v3 .hv3-x:hover { background: rgba(14, 14, 12, .05); color: var(--ink); }

/* ---------- hero art ---------- */
/* Real thumbnails when there are any. Loey's promise is "your work will look
   beautiful", which a grey rectangle cannot make — so the stage shows the
   captured work, not an abstraction of it. */
.app.home-v3 .hv3-art-shots { position: relative; height: 236px; }
.app.home-v3 .hv3-hshot {
  position: absolute; background: var(--paper); border: 1px solid var(--ink08);
  border-radius: 9px; overflow: hidden;
  box-shadow: 0 2px 5px rgba(14, 14, 12, .05), 0 16px 38px rgba(14, 14, 12, .09);
}
.app.home-v3 .hv3-hshot img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.app.home-v3 .hv3-hshot.a { width: 200px; height: 158px; left: 50%; top: 50%; transform: translate(-50%, -50%) rotate(-2deg); z-index: 3; }
.app.home-v3 .hv3-hshot.b { width: 134px; height: 110px; right: 26px; top: 30px; transform: rotate(3deg); z-index: 2; }
.app.home-v3 .hv3-hshot.c { width: 100px; height: 82px; left: 24px; bottom: 22px; transform: rotate(-5deg); z-index: 1; }
.app.home-v3 .hv3-hplay,
.app.home-v3 .hv3-art-stack b {
  width: 42px; height: 42px; border-radius: 50%; background: var(--ink); color: var(--paper);
  display: flex; align-items: center; justify-content: center; position: absolute;
  left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 4;
  box-shadow: 0 1px 2px rgba(14, 14, 12, .05), 0 4px 12px rgba(14, 14, 12, .05);
}
.app.home-v3 .hv3-hplay::after {
  content: ""; position: absolute; inset: -9px; border-radius: 50%;
  border: 1px solid rgba(14, 14, 12, .13);
}
/* Dashed outlines, not grey fills. A filled placeholder looks like content
   that failed to load; an outline looks like a slot waiting to be filled,
   which is the true statement. */
.app.home-v3 .hv3-art-slots { position: relative; height: 236px; display: flex; align-items: center; justify-content: center; }
.app.home-v3 .hv3-slot {
  position: absolute; border: 1.5px dashed var(--rule-faint); border-radius: 10px;
  background: rgba(250, 250, 250, .5);
}
.app.home-v3 .hv3-slot.a { width: 186px; height: 150px; transform: rotate(-2deg); z-index: 2; display: flex; align-items: center; justify-content: center; }
.app.home-v3 .hv3-slot.b { width: 126px; height: 104px; right: 44px; top: 40px; transform: rotate(2.5deg); opacity: .55; }
.app.home-v3 .hv3-slot.c { width: 92px; height: 76px; left: 38px; bottom: 30px; transform: rotate(-4deg); opacity: .34; }
.app.home-v3 .hv3-slot-plus {
  width: 38px; height: 38px; border-radius: 50%; background: var(--ink08); color: var(--ink-soft);
  display: flex; align-items: center; justify-content: center;
}
.app.home-v3 .hv3-slot-plus svg { width: 16px; height: 16px; }

/* ---------- Layouts + Edits ---------- */
/* Doors, and they come before the shelf: the work flows open-the-layout →
   pull-captures-in, not capture → open → add (Mark, 29 July). */
.app.home-v3 .hv3-feat { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 24px; }
.app.home-v3 .hv3-card {
  background: var(--paper); border: 1px solid var(--ink08); border-radius: 16px;
  padding: 24px 26px; display: grid; grid-template-columns: 1fr .8fr; gap: 14px;
  align-items: stretch; min-height: 206px; text-align: left; cursor: pointer;
  transition: border-color .12s ease;
}
.app.home-v3 .hv3-card:hover { border-color: var(--ink18); }
.app.home-v3 .hv3-card-in { display: flex; flex-direction: column; height: 100%; min-width: 0; }
.app.home-v3 .hv3-card-hd { display: flex; align-items: center; gap: 10px; margin-bottom: 11px; }
.app.home-v3 .hv3-card-ico {
  width: 33px; height: 33px; border-radius: 9px; background: var(--ink08);
  display: flex; align-items: center; justify-content: center; color: var(--ink); flex: 0 0 auto;
}
.app.home-v3 .hv3-card-ico svg { width: 17px; height: 17px; }
/* 500, like the hero heading and every other title on this page. 600 was the
   only weight above 500 left in the fence (Mark, 29 July). */
.app.home-v3 .hv3-card-t { font-size: 19px; font-weight: 500; color: var(--ink); letter-spacing: -.015em; }
.app.home-v3 .hv3-card-hd .home-pro { margin-left: 0; line-height: 1.25; }
.app.home-v3 .hv3-card-lead { font-size: 14px; color: var(--ink); font-weight: 500; margin-bottom: 6px; }
.app.home-v3 .hv3-card-d { font-size: 12.5px; color: var(--ink-mute); line-height: 1.5; }
/* margin-top:auto matters: without it the two Explore links sit at different
   heights because Edits' description runs a line longer, and a pair that
   doesn't line up at the bottom reads as a bug rather than a layout. */
.app.home-v3 .hv3-card-go {
  margin-top: auto; padding-top: 16px; font-size: 12.5px; color: var(--ink);
  display: inline-flex; align-items: center; gap: 5px;
}
/* 11px, gap 5 — the same chevron at the same size as "View library ›" below.
   These were 13px arrows; a chevron reads heavier at the same box, and three
   forward links on one page should be one link. */
.app.home-v3 .hv3-card-go svg { width: 11px; height: 11px; }
.app.home-v3 .hv3-art { position: relative; align-self: center; height: 152px; width: 100%; }
/* ── The returning hero's artwork / slider (7 Aug 2026) ───────────────────
   Same 236px art column as every other hero state, so switching between them
   never moves the copy beside it. Slides are stacked and cross-faded rather
   than translated: one compositor property, no layout, nothing to reflow. */
.app.home-v3 .hv3-art-media-hero { position: relative; height: 236px; display: flex; align-items: center; justify-content: center; }
.app.home-v3 .hv3-slides {
  position: relative; display: block; height: 190px; aspect-ratio: 16 / 9;
  background: var(--ink08); border-radius: 10px; overflow: hidden;
}
.app.home-v3 .hv3-slide {
  position: absolute; inset: 0; opacity: 0; transition: opacity .5s ease;
  pointer-events: none;
}
.app.home-v3 .hv3-slide.is-on { opacity: 1; }
.app.home-v3 .hv3-slide img,
.app.home-v3 .hv3-slide video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Dots sit UNDER the frame, not on it — over a photograph they need a scrim to
   stay legible, and a scrim on a hero is a lot of furniture for two dots. */
.app.home-v3 .hv3-dots {
  position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%);
  display: flex; gap: 6px;
}
.app.home-v3 .hv3-dot {
  width: 6px; height: 6px; padding: 0; border: 0; border-radius: 50%;
  background: var(--ink18); cursor: pointer; transition: background .15s ease;
}
.app.home-v3 .hv3-dot:hover { background: var(--ink32); }
.app.home-v3 .hv3-dot.is-on { background: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .app.home-v3 .hv3-slide { transition: none; }
}
/* ── Guide card screenshots (7 Aug 2026) ──────────────────────────────────
   The image fills the same 96px band the drawn bars occupy, so a gallery of
   mixed real-and-drawn cards still lines up while the set is being filled in. */
.g2-card-thumb.is-shot { padding: 0; background: var(--ink08); }
/* FIT, not fill (Mark, 9 Aug 2026: "guide cards image should fit not fill").
   Cover in a square crops a wide desktop shot top and bottom and a tall page
   everywhere but its header — which is the same complaint the square was meant
   to answer, one shape further on. Contain shows the whole capture and mats
   what is left; centred, because pushing a letterboxed wide shot to the top of
   the square puts all the mat in one place and reads as a mistake. */
.g2-card-img { display: block; width: 100%; height: 100%; object-fit: contain; object-position: center; }

/* ── Step 1's source plate (7 Aug 2026) ───────────────────────────────────
   The page they are ON, drawn from what the app already knows — no load, no
   capture, no network. Same 16:9 window and the same furniture as the scroll
   hero below it, so step 1 → step 2 is one frame gaining a picture rather than
   two different treatments. When the live frame arrives it goes inside
   .hv3-plate-body and nothing else here changes. */
.app.home-v3 .hv3-art-plate { position: relative; height: 236px; display: flex; align-items: center; justify-content: center; }
.app.home-v3 .hv3-plate {
  position: relative; display: flex; flex-direction: column;
  height: 190px; aspect-ratio: 16 / 9; overflow: hidden;
  background: var(--paper); border: 1px solid var(--ink08);
  border-radius: 9px;
  box-shadow: 0 2px 5px rgba(14, 14, 12, .05), 0 16px 38px rgba(14, 14, 12, .09);
}
/* The faux chrome is .lab-chrome's grammar at hero scale — three dots, then the
   address — so the preview and the home plate read as one product. */
.app.home-v3 .hv3-plate-bar {
  display: flex; align-items: center; gap: 5px; flex: none;
  padding: 7px 10px; border-bottom: 1px solid var(--ink08);
}
.app.home-v3 .hv3-plate-bar i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--ink18); flex: none;
}
/* 16px and no bigger: a favicon scaled up to fill a hero is soft, and soft
   artwork is the wrong advertisement for a capture tool. */
.app.home-v3 .hv3-plate-fav {
  width: 14px; height: 14px; margin-left: 7px; flex: none; border-radius: 3px;
  background-size: contain; background-repeat: no-repeat; background-position: center;
}
.app.home-v3 .hv3-plate-host {
  font-family: var(--mono); font-size: 10px; color: var(--ink50);
  margin-left: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app.home-v3 .hv3-plate-body {
  flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: center;
  gap: 12px; padding: 0 22px;
}
.app.home-v3 .hv3-plate-title {
  font-family: var(--sans); font-size: 15px; font-weight: 500; line-height: 1.25;
  letter-spacing: -0.01em; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* A hint of a page under the title — enough to read as content, quiet enough
   not to pretend it is a render of their site. */
.app.home-v3 .hv3-plate-skel { display: flex; flex-direction: column; gap: 7px; }
.app.home-v3 .hv3-plate-skel i { height: 6px; border-radius: 3px; background: var(--ink08); }
.app.home-v3 .hv3-plate-skel i:nth-child(1) { width: 78%; }
.app.home-v3 .hv3-plate-skel i:nth-child(2) { width: 92%; }
.app.home-v3 .hv3-plate-skel i:nth-child(3) { width: 54%; }

/* ── Step 2's scrolling hero (7 Aug 2026) ─────────────────────────────────
   A 16:9 window with the full-page capture cover-cropped into it, panned by
   object-position from hv3HeroScroll — the same mechanism the board's page
   scroll uses, so what is previewed here is what the button hands over. The
   window carries the shot's own furniture (paper, hairline, the same 9px
   corner and shadow as .hv3-hshot) so it still reads as a capture rather than
   as a video player dropped on the page. */
.app.home-v3 .hv3-art-scroll { position: relative; height: 236px; display: flex; align-items: center; justify-content: center; }
.app.home-v3 .hv3-scrollwin {
  position: relative; display: block; height: 190px; aspect-ratio: 16 / 9;
  background: var(--paper); border: 1px solid var(--ink08);
  border-radius: 9px; overflow: hidden;
  box-shadow: 0 2px 5px rgba(14, 14, 12, .05), 0 16px 38px rgba(14, 14, 12, .09);
}
/* object-position is the ONE thing the driver writes; everything else is fixed
   here so a rAF never touches layout. */
.app.home-v3 .hv3-scrollwin img {
  width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: 50% 0%;
}
/* ── The two feature cards' image / video slot (7 Aug 2026) ───────────────
   Same 152px box as the drawn mosaic and stack it replaces, so swapping one
   for the other never changes the card's height. The aspect-ratio comes from
   the manifest's declared w/h as an inline style, which is what stops the row
   reflowing when a remote image lands. */
.app.home-v3 .hv3-art-media {
  display: block; height: 152px; margin: 0 auto; overflow: hidden;
  border-radius: 10px; background: var(--ink08);
}
.app.home-v3 .hv3-cardimg,
.app.home-v3 .hv3-cardvid {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: center;
}
/* Reduced motion is handled in hv3CardArt, not here: hiding the <video> in CSS
   would leave an empty box, because a poster is an attribute of the element and
   not a background. The JS renders the poster as a plain <img> instead. */
/* A layout is a COMPOSITION — things arranged in a plane, so: perspective.
   An edit is a SEQUENCE — things one after another in time, so: a receding
   stack. Give them the same art and the two products look like one product. */
.app.home-v3 .hv3-art-mosaic {
  display: grid; grid-template-columns: 52px 40px; grid-auto-rows: 34px; gap: 8px;
  align-content: center; justify-content: center; height: 152px;
  transform: perspective(640px) rotateY(-21deg) rotateX(5deg) rotateZ(-2deg);
}
.app.home-v3 .hv3-art-mosaic i {
  background: var(--paper); border: 1px solid var(--ink08); border-radius: 4px;
  box-shadow: 0 1px 2px rgba(14, 14, 12, .05), 0 4px 12px rgba(14, 14, 12, .05);
  display: block;
}
.app.home-v3 .hv3-art-mosaic i:nth-child(3) { background: rgba(14, 14, 12, .1); }
.app.home-v3 .hv3-art-mosaic i:nth-child(4) { background: rgba(14, 14, 12, .05); }
.app.home-v3 .hv3-art-stack { position: relative; }
.app.home-v3 .hv3-art-stack i {
  position: absolute; background: var(--paper); border: 1px solid var(--ink08);
  border-radius: 6px; box-shadow: 0 1px 2px rgba(14, 14, 12, .05), 0 4px 12px rgba(14, 14, 12, .05);
  display: block; left: 50%; top: 50%;
}
.app.home-v3 .hv3-art-stack i:nth-child(1) { width: 118px; height: 82px; transform: translate(-50%, -50%) translate(-26px, -12px) scale(.86); opacity: .5; }
.app.home-v3 .hv3-art-stack i:nth-child(2) { width: 118px; height: 82px; transform: translate(-50%, -50%) translate(-13px, -6px) scale(.93); opacity: .75; }
.app.home-v3 .hv3-art-stack i:nth-child(3) { width: 118px; height: 82px; transform: translate(-50%, -50%); }
.app.home-v3 .hv3-art-stack b { width: 36px; height: 36px; }
.app.home-v3 .hv3-art-stack b svg,
.app.home-v3 .hv3-hplay svg { width: 12px; height: 12px; }

/* ---------- recents ---------- */
.app.home-v3 .hv3-caps-wrap { margin-bottom: 26px; }
.app.home-v3 .hv3-caps-hd { display: flex; align-items: baseline; justify-content: space-between; margin: 2px 2px 12px; }
.app.home-v3 .hv3-caps-hd .hv3-lbl { margin-bottom: 0; }
.app.home-v3 .hv3-link {
  font-size: 12.5px; color: var(--ink); display: inline-flex; align-items: center; gap: 5px;
  background: none; border: 0; cursor: pointer; padding: 0;
}
.app.home-v3 .hv3-link svg { width: 11px; height: 11px; }
.app.home-v3 .hv3-caps { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; }
.app.home-v3 .hv3-cap { cursor: pointer; text-align: left; background: none; border: 0; padding: 0; display: block; min-width: 0; }
.app.home-v3 .hv3-shot {
  display: block; position: relative; aspect-ratio: 5 / 4; border: 1px solid var(--ink08);
  border-radius: 10px; overflow: hidden; background: var(--paper); transition: border-color .12s ease;
}
.app.home-v3 .hv3-cap:hover .hv3-shot { border-color: var(--ink18); }
.app.home-v3 .hv3-shot img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.app.home-v3 .hv3-shot.is-tall img { height: auto; object-fit: cover; object-position: top center; }
.app.home-v3 .hv3-vid {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 26px; height: 26px; border-radius: 50%; background: rgba(14, 14, 12, .55);
}
.app.home-v3 .hv3-vid::after {
  content: ""; position: absolute; left: 10px; top: 8px;
  border-left: 8px solid #FAFAFA; border-top: 5px solid transparent; border-bottom: 5px solid transparent;
}
.app.home-v3 .hv3-cap-t { display: block; font-size: 12px; color: var(--ink); line-height: 1.3; padding-top: 9px; }
.app.home-v3 .hv3-cap-m { display: block; font-size: 11.5px; color: var(--ink-mute); line-height: 1.3; margin-top: 1px; }
/* Shallower than a real thumbnail and fading to the right. Five equal dashed
   boxes is a wall of absence; a ramp reads as a row that fills from the left,
   which is what will actually happen. */
.app.home-v3 .hv3-caps.is-ghost i { aspect-ratio: 16 / 7; border: 1.5px dashed var(--rule-faint); border-radius: 10px; display: block; }
.app.home-v3 .hv3-caps.is-ghost i:nth-child(2) { opacity: .7; }
.app.home-v3 .hv3-caps.is-ghost i:nth-child(3) { opacity: .46; }
.app.home-v3 .hv3-caps.is-ghost i:nth-child(4) { opacity: .28; }
.app.home-v3 .hv3-caps.is-ghost i:nth-child(5) { opacity: .14; }

/* ---------- help ---------- */
/* ── ON ANY TAB, ON HOME (28 Aug 2026) ─────────────────────────────────────
   Mark: "these are handy but i feel would not get found out about."

   One card, three rows, and the rows are the DOORS' anatomy at v3's scale:
   keycap left, act over explanation right, left-aligned. Same numbers the door
   settled on the same day — 12px gutter, 2px between the two lines — so the
   thing you read on home is the thing you meet on the door. Three across on a
   wide window, stacking under one; hairlines between so it reads as a list of
   three keys rather than a paragraph of pairs. */
.app.home-v3 .hv3-keys-wrap { margin-bottom: 26px; }
/* Recent captures' header is label-left / link-right, so it is space-between.
   This one is a label and its own aside — they belong together, at the left. */
.app.home-v3 .hv3-keys-wrap .hv3-caps-hd { justify-content: flex-start; gap: 7px; }
.app.home-v3 .hv3-keys {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  background: var(--paper); border: 1px solid var(--ink08); border-radius: 16px;
  padding: 4px;
}
.app.home-v3 .hv3-key { display: flex; align-items: center; gap: 12px; padding: 13px 16px; min-width: 0; }
.app.home-v3 .hv3-key + .hv3-key { border-left: 1px solid var(--ink08); }
.app.home-v3 .hv3-key kbd {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em; white-space: nowrap; flex: none;
  border: 1px solid var(--bmed); border-radius: 8px; padding: 8px 11px;
  color: var(--ink); background: var(--paper);
  display: inline-flex; align-items: center; gap: 4px;
}
/* The modifier SYMBOLS are the pill's face — the mono font draws ⌥ and ⇧ small,
   so scaling the whole pill only makes the word shout. The door learned this on
   27 Aug over three passes; home inherits the answer rather than the argument. */
.app.home-v3 .hv3-key-t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.app.home-v3 .hv3-key-t b { font-size: 12.5px; font-weight: 500; color: var(--ink); }
.app.home-v3 .hv3-key-t i { font-style: normal; font-size: 11.5px; line-height: 1.45; color: var(--ink50); }
@media (max-width: 1080px) {
  .app.home-v3 .hv3-keys { grid-template-columns: 1fr; }
  .app.home-v3 .hv3-key + .hv3-key { border-left: 0; border-top: 1px solid var(--ink08); }
}
.app.home-v3 .hv3-help {
  display: flex; align-items: center; gap: 15px; padding: 17px 22px; width: 100%;
  background: var(--paper); border: 1px solid var(--ink08); border-radius: 16px;
  text-align: left; cursor: pointer; transition: border-color .12s ease;
}
.app.home-v3 .hv3-help:hover { border-color: var(--ink18); }
.app.home-v3 .hv3-help-ico {
  width: 38px; height: 38px; border-radius: 10px; background: var(--ink08);
  display: flex; align-items: center; justify-content: center; color: var(--ink); flex: 0 0 auto;
}
.app.home-v3 .hv3-help-ico svg { width: 17px; height: 17px; }
.app.home-v3 .hv3-help-t { flex: 1; min-width: 0; }
.app.home-v3 .hv3-help-t b { display: block; font-size: 14px; color: var(--ink); font-weight: 500; }
.app.home-v3 .hv3-help-t i { display: block; font-size: 12.5px; color: var(--ink-mute); margin-top: 1px; font-style: normal; }
.app.home-v3 .hv3-help-go { font-size: 12.5px; color: var(--ink); display: inline-flex; align-items: center; gap: 5px; flex: none; }
.app.home-v3 .hv3-help-go svg { width: 11px; height: 11px; }

/* ---------- narrow ---------- */
@media (max-width: 1160px) {
  .app.home-v3 .hv3-hero { grid-template-columns: 1fr; }
  .app.home-v3 .hv3-art-shots, .app.home-v3 .hv3-art-slots { display: none; }
  .app.home-v3 .hv3-feat { grid-template-columns: 1fr; }
  .app.home-v3 .hv3-card { grid-template-columns: 1fr; }
  .app.home-v3 .hv3-card .hv3-art { display: none; }
  .app.home-v3 .hv3-caps { grid-template-columns: repeat(3, 1fr); }
}
/* ==== HOME V3 END ==== */

/* ==== HOME GRID BEGIN ====
   Mark, 8 Aug 2026: "we dont want it like they are boxes on a canvas. the
   sections are just blocks, edge to edge. grid. no separate floating boxes."

   So: ONE surface. Every rule below either removes card chrome that v3 put on
   (radius, border, background, gap, shadow) or draws a hairline where a card
   edge used to be. Nothing here floats, and nothing has a corner radius.

   Scoped under `.app.home-grid`, which only exists alongside `.home-v3` — grid
   is a layer on v3, not a replacement (see HOME_DESIGN in app.js). Deleting
   from this fence to HOME GRID END and dropping the 'grid' option is the whole
   revert.
   ─────────────────────────────────────────────────────────────────────────── */

/* Flush: every gutter above the grid has to go, or it is a canvas with a board
   on it again. `.main` is the real culprit — it carries `padding: 20px 44px
   32px` for every mode's stage, which is right everywhere except here.
   `.mode-home` scopes it: `.home-grid` alone stays on `.app` after you navigate
   away, so without that guard the next panel would lose its gutters too. */
.app.home-grid.mode-home .main { padding: 0 !important; }
.app.home-grid .panel[data-panel="home"],
.app.home-grid #homeRoot,
.app.home-grid .home { padding: 0 !important; margin: 0 !important; max-width: none !important; }

/* ONE grid for the whole screen, not a column of bands beside a column of rail
   sections. The rail's sections used to be laid out independently, so "Latest
   sites" began wherever the captures above it happened to end — a few pixels
   off the mode row, which is exactly the kind of near-miss that reads as a
   mistake (Mark, 9 Aug 2026: "align the top of latest sites with the top of the
   mode row. they are a bit off so looks odd").

   `display: contents` on .hg-main lifts the bands into this grid, and the rail
   takes `grid-template-rows: subgrid` so its sections snap to the SAME row
   lines the bands define. Alignment then can't drift with content: it isn't
   matched, it's shared. Rows are named by position so an absent author bar
   simply collapses row 1 to zero. */
.app.home-grid .hv3.hg-shell {
  display: grid; grid-template-columns: minmax(0, 1fr) 232px;
  /* COMPOSE takes the slack, rather than a trailing filler row. A filler row
     made the grid reach the bottom of the stage, but it did it by leaving a
     band of empty paper under the last one — technically full height, visibly
     a gap (Mark, 9 Aug 2026, twice: "seems to be a bit of a gap at the very
     bottom" → "use the full height of the page. layouts and sequence sections
     needs a bit more breathing space"). Giving the slack to the compose band
     answers both at once: no empty strip, and the two cells that were tightest
     get the room.
     Rows: author bar, hero, modes, COMPOSE, help. */
  grid-template-rows: auto auto auto 1fr auto;
  gap: 0; padding: 0; margin: 0; max-width: none; background: var(--paper);
  min-height: 100%;
}
/* …which only means anything if the boxes between .main and the grid pass the
   height down. Scoped to the home panel: every other mode's stage sizes to its
   own content. */
.app.home-grid.mode-home .main-inner,
.app.home-grid.mode-home .panel[data-panel="home"],
.app.home-grid.mode-home .home { height: 100%; min-height: 100%; }
.app.home-grid .hg-main { display: contents; }
.app.home-grid .hg-authorbar { grid-column: 1 / -1; grid-row: 1; }
.app.home-grid .hg-hero { grid-column: 1; grid-row: 2; }
.app.home-grid .hg-modes { grid-column: 1; grid-row: 3; }
.app.home-grid .hg-compose { grid-column: 1; grid-row: 4; }
/* ── ON ANY TAB, AS A BAND (28 Aug 2026) ──────────────────────────────────
   Same block as the card home, stripped of its card: in the grid a band IS the
   block and the hairline IS the separator, so the border, the radius and the
   inner padding all come off and the three keys become cells with the compose
   band's own 44px lead-in. Row 5; the guide card moves down to 6. */
.app.home-grid .hg-keys { grid-column: 1; grid-row: 5; padding: 0; margin-bottom: 0; }
.app.home-grid .hg-keys .hv3-caps-hd { margin: 0; padding: 18px 44px 13px; }
.app.home-grid .hg-keys .hv3-keys {
  border: 0; border-radius: 0; background: transparent; padding: 0;
  border-top: 1px solid var(--ink08);
}
.app.home-grid .hg-keys .hv3-key { padding: 16px 28px; }
.app.home-grid .hg-keys .hv3-key:first-child { padding-left: 44px; }
.app.home-grid .hg-keys .hv3-key + .hv3-key { border-left: 1px solid var(--ink08); }
.app.home-grid .hg-help { grid-column: 1; grid-row: 6; }
/* A band is a block, not a card. The rule IS the separator. */
.app.home-grid .hg-band {
  background: transparent; border: 0; border-radius: 0; box-shadow: none;
  margin: 0; border-bottom: 1px solid var(--ink08);
}
.app.home-grid .hg-hd {
  padding: 0 0 14px; display: flex; align-items: baseline;
  justify-content: space-between; gap: 12px;
}

/* ── band 1 · hero, halved by a vertical rule ─────────────────────────────── */
/* border: 0 overrides .hg-band's shared hairline. The mode row below is a solid
   dark strip, so its own top edge is the division — a rule there was drawing a
   line onto the edge of a block that already has one (Mark, 9 Aug 2026). */
.app.home-grid .hv3-hero.hg-hero {
  position: relative; display: grid; grid-template-columns: 1fr 1fr;
  gap: 0; padding: 0; align-items: stretch; min-height: 284px; overflow: hidden;
  border: 0;
}
.app.home-grid .hv3-hero.hg-hero .hv3-hero-txt { padding: 34px 44px; align-self: center; }
/* The media half is tagged by class, never by position — the intro states put
   their dismiss control last, and :last-child handed it the whole column.
   It FILLS its half: the 30px inset plus centring left a letterbox of tinted
   background around the artwork, which on a design with no floating boxes read
   as an image that had failed to load (Mark, 9 Aug 2026: "can the main top
   image fill the space instead of white space around"). */
/* height: auto is doing real work — every art variant hard-codes height: 236px,
   which would leave a band of tint under it. Auto lets the grid row set it, so
   the half is filled by definition rather than by a matching number. */
.app.home-grid .hv3-hero.hg-hero .hg-hero-media {
  position: relative; inset: auto; margin: 0; height: auto; min-height: 0;
  border-left: 1px solid var(--ink08); background: var(--ink04);
  padding: 0; overflow: hidden;
}
/* Everything the media half holds is positioned ABSOLUTELY against it, and that
   is load-bearing rather than stylistic: the hero's height must come from the
   text side, never from the artwork. A full-page capture is 800×2400, so an
   in-flow image at 100% width computed 1770px tall and dragged the hero — and
   every band under it — off the bottom of the screen. Out of flow, the picture
   can be any shape at all and the band stays the height the copy needs.
   One capture, cropped to the half. */
.app.home-grid .hv3-hero.hg-hero .hg-hero-cover { display: block; }
.app.home-grid .hv3-hero.hg-hero .hg-hero-cover img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: top center; display: block; border-radius: 0;
}
/* The framed states — browser plate, scroll window, author image or video —
   fill the half instead of sitting centred in a letterbox of tint. The
   compositions (dashed slots, mosaic, shot stack) are deliberately NOT in this
   list: their children are placed against the box by hand, and stretching them
   would pull the composition apart. */
.app.home-grid .hv3-hero.hg-hero .hg-hero-media.hv3-art-plate > *,
.app.home-grid .hv3-hero.hg-hero .hg-hero-media.hv3-art-scroll > *,
.app.home-grid .hv3-hero.hg-hero .hg-hero-media.hv3-art-media-hero > * {
  position: absolute; inset: 0; width: 100%; height: 100%;
  max-width: none; max-height: none; margin: 0; border-radius: 0;
}
.app.home-grid .hv3-hero.hg-hero .hg-hero-media.hv3-art-media-hero img,
.app.home-grid .hv3-hero.hg-hero .hg-hero-media.hv3-art-media-hero video {
  object-fit: cover; object-position: top center;
}
.app.home-grid .hv3-hero.hg-hero .hg-hero-x { position: absolute; top: 14px; right: 14px; z-index: 3; }

/* ── band 2 · modes ───────────────────────────────────────────────────────────
   A dark strip, no heading, no dividing lines (Mark, 8 Aug 2026). It is the one
   band that inverts, which is what gives the page a spine — and with the rules
   gone the seven modes read as one object rather than seven boxes. Colours are
   literal rather than tokens: this strip is dark in BOTH themes, so --ink and
   --paper would swap under it and put dark text on dark. */
/* 24 here + 20 inside each cell = the same 44px inset the hero and compose
   bands use, on BOTH sides. Without it the first icon sat 20px from the edge
   while the last cell's text stopped short of it, and the row read as shunted
   left (Mark: "less gap on left"). */
.app.home-grid .hg-modes { padding: 0 24px; background: #16150F; border-bottom: 0; }
.app.home-grid .hg-moderow { display: grid; grid-template-columns: repeat(7, 1fr); border-left: 0; }
/* Centred (Mark, 9 Aug 2026). Left-aligned, every cell started its text at its
   own left edge while the icons sat above them in seven different places — the
   row read as seven ragged little columns. Centred, the icon and both lines
   share one axis per cell and the strip reads as one instrument panel. */
.app.home-grid .hg-mode {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 18px 14px 20px; background: transparent; border: 0;
  cursor: pointer; font: inherit; text-align: center; transition: background .12s ease;
}
.app.home-grid .hg-mode:hover { background: rgba(255, 255, 255, .06); }
.app.home-grid .hg-mode-ico { display: flex; color: #F4F2EC; }
.app.home-grid .hg-mode-ico svg { width: 17px; height: 17px; }
.app.home-grid .hg-mode-nm { font-size: 12.5px; line-height: 1.2; color: #F7F5EF; }
.app.home-grid .hg-mode-sb { font-size: 10.5px; line-height: 1.35; color: rgba(247, 245, 239, .52); }

/* ── band 3 · compose, three cells ────────────────────────────────────────── */
.app.home-grid .hg-compose { display: grid; grid-template-columns: 1.14fr 1fr 1fr; padding: 0; }
.app.home-grid .hg-cell {
  padding: 24px 28px 22px; border-right: 1px solid var(--ink08);
  display: flex; flex-direction: column; min-width: 0;
}
.app.home-grid .hg-cell:last-child { border-right: 0; }
.app.home-grid .hg-feat { cursor: pointer; }
.app.home-grid .hg-feat:hover { background: var(--ink04); }
.app.home-grid .hg-compose-copy { justify-content: center; padding-left: 44px; }
.app.home-grid .hg-compose-copy h2 { font-size: 23px; line-height: 1.14; letter-spacing: -.015em; margin: 10px 0 8px; font-weight: 500; }
.app.home-grid .hg-compose-copy p { font-size: 13px; line-height: 1.5; color: var(--ink-soft); margin: 0; }
.app.home-grid .hg-feat-hd { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.app.home-grid .hg-feat-t { font-size: 15.5px; font-weight: 500; }
.app.home-grid .hg-feat-p { font-size: 12.5px; line-height: 1.5; color: var(--ink-soft); margin: 0 0 12px; }
/* The art keeps its OWN size inside a clipped box — stretching it to the cell
   collapsed the mosaic's absolutely-positioned tiles to dots. */
/* The art GROWS with the cell rather than sitting at a fixed height under a
   growing column of blank paper — otherwise handing the compose band the slack
   just moves the gap inside it. min-height keeps it honest on short screens. */
.app.home-grid .hg-feat-art { margin-top: auto; position: relative; flex: 1 1 auto; min-height: 150px; display: flex; }
/* An explicit box, because the two arts size themselves differently: the mosaic
   is a grid with intrinsic height, the stack is absolutely-positioned children
   and collapses to 0×0 without one. */
.app.home-grid .hg-feat-art .hv3-art { position: relative; inset: auto; margin: 0; transform: none; width: 100%; height: auto; flex: 1 1 auto; min-height: 150px; }
/* The stack art is a <span>, and height does not apply to an inline box — it
   computed 152px and laid out at 0. The mosaic escaped this only because its
   own rule already makes it a grid. */
.app.home-grid .hg-feat-art .hv3-art-stack { display: block; }
.app.home-grid .hg-feat-go { display: flex; align-items: center; gap: 4px; font-size: 12.5px; color: var(--ink); margin-top: 12px; }
.app.home-grid .hg-feat-go svg { width: 12px; height: 12px; }

/* One line, and short. Two stacked lines plus a 38px icon made the guide strip
   73px tall, which was most of the difference between the page fitting a 800px
   laptop and scrolling by a hair (Mark, 9 Aug 2026: "its a bit annoying that
   the home page scrolls a tiny bit on my laptop. maybe put the guide text on 1
   line and reduce the section height so it fits on the full page"). Title and
   subtitle now sit side by side, and the icon comes down to match. */
.app.home-grid .hv3-help.hg-help { border: 0; border-radius: 0; margin: 0; padding: 11px 44px; background: transparent; gap: 12px; }
.app.home-grid .hg-help .hv3-help-ico { width: 26px; height: 26px; border-radius: 7px; }
.app.home-grid .hg-help .hv3-help-ico svg { width: 14px; height: 14px; }
.app.home-grid .hg-help .hv3-help-t { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.app.home-grid .hg-help .hv3-help-t b { display: inline; font-size: 13px; flex: none; }
.app.home-grid .hg-help .hv3-help-t i {
  display: inline; font-size: 12.5px; margin-top: 0; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── the rail ─────────────────────────────────────────────────────────────── */
/* Subgrid: the rail spans the band rows and inherits their row lines, so
   section 1 ends and section 2 begins on exactly the line where the hero ends
   and the mode row begins. Section 2 then spans everything below. */
.app.home-grid .hg-rail {
  border-left: 1px solid var(--ink08);
  grid-column: 2; grid-row: 2 / -1;
  display: grid; grid-template-rows: subgrid;
}
.app.home-grid .hg-rail-sec { padding: 22px 20px; border-bottom: 1px solid var(--ink08); min-width: 0; }
/* Latest captures runs down past the hero to the FOOT of the mode row, so the
   divide under it lines up with the bottom of the dark strip rather than the
   top (Mark, 9 Aug 2026) — and the extra height is what pays for a fifth
   capture in the list. */
.app.home-grid .hg-rail-sec:first-child { grid-row: 1 / 3; overflow: hidden; }
.app.home-grid .hg-rail-sec:nth-child(2) { grid-row: 3 / -1; border-bottom: 0; }
/* No subgrid (or the rail wraps under at narrow widths): fall back to a plain
   column so the sections still stack rather than collapsing onto one row. */
@supports not (grid-template-rows: subgrid) {
  .app.home-grid .hg-rail { display: flex; flex-direction: column; }
}
.app.home-grid .hg-none { font-size: 11.5px; line-height: 1.5; color: var(--ink50); margin: 0; }
/* `font: inherit` took the body's 15px with it and blew away .hv3-lbl's 10.5px,
   which is why "ALL" arrived the size of a heading. Reset the button chrome
   only, and state the type. */
.app.home-grid .hg-all {
  background: transparent; border: 0; padding: 0; cursor: pointer;
  font-family: var(--sans); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink50);
}
.app.home-grid .hg-all:hover { color: var(--ink); }
/* A row with two controls, not one big button: the download needs its own hit
   area, and a <button> nested in a <button> is invalid markup Chrome flattens. */
.app.home-grid .hg-cap { display: flex; align-items: center; position: relative; }
.app.home-grid .hg-cap-open,
.app.home-grid .hg-site { display: flex; align-items: center; gap: 10px; width: 100%; background: transparent; border: 0; padding: 8px 0; cursor: pointer; font: inherit; text-align: left; }
.app.home-grid .hg-cap:hover,
.app.home-grid .hg-site:hover { background: var(--ink04); }
/* Reveal on hover or keyboard focus — always-on it competed with the title for
   a 232px rail, but focus-within keeps it reachable without a mouse. */
.app.home-grid .hg-cap-dl {
  position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; border: 0; border-radius: 5px;
  background: var(--paper); color: var(--ink50); cursor: pointer;
  opacity: 0; transition: opacity .12s ease, color .12s ease;
}
.app.home-grid .hg-cap:hover .hg-cap-dl,
.app.home-grid .hg-cap:focus-within .hg-cap-dl { opacity: 1; }
.app.home-grid .hg-cap-dl:hover { color: var(--ink); }
.app.home-grid .hg-cap-shot { width: 54px; height: 38px; border-radius: 5px; overflow: hidden; background: var(--ink08); flex: none; display: block; position: relative; }
/* A play dot, so a clip doesn't read as a still in the rail. */
.app.home-grid .hg-cap-vid {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 14px; height: 14px; border-radius: 50%; background: rgba(22, 21, 15, .62);
}
.app.home-grid .hg-cap-vid::after {
  content: ''; position: absolute; left: 5.5px; top: 4px;
  border-left: 5px solid #fff; border-top: 3.5px solid transparent; border-bottom: 3.5px solid transparent;
}
.app.home-grid .hg-cap-shot img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }
.app.home-grid .hg-cap-meta { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.app.home-grid .hg-cap-t { font-size: 11.5px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 132px; }
.app.home-grid .hg-cap-m { font-size: 10px; color: var(--ink50); }
.app.home-grid .hg-site-fav { width: 18px; height: 18px; border-radius: 4px; background: var(--ink08); flex: none; object-fit: cover; }
.app.home-grid .hg-site-nm { font-size: 11.5px; }
.app.home-grid .hg-site-ct { margin-left: auto; font-size: 10.5px; color: var(--ink32); }

/* Steps 1 and 2's hero image: no drop shadow (Mark, 8 Aug 2026). It reads as a
   floating object, which is the one thing this design is trying not to be —
   and the media half already has its own tint and a rule to sit against. */
.app.home-grid .hv3-plate,
.app.home-grid .hv3-scrollwin { box-shadow: none; }

/* ── type: no mono on headings (Mark, 8 Aug 2026) ─────────────────────────────
   The eyebrows and the sidebar's group labels were set in Söhne Mono. Mono is
   for data — filenames, dimensions, hosts — not for headings. Same size, same
   letterspacing, same case; the sans just carries them better. Scoped to the
   grid design so the card home is untouched until Mark wants this app-wide. */
.app.home-grid .hv3-lbl,
.app.home-grid .sidebar .hv3-grp { font-family: var(--sans); letter-spacing: .1em; }

/* ── the sidebar, cleaned up (Mark) ───────────────────────────────────────────
   The descriptor line moves to the mode row, which now carries it, so the rail
   is names only. Gated on the grid design so switching back to v3 — which has
   no mode row — does not leave the modes unexplained. */
.app.home-grid .sidebar .hv3-sb { display: none; }
/* …which left the names bunched, because their row spacing was built around a
   two-line item. Give them air (Mark: "way too bunched"). */
.app.home-grid .sidebar .hv3-item { padding-top: 10px; padding-bottom: 10px; margin-bottom: 3px; }
.app.home-grid .sidebar .hv3-grp { margin-top: 26px; margin-bottom: 10px; }
.app.home-grid .sidebar .hv3-grp:first-child { margin-top: 0; }
/* …and the Pro card sits at the FOOT of the rail rather than under the last
   group, which is where it was drifting to whatever the group heights were. */
.app.home-grid .sidebar { display: flex; flex-direction: column; }
.app.home-grid .sidebar .hv3-prox.is-pinned { margin-top: auto; }

/* ── narrow ───────────────────────────────────────────────────────────────────
   A ladder, not a cliff. The single 1180px breakpoint threw the whole thing
   into one column the moment the window got a bit narrower, which meant the
   layout was either perfect or unrecognisable with nothing in between (Mark,
   9 Aug 2026: "need to work on the responsiveness of home page"). Each rung
   below gives up the least valuable thing at that width and keeps the rest.

   Widths are the WINDOW, and the left sidebar is already narrowing itself at
   1100px on its own — these rungs are chosen to land either side of that so
   the two don't fire together and lurch. */

/* Rung 1 — trim. Nothing moves; the rail and the gutters just give back the
   space the bands need to keep their shape. */
@media (max-width: 1440px) {
  .app.home-grid .hv3.hg-shell { grid-template-columns: minmax(0, 1fr) 208px; }
  .app.home-grid .hv3-hero.hg-hero .hv3-hero-txt { padding: 30px 34px; }
  .app.home-grid .hg-cell { padding: 22px 22px 20px; }
  .app.home-grid .hg-compose-copy { padding-left: 34px; }
  .app.home-grid .hv3-help.hg-help { padding: 15px 34px; }
  .app.home-grid .hg-modes { padding: 0 14px; }
}

/* Rung 2 — the mode row wraps to two rows of four rather than squeezing seven
   cells into a width where the labels break mid-word. */
@media (max-width: 1280px) {
  .app.home-grid .hv3.hg-shell { grid-template-columns: minmax(0, 1fr) 188px; }
  .app.home-grid .hg-moderow { grid-template-columns: repeat(4, 1fr); }
  .app.home-grid .hg-cap-t { max-width: 96px; }
  .app.home-grid .hg-compose-copy h2 { font-size: 21px; }
}

/* Rung 3 — the rail comes out of the side and goes UNDER the bands, two
   sections abreast. It collapses rather than disappearing: "Latest captures" is
   how you get back to yesterday's work, and at this width the bands need the
   whole stage more than the rail needs to be beside them. */
@media (max-width: 1120px) {
  .app.home-grid .hv3.hg-shell { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  /* One column: every explicit row/column placement has to be released, or the
     bands pile onto five fixed rows with the rail on top of them. */
  .app.home-grid .hg-authorbar,
  .app.home-grid .hg-hero,
  .app.home-grid .hg-modes,
  .app.home-grid .hg-compose,
  .app.home-grid .hg-help { grid-column: 1; grid-row: auto; }
  .app.home-grid .hg-rail {
    border-left: 0; border-top: 1px solid var(--ink08);
    grid-column: 1; grid-row: auto;
    display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: none;
  }
  .app.home-grid .hg-rail-sec:first-child,
  .app.home-grid .hg-rail-sec:nth-child(2) { grid-row: auto; overflow: visible; }
  .app.home-grid .hg-rail-sec:first-child { border-right: 1px solid var(--ink08); border-bottom: 0; }
  .app.home-grid .hg-cap-t { max-width: none; }
  .app.home-grid .hg-compose { grid-template-columns: 1fr 1fr; }
  /* Three cells over two columns: the copy takes the full first row. */
  .app.home-grid .hg-compose-copy { grid-column: 1 / -1; padding-left: 34px; }
  .app.home-grid .hg-cell { border-bottom: 1px solid var(--ink08); }
  .app.home-grid .hg-cell:last-child { border-right: 0; }
}

/* Rung 4 — the hero stops being two halves. The picture goes on top at a fixed
   band height and the copy sits under it, which is the one arrangement that
   still works when neither half can hold both. */
@media (max-width: 940px) {
  .app.home-grid .hv3-hero.hg-hero {
    grid-template-columns: 1fr; grid-template-rows: 168px auto;
    min-height: 0;
  }
  .app.home-grid .hv3-hero.hg-hero .hg-hero-media {
    grid-row: 1; border-left: 0; border-bottom: 1px solid var(--ink08);
  }
  .app.home-grid .hv3-hero.hg-hero .hv3-hero-txt { grid-row: 2; padding: 24px 28px 28px; }
  .app.home-grid .hg-moderow { grid-template-columns: repeat(3, 1fr); }
  .app.home-grid .hg-compose { grid-template-columns: 1fr; }
  .app.home-grid .hg-cell { border-right: 0; }
  .app.home-grid .hg-compose-copy { grid-column: 1; padding-left: 28px; }
  .app.home-grid .hg-rail { grid-template-columns: 1fr; }
  .app.home-grid .hg-rail-sec:first-child { border-right: 0; border-bottom: 1px solid var(--ink08); }
  .app.home-grid .hv3-help.hg-help { padding: 15px 28px; }
}

/* Rung 5 — two modes abreast is the floor. Below this the strip would be a
   column of buttons, and the left sidebar already is one. */
@media (max-width: 720px) {
  .app.home-grid .hg-moderow { grid-template-columns: repeat(2, 1fr); }
  .app.home-grid .hg-mode-sb { display: none; }
}
/* ==== HOME GRID END ==== */

/* ==== QUEUE STRIP BEGIN ======================================================
   The showreel queue, on screen at rest and draggable in place (Mark, 10 Aug
   2026: "this is just drag to reorder"). Deleting from this fence to QUEUE
   STRIP END, plus LAYOUTS_QUEUE_STRIP in app.js, is the whole revert.

   Two placements, chosen by the plate's shape rather than by a setting: under a
   wide plate, beside a tall one. A wide plate leaves room below it and none
   beside; a tall one the reverse.
   --------------------------------------------------------------------------- */
/* 10px, matching .layouts-toolbar directly above it — not a 100px pill (Mark,
   10 Aug 2026: "style it so that it isnt a round pill but has the rounded
   corners like the rest of the app"). The pill came from the mockup, where it
   floated alone; sitting under the toolbar it has to agree with it, and two
   different corner languages stacked 10px apart read as two different apps. */
/* No drop shadow (Mark, 11 Aug 2026). It came from the floating-pill mockup,
   where the bar hovered over the artwork and had to lift off it. In place it
   sits 10px under .layouts-toolbar wearing a shadow roughly twice as deep as
   the toolbar's (38px blur at .09 against 18px at .06) — two bars a thumb apart
   floating at two different heights. Paper on the stage grey is separation
   enough for a bar that is resting on the stage, not hovering over it. */
.layouts-qstrip {
  display: flex; align-items: center; gap: 7px;
  padding: 9px 11px; background: var(--paper); border-radius: 10px;
  z-index: 5; max-width: calc(100% - 32px);
}
.layouts-qstrip.is-under { margin: 10px auto 0; flex: none; }
/* The RAIL scrolls, the count stays. A full queue on a short window overflows,
   and the count is the one thing in here that must not scroll away — it is how
   you know there is no room for another. */
.layouts-qstrip .qs-rail {
  display: flex; align-items: center; gap: 7px; min-width: 0;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  padding: 2px 0;   /* the drop marker's 4px offset needs somewhere to land */
}
/* min-height:0 as well as min-width:0 — a flex child defaults to min-*:auto and
   would refuse to shrink, so the rail would push the strip past the stage
   instead of scrolling inside it. */
.layouts-qstrip.is-side .qs-rail { flex-direction: column; overflow-x: hidden; overflow-y: auto; min-height: 0; }
.layouts-qstrip .qs-rail::-webkit-scrollbar { display: none; }
/* Absolute rather than a flex sibling: wrapping the scroller in a row would
   change the box layoutsFitBoard measures to size the plate. */
.layouts-qstrip.is-side {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  flex-direction: column; max-height: calc(100% - 40px);
}
.layouts-qstrip.is-side .qs-rail { max-height: 100%; }

/* THE HAIRLINE IS NOT DECORATION (Mark, 12 Aug 2026: "if the capture are light,
   its hard to see them"). The strip's own background is --paper, and a pale
   capture — a white hero, a cream template — painted straight onto it has no
   edge at all: the tile stops existing and the queue reads as a column of gaps
   with numerals floating in it. The --ink08 fill behind only shows while the
   image is still loading, so it never solved this.

   An OUTLINE drawn inward rather than an inset box-shadow: the four drop-marker
   rules below own `box-shadow` to paint the "put it here" line, and a base
   box-shadow would simply be replaced by them mid-drag — the tiles would lose
   their edges at exactly the moment you are trying to aim between them. */
.layouts-qstrip .qs-cell {
  position: relative; width: 52px; height: 34px; border-radius: 5px; overflow: hidden;
  background: var(--ink08); cursor: grab; flex: none;
  outline: 1px solid var(--bmed); outline-offset: -1px;
}
.layouts-qstrip .qs-cell:active { cursor: grabbing; }
.layouts-qstrip .qs-cell.is-lifting { opacity: .4; }
.layouts-qstrip .qs-cell img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.layouts-qstrip .qs-n {
  position: absolute; left: 3px; top: 2px; font-size: 9px; line-height: 1;
  color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
}
/* The remove is hover-only HERE and that is defensible: unlike the slot corner,
   the thing it acts on is already visible and already says what it is. It is a
   refinement on a discovered object, not the only route to a hidden one. */
.layouts-qstrip .qs-x {
  position: absolute; right: 2px; top: 2px; width: 15px; height: 15px; padding: 0;
  border: 0; border-radius: 50%; background: rgba(22, 21, 15, .74); color: #fff;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity .12s ease;
}
.layouts-qstrip .qs-x svg { display: block; }
.layouts-qstrip .qs-cell:hover .qs-x { opacity: 1; }
/* ⇄ (30 Aug 2026): the strip's replace — × ’s twin, on the other corner. */
.layouts-qstrip .qs-swap {
  position: absolute; left: 2px; top: 2px; width: 15px; height: 15px; padding: 0;
  border: 0; border-radius: 50%; background: rgba(22, 21, 15, .74); color: #fff;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity .12s ease;
}
.layouts-qstrip .qs-swap svg { display: block; }
.layouts-qstrip .qs-cell:hover .qs-swap { opacity: 1; }
.layouts-qstrip .qs-x:hover { background: rgba(22, 21, 15, .92); }
/* The drop marker sits BETWEEN cells, so the gesture reads as "put it here"
   rather than "swap with that one" — which is what the move semantics do. */
.layouts-qstrip.is-under .qs-cell.drop-before { box-shadow: -4px 0 0 -1px var(--ink); }
.layouts-qstrip.is-under .qs-cell.drop-after { box-shadow: 4px 0 0 -1px var(--ink); }
.layouts-qstrip.is-side .qs-cell.drop-before { box-shadow: 0 -4px 0 -1px var(--ink); }
.layouts-qstrip.is-side .qs-cell.drop-after { box-shadow: 0 4px 0 -1px var(--ink); }

/* The add is a BUTTON, not a seventh empty cell (Mark, 11 Aug 2026: "showreel
   stack — add button is tiny"). At 52×34 with a 15px glyph it was the same
   footprint as the thumbnails beside it and read as one more slot waiting to be
   filled — small, and ambiguous with it. Taller than a cell, a glyph you can
   actually hit, and its own word: it is a different kind of object from the
   queue it ends, and now looks like one. Same treatment in both orientations,
   since the stack has no spare width to grow into. */
.layouts-qstrip .qs-add {
  width: 52px; height: 46px; border-radius: 6px; flex: none;
  border: 1.5px dashed var(--ink32); background: transparent; color: var(--ink50);
  font-family: var(--sans); font-size: 9.5px; line-height: 1; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  transition: border-color .12s ease, color .12s ease;
}
.layouts-qstrip .qs-add b { font-size: 18px; font-weight: 400; line-height: 1; }
.layouts-qstrip .qs-add:hover { border-color: var(--ink); color: var(--ink); }
.layouts-qstrip .qs-count { font-size: 11px; color: var(--ink50); white-space: nowrap; margin-left: 4px; }
.layouts-qstrip.is-side .qs-count { margin: 2px 0 0; }
/* Clear, after the count: the Style strip's quiet text button (shell.css .style-thumb-clear), same size and rest */
.layouts-qstrip .qs-clear { flex: none; margin-left: 2px; padding: 6px 10px; border: 0; background: transparent; color: rgba(var(--fg), .5); font: inherit; font-size: 12.5px; cursor: pointer; border-radius: 6px; }
.layouts-qstrip .qs-clear:hover { color: var(--ink); background: rgba(var(--fg), .05); }
.layouts-qstrip.is-side .qs-clear { margin: 0; }
/* ==== QUEUE STRIP END ====================================================== */

/* ── Backgrounds chooser ─────────────────────────────────────────────────────
   Uploaded background images: the store is separate from the library, so the
   surface is its own too. Cards borrow .lib-card/.lib-thumb so it reads as
   house furniture; only the add tile and the inline delete confirm are new.
   The confirm sits ON the card because there is one modal root — a nested
   showModal would tear the chooser down under itself. */
.bgpk-modal { max-width: 760px; }
.bgpk-hint { font-size: 12.5px; color: var(--ink50); margin: -6px 0 14px; }
.bgpk-err { font-size: 12.5px; color: #b23; margin: 0 0 12px; }
.bgpk-err[hidden] { display: none; }
.bgpk-wrap { max-height: min(52vh, 420px); overflow: auto; margin: 0 -4px; padding: 0 4px 4px; }
.bgpk-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.bgpk-card { position: relative; }
.bgpk-card .lib-thumb { aspect-ratio: 4 / 3; }
.bgpk-card .lib-thumb-img { width: 100%; height: 100%; max-width: none; max-height: none; object-fit: cover; }
.bgpk-card .lib-card-title { font-size: 12.5px; }
.bgpk-card .lib-card-meta { font-family: var(--mono); font-size: 10.5px; color: var(--ink50); }
.bgpk-card .lib-thumb-actions { opacity: 0; transition: opacity .12s; }
.bgpk-card:hover .lib-thumb-actions { opacity: 1; }
.bgpk-add {
  border: 1px dashed var(--ink32);
  border-radius: 10px;
  background: none;
  color: var(--ink);
  cursor: pointer;
  min-height: 140px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  font: inherit; font-size: 12.5px;
  transition: border-color .12s, color .12s;
}
.bgpk-add:hover { border-color: var(--ink); }
.bgpk-add.is-busy, .bgpk-add:disabled { opacity: .5; cursor: default; }
.bgpk-add-plus { font-size: 20px; line-height: 1; color: var(--ink50); }
.bgpk-confirm {
  position: absolute; inset: 0;
  background: rgba(250, 250, 249, .94);
  border-radius: 10px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  padding: 12px; text-align: center;
}
.bgpk-confirm[hidden] { display: none; }
.bgpk-confirm-q { font-size: 12.5px; color: var(--ink); }
.bgpk-confirm button { font: inherit; font-size: 12px; padding: 5px 12px; border-radius: 8px; border: 1px solid var(--border); background: none; color: var(--ink); cursor: pointer; }
.bgpk-confirm-yes { color: #b23 !important; }
.app.dark .bgpk-confirm { background: rgba(14, 14, 12, .92); }
/* Backdrop row: two sources side by side (Upload / Capture) plus Remove.
   Wraps rather than squeezing — the sidebar is 308px and "+ Change capture"
   is a wide label. */
.layouts-bg-srcs { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }

/* ── type: the editing surface ──────────────────────────────────────────
   Everything a viewer sees is painted on the CANVAS by drawText, so preview
   and export are one renderer and cannot drift. What lives here is only what
   a designer needs in order to point at a piece of type: the selection frame,
   the two width handles, the snap guides, and — while the caret is in it —
   the contenteditable body itself. The layer's position and size are written
   by sizeCanvas from the canvas's own rect, because the canvas is letterboxed
   inside the stage and inset:0 would put every box in the wrong place at
   every aspect but one. */
.reel-tx{position:absolute;z-index:4;pointer-events:none;opacity:0}
/* The surface exists only while the reel is stopped. Frames and handles drawn
   over a running reel are furniture on top of the thing you are judging. */
.reel-panel.reel-texting .reel-tx{pointer-events:auto;opacity:1}
.reel-txel{position:absolute;transform:translate(-50%,-50%);box-sizing:border-box;
  pointer-events:auto;cursor:default}
.reel-txel .reel-txbody{white-space:pre-wrap;word-break:break-word;outline:none}
.reel-txel.empty .reel-txbody::before{content:'Type…';opacity:.45}
/* An outset ring, not a border: a border would change the box the type is
   measured into and the DOM frame would stop agreeing with the canvas. */
.reel-txel::after{content:'';position:absolute;inset:-5px;border:1px solid transparent;
  border-radius:3px;pointer-events:none}
/* HONEY, not the #F7CE46 it was until 1 August (Mark: "the text box outline
   and blue. lets make it our honey colour."). The blue was borrowed from every
   other design tool's selection colour, which is exactly why it read as
   foreign — it was the one hue in the app belonging to no token. #F7CE46 is
   the accent already carrying the favourite star, the alert chip and the home
   dots, and it holds against both a black card and a white one. The drag
   guides go with it: two colours for one gesture is the same inconsistency in
   miniature. */
.reel-txel.on::after{border-color:#F7CE46}
.reel-txel.editing::after{border-color:#F7CE46;border-style:dashed}
.reel-txh{position:absolute;top:50%;width:9px;height:9px;margin-top:-4.5px;border-radius:2px;
  background:#fff;border:1.5px solid #F7CE46;display:none;cursor:ew-resize;z-index:2}
.reel-txel.on .reel-txh{display:block}
.reel-txh.l{left:-9px}
.reel-txh.r{right:-9px}
/* Spacing bars (#302). A DIFFERENT colour from the alignment hairline on
   purpose: alignment is about a shared edge, spacing about a shared distance,
   and on a plate with two of each the only thing keeping them apart is that
   they do not look alike. Same reason Figma's are pink and its guides are red.
   Drawn on both the layouts/style layer and the reel's, so one rule. */
.tx-gapbar{position:absolute;background:#F04E98;z-index:6;pointer-events:none;opacity:.95}
.tx-gapbar.x{height:2px;transform:translateY(-1px)}   /* a horizontal bar measuring a horizontal gap */
.tx-gapbar.y{width:2px;transform:translateX(-1px)}
/* The end caps, so a bar reads as a MEASUREMENT and not as a stray rule. */
.tx-gapbar::before,.tx-gapbar::after{content:'';position:absolute;background:#F04E98}
.tx-gapbar.x::before,.tx-gapbar.x::after{width:2px;height:8px;top:-3px}
.tx-gapbar.x::before{left:0}
.tx-gapbar.x::after{right:0}
.tx-gapbar.y::before,.tx-gapbar.y::after{height:2px;width:8px;left:-3px}
.tx-gapbar.y::before{top:0}
.tx-gapbar.y::after{bottom:0}
.reel-tguide{position:absolute;background:#F7CE46;z-index:5;pointer-events:none;opacity:.9}
.reel-tguide.v{width:1px;top:0;bottom:0}
.reel-tguide.h{height:1px;left:0;right:0}

/* ── type: the panel group ──────────────────────────────────────────────
   Text is a property of a BLOCK, so this group appears on a capture and on a
   card alike. The list at the top is the only place the elements are named:
   on the artboard they are the words themselves, which is a better label than
   any we could write, but you still need a way back to one you have moved off
   screen or set to 5% opacity. */
.reel-cc-text[hidden]{display:none}
.reel-txlist{display:flex;flex-direction:column;gap:2px}
.reel-txlist[hidden]{display:none}
/* 17 Aug 2026, Mark: "can we make the actual text layer slightly taller".
   24px was the Sequence strip's own rung and it came here by inheritance — but
   this list is the only place you SELECT an element, and a 24px target with a
   ×, a duplicate and a copy icon inside it was three hit areas in a row barely
   taller than the type. 30px gives the row breathing space and the icons room
   to be pressed without gaining a single pixel of visual weight. */
.reel-txrow{display:flex;align-items:center;gap:7px;width:100%;padding:0 6px;height:30px;
  border:1px solid transparent;border-radius:7px;background:transparent;cursor:pointer;text-align:left}
/* ADD + PASTE SIT TOGETHER (17 Aug 2026, Mark: "the add and paste text buttons
   need sorting. smaller, maybe next to each other"). Two full-width dashed
   slabs stacked read as two separate invitations of equal weight, when Paste is
   the rarer one and only appears at all with something on the clipboard. Side
   by side at the app's compact tier: Add takes the room, Paste takes what it
   needs. Shared by all three panels — Style, Layouts and Sequence carry the
   identical pair, so they get the identical rule. */
.tx-addrow{display:flex;align-items:stretch;gap:6px;margin-top:8px}
.tx-addrow > .ghost-btn.is-add{flex:1 1 auto;min-width:0;padding:7px 10px;font-size:11.5px;
  border-radius:8px;white-space:nowrap}
.tx-addrow > .ghost-btn.is-add[hidden]{display:none}
/* Paste never grows past its words; the room belongs to Add. */
.tx-addrow > [data-sstxpaste],
.tx-addrow > #layouts-txpaste,
.tx-addrow > #reel-txpaste{flex:0 0 auto}
.reel-txrow:hover{background:var(--acc-band, rgba(0,0,0,.05))}
.reel-txrow.on{border-color:var(--ink);background:var(--acc-band, rgba(0,0,0,.05))}
/* The T-number is monospaced so the column stays a column past nine elements. */
.reel-txrow .g{font-family:var(--mono, ui-monospace, Menlo, monospace);font-size:9px;
  letter-spacing:.06em;color:var(--ink-mute);flex:none}
.reel-txrow .s{font-family:var(--sans);font-size:12.5px;color:var(--ink);flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.reel-txrow .s.empty{color:var(--ink-mute);font-style:italic}
.reel-txrow .x{display:inline-flex;align-items:center;color:var(--ink-mute);flex:none;padding:0 2px}
.reel-txrow .x svg{display:block}
.reel-txrow .x:hover{color:var(--ink)}
/* #305. Duplicate and Copy, on the element's own row (Mark, 10 Aug 2026: "i
   meant in the panel. small duplicate, copy icons"). They live at the quiet end
   of the row with the ×, muted until you reach for them, so a list of six
   elements still reads as a list of six names rather than eighteen controls.
   The label beside them is `flex:1;min-width:0` and ellipsises, so the icons
   never get squeezed by a long line of type. */
.reel-txrow .o{display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;border-radius:5px;flex:none;color:var(--ink-mute)}
.reel-txrow .o:hover{color:var(--ink);background:rgba(14,14,12,.09)}
.reel-txrow .o svg{display:block}
/* The gap the row sets is 7px, which is right between the T-number, the name
   and the ×, and too loose for a pair of icons that belong together. */
.reel-txrow .o + .o{margin-left:-4px}
.reel-txrow .o + .x{margin-left:-1px}
/* THE ADD BUTTON IS THE APP'S ADD BUTTON (#276. Mark: "'add text element'
   button padding and spacing"). What was here was a hand-rolled copy of
   .ghost-btn.is-add that missed it in four places — dashed --bmed on
   transparent, yes, but 9px radius against 8, 12.5px type against 12, --ink
   against --ink50, and a 34px fixed height with ZERO padding against 8/13.
   Near-misses like that are worse than a different control, because the eye
   reads them as the same thing rendered badly. The markup now carries
   `ghost-btn is-add` and everything that draws pixels comes from there.

   WIDTH IS THE ACTUAL BUG. The old rule said `align-self:stretch`, which
   works only inside a flex container. #reel-cc-imgadd sat in a .field (flex
   column) so it filled; #reel-txadd sits in an .option-expand, which is a
   BLOCK, so stretch was inert and the button shrank to its own label — 117px
   reading "Add text to this block", then 96px once the label became "Add text
   element". The same class, two widths, in one panel, and a button that
   visibly shrank when you used it. width:100% holds in both parents. */
.reel-txadd{width:100%}
/* #300. The two copy buttons are ONE control with two reaches, so they share a
   row rather than stacking into two more full-width lines under the add button
   — a Text drawer that is three stacked ghost buttons before you reach the
   editor reads as a menu, not a panel. Equal columns for the same reason the
   loose actions row uses them: the labels differ in length and ragged widths
   on a shared spine is the exact thing #276 was fixed to stop. */
.reel-txcopy{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.reel-txcopy[hidden]{display:none}
.reel-txcopy .ghost-btn{width:100%}
.reel-txcopy .ghost-btn:disabled{opacity:.4;cursor:default}
/* #305. Element-level Duplicate / Copy, inside the selected-element editor.
   Same two-up grid as .reel-txcopy above so the two scopes read as the same
   KIND of control at different levels, rather than as two unrelated widgets. */
.reel-txpaste[hidden]{display:none}
/* The three parts of the Text drawer — the element list, the add button, the
   editor for whichever is selected — had NOTHING between them: the expand is
   display:block and none of the three carries a margin, so the list sat flush
   on the button and the button flush on the editor. One stack, one gap, the
   same 12px the editor already uses inside itself. */
.reel-cc-text > .option-expand{display:flex;flex-direction:column;gap:12px}
.reel-txed{display:flex;flex-direction:column;gap:0}   /* rows carry the rail's own 14px (21 Sep 2026) */
/* .seq-settings .field adds margin-bottom:12px, which is right in a block
   container and DOUBLE-COUNTS in this one: it stacked on the 12px gap, so
   Font and Weight sat 24px apart while the four sliders between them sat 12.
   Two rhythms in one column, alternating. The gap is the rhythm here. */
.reel-txed .field{margin-bottom:0}
.reel-txed[hidden]{display:none}
/* A slider row: the app's 13px .field-label on the left, the app's
   .opt-slider-wrap (slider + mono readout) on the right. What used to be here
   was a bespoke 52px/1fr/40px grid with a 10px label and a 9.5px readout —
   its own type scale, its own slider, its own everything, in a panel sitting
   next to options that already had all three (Mark, 1 August 2026). The one
   thing kept from it is the idea of a label spine, which the fixed label
   column still gives; everything drawing pixels is now the app's. */
/* Tightened with the other two type panels (#308) — Mark: "the spacing between
   the sliders can be reduced." Sequence's fields carried their gap from the
   parent's row rhythm; the type block is one group and reads better close. */
.reel-tx-f{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:2px 0}
.reel-tx-f > .field-label{flex:none}
/* 132px is the width the option sliders take in a full-width options column;
   this panel is 270px, so it narrows to the same 96px the canvas accordion
   already uses. Same control, one measurement different. */
.reel-side .opt-slider{width:96px}
/* Only the size changes here — the width is the base rule's calc, which is in
   ch and em and so narrows with it. A 34px minimum was the same wobble as the
   layouts rail, one panel over. */
.reel-side .opt-slider-val{font-size:11px}
.reel-txwarn{font-family:var(--sans);font-size:11.5px;line-height:1.45;color:var(--ink-soft);
  margin:2px 0 0;padding:6px 7px;border-radius:6px;background:var(--acc-band, rgba(0,0,0,.05))}
.reel-txwarn[hidden]{display:none}
.reel-txwarn b{font-weight:500;color:var(--ink)}
/* REMOVE IS .layouts-reset — the app's quiet text action, and already the
   control the sibling two rows below uses ("Remove this layout from the
   sequence"). It was a bordered 34px button here, which gave the one
   destructive control in the drawer MORE weight than the settings above it,
   and boxed it where Molly's rule says nothing centres. Borderless, left
   aligned on the same spine as every label.
   The .seq-settings override adds a 12px side margin for rows that sit
   directly in the band; this one is already inset by the expand, so it keeps
   the column edge. 6px on top makes an 18px break — visibly more than the
   12px rhythm, so the drawer ends rather than continuing. */
.reel-txed .layouts-reset{margin:6px 0 0;width:100%}
.reel-txhint{font-family:var(--sans);font-size:11.5px;line-height:1.5;color:var(--ink-mute);margin:1px 0 0}
.reel-txhint[hidden]{display:none}

/* ═══ SHOT STEP (#201) ══════════════════════════════════════════════════════
   Making a shot happens INSIDE the editor. The layouts board is pinned over
   .main-inner while the editor panel stays mounted underneath, so there is no
   screen change to see — you are still in the same place, with the same rail
   and the same right panel column.

   Both panels carry .is-active while the step is up (see layoutsOpenShotStep),
   which is the only reason the board can be measured at all: the toReel
   exporter reads geometry from these elements and .panel{display:none} would
   hand it zeroes. So the step is hidden by OPACITY during a background render
   (.is-shotrender), never by display. */
.main-inner { position: relative; }
.panel.layouts-panel.is-shotstep {
  position: absolute; inset: 0; z-index: 40;
  display: flex; flex-direction: column;
  background: var(--canvas);
}
/* The class above sets display:flex, which beats the UA [hidden] rule — so it
   needs its own guard, like every other toggled flex/grid class in here. */
.panel.layouts-panel.is-shotstep[hidden] { display: none; }
/* THE STAGE IS THE SAME BOX IT IS IN LAYOUTS (#318. Mark, 4 Aug 2026:
   "Editing a layout from sequence - the transport is right to the bottom. make
   is consistent with normal layouts page.")
   This used to read `flex: 1 1 auto; min-height: 0; max-height: none` -- the
   stage cancelled its own calc(100vh - 122px) clamp and stretched to fill the
   step panel instead. That was written when the step had a header of its own to
   sit under, and it stopped being true when the Cancel / Add shot bar moved up
   into the topbar (see the note below): with nothing above it, the stage simply
   grew to whatever height .main-inner happened to be, and .main-inner in the
   sequence editor is set by the EDITOR's content, not by the board. Measured at
   1440x950: standalone the stage is 828px and the transport bar ends 50px clear
   of the fold; in the step the stage was 872px and the bar ended 6px off it.
   Same bar, same board, pinned to the bottom of the window in one place and
   floating comfortably above it in the other -- which is what Mark saw.
   Dropping the override rather than patching a new number in is the whole fix.
   .layouts-stage already states its height once, and both routes into a board
   should be looking at the identical box, so the step now inherits that clamp
   like anything else. flex: 0 0 auto keeps the flex parent from stretching or
   shrinking it back off the clamp; min-height on the base rule still floors it
   if the panel underneath is ever shorter. The panel's own background is
   var(--canvas), the same as the stage's, so the slack below the stage in a
   taller panel is invisible. */
.panel.layouts-panel.is-shotstep .layouts-stage {
  flex: 0 0 auto;
}
/* The render is filming the live board: it must keep its layout, so it is made
   invisible and inert rather than removed. */
.panel.layouts-panel.is-shotrender {
  opacity: 0; pointer-events: none;
}

/* The step's actions live in the topbar action slot like every other mode's,
   NOT on a bar of their own (Mark, 1 Aug: "not a fan of the big white bar with
   cancel and done on. looks poor" / "need to sort out the top nav buttons").
   The bar was a second header competing with the real one, and because both
   panels are active during the step it left three Exports along the top saying
   three different things. So: while the step is up, the sequence pair and the
   Layouts trio are HIDDEN rather than unmounted — nothing has to be rebuilt
   when the step closes, and the reel keeps its own topbar state. The board
   already names itself in its control chip, so the title went with the bar.
   3 Aug: matched by exclusion rather than by naming the two owners. A capture
   mode relocates its own .action-footer (Capture / Open capture window) into
   this same slot, and that footer carries neither data-reel nor data-layouts,
   so the old pair of selectors let it sit alongside the step's Cancel + Add
   shot. While the step is up, the step owns the slot: hide everything in it
   that is not the step's. */
.app.in-shotstep #topbar-actions-slot > *:not([data-shotstep]) { display: none; }

/* The rail says why a second template is waiting, rather than a dead control. */
.rail-tpl-busy {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-mute);
  margin: -6px 2px 11px;
}

/* ── #309 Layouts transport bar (Mark, 4 Aug 2026) ─────────────────────────
   The floating toolbar above the plate became ONE bar under it, and that bar
   is a real transport: play, restart, progress, time, then the layout's name,
   its motion, and zoom. Modelled on the Sequence transport Mark named, sized
   down to this bar's height (28px round rather than 34px).
   Only a MOVING board gets .has-transport (the rail needs the room); a still
   board keeps the fit-content bar — play, name, motion, zoom. */
.layouts-toolbar.has-transport { width: min(100%, 720px); }
.layouts-toolbar .lt-play {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%; border: 0;
  background: var(--ink); color: var(--paper); cursor: pointer;
}
.layouts-toolbar .lt-play:hover { opacity: .86; }
/* A board on Still keeps its play button and greys it out, rather than losing
   it (Mark, 4 Aug 2026) — the app's ordinary dead-control idiom, matching
   .lt-btn[disabled] above. The :hover lift is neutralised so it doesn't read
   as pressable. */
.layouts-toolbar .lt-play[disabled] { opacity: .45; cursor: default; }
.layouts-toolbar .lt-play[disabled]:hover { opacity: .45; }
/* The play triangle is optically left-heavy inside a circle; nudge it right.
   The pause bars are symmetrical, so the offset is undone when paused. */
.layouts-toolbar .lt-play svg { margin-left: 0; }
.layouts-toolbar .lt-play.is-paused svg { margin-left: 2px; }
.layouts-toolbar .lt-ico {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 7px; border: 0;
  background: none; color: var(--ink65); cursor: pointer;
}
.layouts-toolbar .lt-ico:hover { color: var(--ink); background: var(--btn2bg); }
/* Restart is part of the greyed still transport (5 Aug 2026) — the hover lift
   is neutralised so a dead control doesn't read as pressable, matching
   .lt-play[disabled]. */
.layouts-toolbar .lt-ico[disabled] { opacity: .45; cursor: default; }
.layouts-toolbar .lt-ico[disabled]:hover { color: var(--ink65); background: none; }
/* The rail takes the slack, so the bar keeps its shape whatever the template
   name's length. A generous invisible hit area above and below the 4px line
   makes it scrubbable without making it look heavy. */
.layouts-toolbar .lt-prog {
  flex: 1 1 auto; min-width: 60px; height: 4px; border-radius: 2px;
  background: var(--bmed); cursor: pointer; position: relative;
  touch-action: none;
}
.layouts-toolbar .lt-prog::before {
  content: ''; position: absolute; left: 0; right: 0; top: -9px; bottom: -9px;
}
.layouts-toolbar .lt-prog > i {
  display: block; height: 100%; width: 0; border-radius: 2px;
  background: var(--ink); pointer-events: none;
}
/* Tabular figures so the readout doesn't jitter as the seconds tick. */
.layouts-toolbar .lt-time { flex: none; font-variant-numeric: tabular-nums; }
/* A board on Still keeps the whole transport and greys it (Mark, 5 Aug 2026:
   "when off, it should still have the progress but greyed out. the jump in
   width is not good"), so the bar holds ONE width whether the motion is on or
   off. Same .45 dead-control opacity as .lt-play[disabled] and .lt-btn[disabled]
   above; the rail loses its scrub cursor and its pointer events, because there
   is no clock behind it to seek. */
.layouts-toolbar .lt-prog.is-off { opacity: .45; cursor: default; pointer-events: none; }
.layouts-toolbar .lt-time.is-off { opacity: .45; }
/* The bar sits UNDER the plate now, so its menu has to open UPWARD — downward
   would run straight into .layouts-stage's overflow:hidden. */
.layouts-toolbar .lt-anim-menu { top: auto; bottom: calc(100% + 6px); }
/* Still heads the motion menu and is separated from the motions themselves. */
.lt-anim-sep { display: block; height: 1px; margin: 4px 2px; background: var(--rule-faint); }

/* ── #309 Composition group in the Layouts side bar ────────────────────────
   Everything that COMPOSES the board — Size, the screen/column and row
   steppers, Split / Overlap / Fan / Wide / Length, Reorder / Done and + Add —
   moved out of the toolbar into a group named after the template. It adopts the
   panel's own vocabulary (.opt-toggle-row, .num-stepper, .pill-select,
   .opt-switch) rather than carrying the toolbar's .lt-btn across, and it
   disappears entirely on templates with no geometry. */
.layouts-cgroup { margin: 0; }   /* no rule under it (10 Sep 2026, Mark: "too many lines") */
.layouts-cgroup-h {
  font-family: var(--sans); font-size: 11.5px; letter-spacing: -0.005em;
  text-transform: none; color: var(--ink-mute); margin: 0 0 2px; font-weight: 500;}
.layouts-cgroup .opt-toggle-row { padding: 12px 0; }
/* A control that needs its label above rather than beside it (the pill sets
   that would otherwise wrap awkwardly against a long label). */
.layouts-cstack { padding: 12px 0 2px; }
.layouts-cstack .field-label { margin-bottom: 9px; }
/* Reorder / Done and + Add ride together at the foot of the group. */
.layouts-cacts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.layouts-cacts .ghost-btn { flex: 1 1 auto; justify-content: center; }

/* ══════════════════════════════════════════════════════════════════════════
   THE ICON BUTTONS — one look each, twelve classes deep
   12 August 2026. Mark: "We really need to go through all our buttons. They
   seem to be all over the place in terms of matching the styling with what
   they do."

   The audit counted eleven icon-button classes across seven sizes (15, 18, 20,
   22, 24, 26, 28, 30), three corner radii and two unrelated colour schemes,
   handed out by whichever panel grew the control rather than by what the
   control is. A remove chip on a library card was 26px at 7px radius; the same
   remove on a clip card was 18px and round; on a queue cell, 15px and round
   again; on a saved reel, 22px at 6px. Nobody chose that. It accumulated.

   There are only ever TWO icon buttons in this app:

     ON MEDIA — it sits on somebody's capture, so it has to carry its own
     contrast: a dark translucent chip with a paper glyph and a backdrop blur,
     7px corner. Replace, Remove, Delete, the anchor flip, the queue cell's ×.

     IN CHROME — it sits on panel ground, which already has contrast, so it
     asks for none: transparent, --ink50, 8px corner, and a --ink08 hover pad
     that appears under the cursor. Close, kebab, the preview chrome's row.

   Each gets a DEFAULT size and a DENSE size, chosen by what it sits on rather
   than by which panel it is in — 24/18 on media, 28/22 in chrome. The audit
   said two sizes flat; two sizes flat is wrong, because a 24px chip on a 52×34
   queue cell covers the picture it is a control for. Four numbers with a rule
   is the honest collapse of seven without one.

   Written as a plain selector list at the END of the sheet, so each selector
   ties with the original it overrides on specificity and wins on source order.
   Only the LOOK is set here. Position, hover-reveal opacity and transitions
   stay with each class where they belong — a chip that appears on hover has to
   go on doing that, and its geometry is its container's business.

   Two components deliberately stay out. The transport's play/prev/next
   (.reel-icon-btn, .reel-tbtn) are round on purpose, which is what a transport
   is; and the lightbox's zoom pair is sized to the readout it sits beside.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── ON MEDIA ─────────────────────────────────────────────────────────────── */
.layouts-corner-btn,
.lib-thumb-actions .lib-thumb-btn,
.lib-fav-star,
.reel-clip-x,
.layouts-qstrip .qs-x,
.vcb-thumb-x,
.reels-del,
.reels-move {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: rgba(16, 16, 14, .82);
  color: #FBFAF8;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  cursor: pointer;
  line-height: 1;
}
.layouts-corner-btn:hover,
.lib-thumb-actions .lib-thumb-btn:hover,
.lib-fav-star:hover,
.reel-clip-x:hover,
.layouts-qstrip .qs-x:hover,
.vcb-thumb-x:hover,
.reels-del:hover,
.reels-move:hover {
  background: rgba(16, 16, 14, 1);
  color: #FBFAF8;
}
/* Default — a card big enough to carry it. */
.layouts-corner-btn,
.lib-thumb-actions .lib-thumb-btn,
.lib-fav-star,
.reels-del,
.reels-move { width: 24px; height: 24px; font-size: 14px; }
/* Dense — the picture underneath is under about 140px, so the chip steps down
   rather than sitting on top of the thing it is a control for. */
.reel-clip-x,
.layouts-qstrip .qs-x,
.vcb-thumb-x { width: 18px; height: 18px; font-size: 12px; }
/* The queue cell is 52×34 and its × is hover-only: the reveal is the class's
   own business, restated here only because this block sets `line-height` and
   the old rule leaned on `line-height: 15px` to centre the glyph. Flexbox does
   that now. */
.layouts-qstrip .qs-x { right: 3px; top: 3px; }
/* The saved-reel pair sit side by side, so the second one keeps its offset. */
.reels-move { right: 34px; }
/* The star is a STATE, not an action: it reads filled once the capture is a
   favourite, and honey rather than paper, which is the one place the accent
   earns a button. Same chip, different glyph treatment. */
.lib-fav-star.is-on { color: #F7CE46; }
.lib-fav-star.is-on svg { fill: currentColor; }

/* ── IN CHROME ────────────────────────────────────────────────────────────── */
.capt-icon-btn,
.chrome-icon-btn,
.libu-kebab,
.styleset-x,
.layouts-pk-close,
.tour-section-remove,
.app.home-v3 .hv3-x,
.home-intro-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--ink50);
  cursor: pointer;
  line-height: 1;
  transition: background .12s ease, color .12s ease;
}
.capt-icon-btn:hover,
.chrome-icon-btn:hover,
.libu-kebab:hover,
.styleset-x:hover,
.layouts-pk-close:hover,
.tour-section-remove:hover,
.app.home-v3 .hv3-x:hover,
.home-intro-x:hover {
  background: var(--ink08);
  color: var(--ink);
}
/* Default — a control with room around it. */
.capt-icon-btn,
.styleset-x,
.layouts-pk-close,
.app.home-v3 .hv3-x { width: 28px; height: 28px; font-size: 15px; }
/* Dense — a control inside a row of other things. */
.chrome-icon-btn,
.libu-kebab,
.tour-section-remove,
.home-intro-x { width: 22px; height: 22px; font-size: 13px; }
/* The kebab is hidden until its folder is hovered — that stays. `display` is
   the switch, so it has to be restored to the flex the block above sets. */
.libu-kebab { display: none; }
.libu-folder:hover .libu-kebab { display: inline-flex; }

/* ══════════════════════════════════════════════════════════════════════════
   THE RAIL IS ONE SIZE
   12 August 2026. Mark: "why is the styling reset button in layouts bigger
   than the reset button for the modes? everything should be consistent."

   It was, and so was every other control in that panel. The right column's
   controls are sized one notch down by rules scoped to `.is-tour-sequence` —
   a class the tour named and every capture mode inherited when its rail was
   built. The Layouts panel renders into the SAME #previewPanel and never gets
   that class, so it ran at the app's full sizes: buttons 12px/9×14 against the
   rails' 11px/4×10, labels 13px against 12.5, accordion headers 15px/14px
   padding against 12.5px/11px. Reset styling was simply the one with a twin
   two clicks away, so it was the one that got noticed.

   The size belongs to the RAIL, not to a mode. `:has(…-panel-head)` is the
   honest way to say "this column is currently a MODE PANEL" without handing it
   a class whose name is about the tour, and without a JS toggle that six other
   renderers would each have to remember to clear.

   GENERALISED 17 Aug 2026 (#288). Style renders into this column too and had no
   head at all, so it was running at the app's full sizes exactly as Layouts was
   before this block — the same drift, one mode later, and invisible because
   Style had no twin two clicks away either. `.mode-panel-head` is the shared
   hook; `.layouts-panel-head` stays in the list so Layouts' own markup and the
   rules keyed on it are untouched.

   Pills are untouched: they already matched at 11px/5×12, which is a small
   sign the rest was drift rather than intent.
   ══════════════════════════════════════════════════════════════════════════ */
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .ghost-btn {
  font-size: 11px;
  padding: 4px 10px;
}
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .option-label,
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .field-label {
  font-size: 12.5px;
}
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .option-summary {
  font-size: 12.5px;
  padding: 11px 0;
}
/* …with the two exceptions the rails already make for themselves: a
   full-width action keeps its own metrics (Export is a .btn-primary and the
   "see it rendered" button is deliberately 38px), and the panel's own title is
   a heading, not a control. */
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .btn-primary,
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .see-render-btn { font-size: 12.5px; padding: 0 14px; }
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .layouts-panel-title { font-size: 13px; }

/* THE STYLE + EXPORT MODAL is the third surface these settings render on, and
   it is 308px wide — the same column width as the rail. Same sizes, then. Its
   pills already agreed (line 12197); nothing else did. */
.styleset-side .ghost-btn { font-size: 11px; padding: 4px 10px; }
/* ONE DENSITY FOR EVERY STYLING SIDEBAR (Mark, 15 Aug: "why is everything so
   big in the styling side bar? need consistency"). The area rail
   ([data-iso-rail]) wears exactly the Style + export sidebar's compact tier —
   same selectors, same values, one place to change them. */
.styleset-side .option-label,
[data-iso-rail] .option-label,
.styleset-side .field-label,
[data-iso-rail] .field-label { font-size: 12.5px; }
.styleset-side .option-summary,
[data-iso-rail] .option-summary { font-size: 12.5px; padding: 11px 0; }
[data-iso-rail] .opt-toggle-row { padding: 11px 0; }
/* No hairlines between the rows (Mark, 15 Aug: "consistent with all other
   side bars") — the Layouts panel's rule, applied here. */
[data-iso-rail] .option-row { border-bottom: 0; }
/* The kit bar keeps its own slightly larger buttons — it is a shelf of saved
   styles, not a setting. */
.styleset-side .styleset-kit-btns .ghost-btn { font-size: 12px; padding: 6px 12px; }

/* The DASHED add is its own tier and has its own rail treatment — full width,
   12px padding, a 10px corner (line 10562). That is deliberate: `.is-add` means
   "make something that does not exist yet", and in a column of settings it
   reads as the empty slot at the end of a list rather than as another command.
   The Layouts panel and the modal are that same column, so they take it too —
   otherwise the notch-down rule above would quietly flatten their adds into
   ordinary buttons, which is the same drift in the other direction. */
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .ghost-btn.is-add,
.styleset-side .ghost-btn.is-add {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px;
  border-radius: 8px;
  font-size: 11.5px;
}

/* ── …EXCEPT the Add + Paste pair (#314, 17 Aug 2026) ──────────────────────
   Mark: "text layer in layouts has gone back to this? we fixed this before".
   He was right, and this rule above is what did it. It arrived in round 31 and
   carries an id, so it outranked round 28's `.tx-addrow > .ghost-btn.is-add`
   (0,3,0) and put `width: 100%` back on both buttons — which inside a flex row
   whose Paste is `flex: 0 0 auto` gave Paste the whole 264px and left Add a
   26px stub. Measured, not guessed; see verify-17aug-text-controls.cjs.

   The rule above is still right for every OTHER dashed add in these rails —
   "make something that does not exist yet", full width, the empty slot at the
   end of a list. The Add/Paste pair is the one place two of them sit side by
   side, and there the full-width tier is not a tier but a contradiction.

   Specificity (1,4,0), one class past the rule it corrects, so it cannot lose
   the same way twice. Everything else about the pair — the flex ratios, the
   nowrap, the type size — still comes from .tx-addrow, which stays the single
   place all three panels read. */
#previewPanel:has(.layouts-panel-head, .mode-panel-head) .tx-addrow > .ghost-btn.is-add,
.styleset-side .tx-addrow > .ghost-btn.is-add {
  width: auto;
  padding: 7px 10px;
}

/* ══════════════════════════════════════════════════════════════════════════
   THE CANVAS TOOLBAR IS ONE HEIGHT
   12 August 2026. Mark: "Why is the +- pill taller than the motion button.
   Make them the same height. its just weird why things would be slightly
   different sizes. Need consistency."

   Measured, before: play 28, restart 26, the motion button 29, the zoom
   stepper 34. Four heights in a strip 700px wide, none of them chosen against
   the others — each control was sized when it was written and never compared
   with its neighbours. The stepper is the one you notice because it is both
   the tallest and the one with a border around it.

   28px, which is the app's control height in a bar (.capt-icon-btn is 28) and
   already what the play button was. Set on the CONTROLS, not on the bar: a bar
   with a fixed height and free-floating children is how they drifted apart in
   the first place.
   ══════════════════════════════════════════════════════════════════════════ */
.layouts-toolbar .lt-play,
.layouts-toolbar .lt-ico,
.layouts-toolbar .lt-btn,
.layouts-toolbar .num-stepper {
  height: 28px;
  box-sizing: border-box;
  flex: none;
}
/* The two icon buttons stay square — a circle and a rounded square of the same
   height, which is what they were before one of them was 26. */
.layouts-toolbar .lt-play,
.layouts-toolbar .lt-ico { width: 28px; }
/* The stepper's own children stretch to it (align-items: stretch), so they need
   no height of their own — but its 12px radius belonged to the taller box. At
   28px that is nearly a stadium beside an 8px button; 8px matches the motion
   control it sits next to and every other small button in the app. */
.layouts-toolbar .num-stepper { border-radius: 8px; }
/* The readout is 15px inside a 34px box; at 28px it needs the bar's own type
   size, which is what the label beside it uses. */
.layouts-toolbar .num-stepper .num-step-val,
.layouts-toolbar .num-stepper .num-step-unit { font-size: 12px; }
.layouts-toolbar .num-stepper .num-step { padding: 0 9px; }

/* ══════════════════════════════════════════════════════════════════════════
   THE TWO AREA TOOLS (Molly, via Mark, 13 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   Area capture is the one mode whose input IS the page, so the two ways of
   taking an area belong above the page rather than in the rail on the far side
   of the screen — you should not have to leave the thing you are aiming at.

   They live in the toolbar that is ALREADY there, next to Reload and the device
   switcher, separated by a hairline. The first version shipped them as a second
   bar underneath it; two stacked centred bars read as two competing chromes, and
   Molly's board is right that there should be one.

   NOT a duplicate of a rail control. The reviewed design carried this pair in
   BOTH places — two controls doing one job, able to disagree about which is
   armed. This is the single home for them; the rail keeps the list, the count
   and the styling.
   ══════════════════════════════════════════════════════════════════════════ */
/* Scoped to the class, not to a bar: the buttons are authored inside
   .lab-toolbar so they have a home to return to, and relocate into .lab-chrome
   when the editor opens (moveStageToolsToChrome). They must look right in both.
   `stage-tool`, not `area-tool` — Page Tour's two stop tools are the same
   control in the same bar, and a class named for one mode would have been a lie
   the second one arrived. */
.stage-tool {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  padding: 5px 11px;
}
.stage-tool[hidden] { display: none; }
.stage-tool svg { flex: none; }
/* In the chrome bar the tools have to out-specify .lab-chrome-tools .ghost-btn,
   which strips borders off everything in there. These two keep theirs: they are
   the mode's actual controls sitting among page-level chrome, and an outline is
   what separates "press me" from "Reload". Draw area's is DASHED — the same
   mark its icon and its on-page box use (Molly). */
/* Base look; the shell-modetop block near the stage-toolbar rules carries the
   version that actually wins inside .lab-chrome-tools. */
.lab-chrome-tools .stage-tool {
  font-size: 11.5px;
  padding: 0 11px;
  height: 28px;
  border: 1px solid var(--bmed);
  gap: 6px;
}
/* ARMED. The pressed tool stays lit for the whole pick, so the page and the
   toolbar agree about what is happening — the old flow gave no standing sign
   that a pick was running once the instruction banner had been read and
   ignored. */
.stage-tool.is-armed {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.stage-tool.is-armed:hover { background: var(--ink); color: var(--paper); }

/* The rail's own count. Sits on the Areas step label, right-aligned, in the
   numeral register the rows use — so "how many have I got" is answerable
   without counting cards. */
.iso-areas-count {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .05em;
  color: var(--ink50);
  margin-left: auto;
  font-weight: 400;
  text-transform: none;
}
.step-label.has-count { display: flex; align-items: baseline; }

/* The areas count, rendered inside #iso-sections so it travels with the cards
   into the Visual Editor rail. Reads as a section label rather than a card. */
.iso-areas-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink50);
  margin: 0 0 9px; font-weight: 500;}
.iso-areas-head .iso-areas-count { letter-spacing: .05em; }

/* ══════════════════════════════════════════════════════════════════════════
   THE EMPTY STATE (Molly, via Mark, 13 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   An empty Area capture board is a page with nothing on it and a rail with
   nothing in it, and nothing anywhere saying that this mode takes SEVERAL
   regions in one pass — which is the whole reason it exists.

   It began life as a card floated over the page. It belongs in the rail: that
   is the column the areas themselves land in, so the instruction sits directly
   above its own result, and it never covers the element it is telling you to
   pick. It REPLACES the AREAS header rather than sitting under it — a header
   reading "None yet" above a card reading "No areas captured yet" is the same
   sentence twice.

   Flat: no shadow, no absolute position. In the rail it is a card among cards,
   not something floating above the page.
   ══════════════════════════════════════════════════════════════════════════ */
.iso-empty {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 15px;
  background: var(--card-fill);
  border: 1px solid var(--bmed);
  border-radius: 12px;
}
.iso-empty[hidden] { display: none; }
.iso-empty-ico { flex: none; color: var(--ink50); display: inline-flex; }
.iso-empty-body { font-size: 12.5px; line-height: 1.45; color: var(--ink65); }
.iso-empty-body b {
  display: block;
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 2px;
}

/* ═══ AREA CAPTURE — DOOR AND STAGE (14 Aug 2026) ══════════════════════════
   Two states in one panel, never both. Before a capture the panel is a DOOR:
   one decision, two ways in, one button. After a capture it is a STAGE: the
   plates themselves, dressed live by the styling rail.

   Deliberately NOT the step form. A numbered 01/02/03 spine says "work down
   this list"; there is no list here, there is one decision and one button.

   ON GREY (Mark, 14 Aug 2026: "i am not a fan of the grey canvas", and before
   that "Really not liking the grey when it is not a canvas with preview"). The
   work-area tint exists to sit BEHIND a preview of a page. Here the plates
   carry their own background — that is the whole point of the padding control
   — so a tinted stage would be a surface behind a surface, and the plate's
   white would have to fight it. Paper, both states, and the plate's hairline
   is what separates the two.
   ────────────────────────────────────────────────────────────────────────── */

/* The off-source strip. Honey rather than red: the window being on another
   page is a FACT to notice, not an error to fix — captures taken there work. */
.pc-offsource {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 20px;
  padding: 11px 14px;
  border-radius: 12px;
  background: rgba(247, 206, 70, .18);
  border: 1px solid rgba(247, 206, 70, .5);
  font-size: 12.5px;
  color: var(--ink);
}
.pc-offsource[hidden] { display: none; }
.pc-off-txt { flex: 1; min-width: 0; line-height: 1.45; }
.pc-off-txt b { font-weight: 500; }
.pc-off-btn {
  flex: none;
  height: 28px;
  padding: 0 12px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  background: var(--card-fill);
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink);
  cursor: pointer;
}
.pc-off-btn:hover { border-color: var(--ink); }
.pc-off-btn.is-primary { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* ── The door ─────────────────────────────────────────────────────────────
   Centred column, capped narrow. Everything on it is one of three things: the
   decision, the explanation, or the action — in that order, top to bottom. */
/* .is-ACTIVE, not bare .is-precapture. `.panel { display: none }` is what hides
   every inactive panel, and `.panel.is-precapture` matches it at the same
   specificity while sitting later in the file — so a bare rule here displayed
   the Area panel on TOP of the Library, the Guide and Settings (Mark, 14 Aug:
   "when i click libray or guide, it just has the area capture page"). Adding
   .is-active puts it a class above the hide rule, where it belongs. */
.panel.is-precapture.is-active { display: flex; flex-direction: column; min-height: 0; }
.pc-door {
  width: 100%;
  max-width: 452px;
  margin: 0 auto;
  padding: 8px 24px 0;
  text-align: center;
}
.pc-door[hidden] { display: none; }
/* The mode's own glyph, at the top, above its heading (Mark: "I think we
   should have the icon for the mode at the top with a heading"). Dashed, like
   every area-drawing affordance in the product. */
.pc-glyph {
  width: 46px; height: 46px;
  border: 1.6px dashed var(--ink32);
  border-radius: 11px;
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 22px;
}
/* (inner square removed — the glyph is the dashed area alone; Mark, 15 Aug) */
.pc-h {
  font-family: var(--sans);
  font-size: 31px;
  font-weight: 500;
  letter-spacing: -.035em;
  line-height: 1.12;
  margin: 0 0 11px;
}
.pc-sub {
  font-size: 13.5px;
  color: var(--ink50);
  line-height: 1.6;
  margin: 0 auto 32px;
  max-width: 40ch;
}

/* ── Devices ──────────────────────────────────────────────────────────────
   The toolbar's language, given room. Individual outline buttons; the live one
   filled ink. No "Device" heading above them (Mark: "no need") and no Custom
   (Mark: "we dont have custom. remove") — More sizes covers the real need. */
.pc-devs { margin: 0 0 30px; }
.pc-dev-row { display: flex; gap: 8px; margin: 0 0 10px; }
.pc-dev {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 14px 6px 12px;
  border: 1px solid var(--bmed);
  /* 10px + no fill — the door's one card rule (Mark, 15 Aug: "device cards
     are not background colour"): hairline alone, on the background. */
  border-radius: 10px;
  background: transparent;
  color: var(--ink50);
  font-family: var(--sans);
  cursor: pointer;
  transition: border-color .14s ease, background .14s ease;
}
.pc-dev:hover { border-color: var(--bstr); }
.pc-dev b { font-size: 12.5px; font-weight: 500; color: var(--ink); }
.pc-dev span { font-family: var(--mono); font-size: 10px; color: var(--ink32); }
.pc-dev.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.pc-dev.is-on b { color: var(--paper); }
.pc-dev.is-on span { color: rgba(255, 255, 255, .5); }
.pc-dev-more {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6px;
  width: 100%;
  border: 0;
  background: transparent;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink32);
  cursor: pointer;
  padding: 4px 0;
}
.pc-dev-more:hover { color: var(--ink65); }
.pc-dev-more .pc-dev-cv { transition: transform .16s ease; }
.pc-dev-more.is-open .pc-dev-cv { transform: rotate(180deg); }
/* More sizes: the same rows the topbar dropdown shows, unfolded in place
   rather than in a menu — there is room on this page and a dropdown over a
   near-empty column is a menu for the sake of one. */
.pc-sizes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; margin: 4px 0 0; }
.pc-ori { display: flex; gap: 6px; margin: 0 0 8px; justify-content: center; }
.pc-size {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 9px 11px;
  border: 1px solid var(--bmed);
  border-radius: 9px;
  background: var(--card-fill);
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.pc-size .pc-size-eg { display: block; font-size: 10px; color: var(--ink32); margin-top: 2px; }
.pc-size .pc-size-dims { margin-left: auto; font-family: var(--mono); font-size: 10px; color: var(--ink32); }
.pc-size.is-on { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }

/* ── The two ways ─────────────────────────────────────────────────────────
   Not decoration. Pressing one opens the window with that tool already armed,
   so the card is the shortcut as well as the explanation. */
.pc-ways { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 0; }
.pc-way {
  border: 1px solid var(--bmed);
  /* 10px — the app-wide card radius — and NO fill (Mark, 15 Aug: "cards are
     not background colour"): the hairline alone is the card, same rule the
     stage's capture cards follow. */
  border-radius: 10px;
  background: transparent;
  padding: 18px 14px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  text-align: center;
  font-family: var(--sans);
  color: var(--ink);
  transition: border-color .14s ease;
}
.pc-way:hover { border-color: var(--bstr); }
/* ── ON ANY TAB — one block, one key per mode (27 Aug 2026) ─────────────────
   Grew from a one-line footnote the same evening (Mark: "each mode should
   have this a 'way to capture' in the rail… lets add these to all three
   modes"). Same anatomy in every rail — mono eyebrow, the mode's own key as
   a pill, a name and one sentence — seated after the mode's ways, before
   Settings. .pc-vp-ambient stays as the class the relocation machinery keys
   on (canvasNodeExcluded + the loose rail-foot placement); .pc-anytab is the
   dress. */
/* --bmed, not --ink18: every other block on this door draws its hairline in
   --bmed, and 0.18 against 0.16 is the kind of difference you cannot name but
   can see when they are stacked. (28 Aug 2026) */
.pc-anytab { border: 1px solid var(--bmed); border-radius: 10px; padding: 10px 12px 11px; margin-top: 14px; text-align: left; }
.pc-anytab-lbl { display: block; font-family: var(--mono); font-size: 8.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink32); margin-bottom: 7px; }
.pc-anytab-row { display: flex; align-items: center; gap: 11px; }
/* THE SYMBOLS WERE THE TINY THING (27 Aug 2026, third pass — Mark: "really
   tiny… still tiny… SPACE is probably a bit too big"). The mono font draws
   ⌥ and ⇧ at small glyph size, so scaling the whole pill only made the WORD
   shout while the symbols stayed small. Split: the modifier symbols are the
   pill's face at 16px; the key name sits quiet beside them. */
.pc-anytab kbd {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em; white-space: nowrap;
  border: 1px solid var(--bmed); border-radius: 8px; padding: 8px 11px; color: var(--ink); background: var(--paper); flex: none;
  display: inline-flex; align-items: center; gap: 4px;
}
/* ── THE MODIFIER SYMBOLS COME OUT OF THE MONO STACK (28 Aug 2026) ─────────
   Mark, twice: "the shift arrow still looks small and oddly positioned."

   It was font FALLBACK, not sizing. The mono stack has ⌥ and does not have ⇧,
   so ⇧ was being drawn by whatever the OS substituted — a different size on a
   different baseline, sitting next to a glyph from another face. No amount of
   font-size or vertical-align equalises that, because the two glyphs are not
   from the same font; the earlier passes were tuning one half of a mismatch.

   So both symbols move to the SYSTEM font, which has ⌥ and ⇧ and draws them as
   a matched pair. Explicitly -apple-system and not var(--sans): the brand face
   may not carry these two glyphs either, and falling back to the system font by
   accident is what got us here. The key NAME stays mono — "Space", "A" — so the
   pill still reads as a key. 15px, where mono needed 16 to look the same size.
   (Chosen from mockup-28aug-glyph-test.html, option C, on Mark's Mac — the
   fault is invisible from a Linux harness.) */
.app.home-v3 .hv3-key kbd .hv3-key-sym,
.capt-key-note kbd .ckn-sym,
.pc-anytab kbd .pc-key-sym {
  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  font-size: 15px; line-height: 1; letter-spacing: .02em;
}
.pc-anytab-tx b { display: block; font-size: 12.5px; font-weight: 500; }
/* 11.5/1.45/2px — the door's one description setting, shared with the cards'
   own second lines. (28 Aug 2026) */
.pc-anytab-tx span { display: block; font-size: 11.5px; color: var(--ink50); line-height: 1.45; margin-top: 2px; }
.pc-way:disabled { opacity: .42; cursor: default; }
.pc-way b { font-size: 13px; font-weight: 500; }
.pc-way > span { font-size: 11.5px; color: var(--ink50); line-height: 1.45; max-width: 22ch; }
.pc-way svg { margin-bottom: 2px; color: var(--ink); }

/* ── The one action ───────────────────────────────────────────────────────
   Full width and unmissable — everything above it is a preference, and this is
   the thing that starts the work. Its own rule, not .btn-primary: that class
   carries width:100% AND margin-top:24px, which squashed the label into a
   one-word column when it sat here. */
.pc-launch {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 54px;
  margin-top: 24px;
  border: 0;
  border-radius: 13px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: transform .16s cubic-bezier(.2, .7, .2, 1), opacity .15s ease;
}
.pc-launch:hover:not(:disabled) { transform: translateY(-1px); }
.pc-launch:disabled { opacity: .38; cursor: default; }
.pc-launch-sub {
  margin: 14px 0 0;
  text-align: center;
  font-size: 12.5px;
  color: var(--ink32);
  line-height: 1.5;
}
/* Quick capture (16 Aug round 12, Mark: "We had all three device icons on.
   Need that again… 'quick capture' with a hint underneath"): the trio's own
   glyphs ride the button, the hint sits under it in the door's quiet voice. */
.pc-quick-devs { display: inline-flex; align-items: center; gap: 5px; }
.pc-quick-hint {
  margin: 6px 0 0;
  text-align: center;
  font-size: 11.5px;
  color: var(--ink50);
  line-height: 1.5;
}
/* THE HINT RIDES THE CARD (27 Aug 2026, Mark). The control grows from a one-
   line pill into a two-line card, so the height cap comes off and the button
   stacks its rows; the hint keeps the door's quiet voice, now inside the
   border like the other two cards' own subtitles. .pc-quick-hint above stays
   for the harnesses that pin the round-12 markup era. */
.pc-launch.pc-vp-quick-card {
  flex-direction: column;
  gap: 7px;
  height: auto;
  padding: 16px 14px;
}
/* BIGGER TRIO (27 Aug 2026, Mark: "the icons in the rail are really tiny, so
   hard to see"). The glyphs carried 15-16px inline sizes from their topbar
   life; in the rail card they are the card's face. CSS outranks the width/
   height attributes, so the markup stays untouched. */
.pc-vp-quick-card .pc-quick-devs svg:nth-child(1) { width: 23px; height: 23px; }
.pc-vp-quick-card .pc-quick-devs svg:nth-child(2) { width: 20px; height: 20px; }
.pc-vp-quick-card .pc-quick-devs svg:nth-child(3) { width: 18px; height: 18px; }
.pc-vp-quick-card .pc-quick-ttl { font-size: 13px; font-weight: 500; }
.pc-vp-quick-card .pc-quick-sub {
  font-size: 11.5px;
  font-weight: 400;
  color: var(--ink50);
  /* 1.45, the door's one description leading — it was the last number on this
     block that still disagreed with its neighbours. (28 Aug 2026) */
  line-height: 1.45;
}

/* ── One door, two modes (15 Aug 2026; rescoped #319, 18 Aug 2026) ───────────
   The door is a single DOM instance shared by Area and Viewport capture
   (adoptStageFor). Elements marked for one mode hide in the other; the
   glyph swaps from the dashed area to a solid viewport frame.

   KEYED ON .app, NOT ON THE DOOR. In canvas mode the right-hand rail IS the
   door: moveOptionsToSide MOVES the door's children out of #pc-door and into
   #canvas-settings, so a rule naming .pc-door as an ancestor stops matching the
   instant the rail is built — and both modes' controls showed in both rails.
   A relocation can move a control out of any container; it cannot move it out
   of .app. (Found by measuring the rebuilt rail, not the door.)

   The GLYPH keeps the door selector, because the glyph is a direct child of the
   door and is hidden entirely in canvas mode — it never travels. */
.app:not(.capture-vp) .pc-mode-vp { display: none; }
.app.capture-vp .pc-mode-area { display: none; }
/* …and once more, one class heavier, for the RELOCATED copies. #canvas-settings
   re-declares display on some of these (`#canvas-settings .pc-ways` restacks the
   cards one per row for a 300px column, at 1-0-1-0), which outranks the .app
   rules above and un-hid Area's ways grid inside Viewport's rail. Answered in
   its own shape rather than with !important — the phase-0 precedent, and the
   same fix that block used for .caps-strip. */
.app:not(.capture-vp) #canvas-settings .pc-mode-vp { display: none; }
.app.capture-vp #canvas-settings .pc-mode-area { display: none; }
.pc-door.is-viewport .pc-glyph { border-style: solid; border-width: 1.6px; }
/* The quiet second way in — outline, under the primary, same footprint. */
.pc-launch.is-quiet {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--bmed);
  margin-top: 10px;
}
.pc-launch.is-quiet:hover:not(:disabled) { border-color: var(--ink32); transform: none; }
/* [hidden] HAS TO BE SAID (#325). .pc-launch sets `display: flex`, which beats
   the UA's [hidden] rule silently — so a button marked hidden in markup, or
   hidden by syncVpDoorControls, stayed on screen and Capture viewport appeared
   with no window to capture into. Exactly the trap .ghost-btn[hidden] exists
   for, in the same stylesheet, with the same note beside it. Any class that
   sets display owes its own [hidden]. */
.pc-launch[hidden] { display: none; }
/* The one motion row — a quiet centred pair, not an option accordion. */
.pc-vp-motion {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-top: 22px;
}
.pc-vp-motion-t { font-size: 12.5px; color: var(--ink50); }
/* #317. A settings row whose name does not explain itself gets one quiet line
   under it — "View stepper" is not a phrase anybody has met before, and the
   thing it turns on appears in a DIFFERENT window, so the row is the only place
   that can say what to expect. Freeze motion needs no such line; it is two
   words that mean themselves. */
.pc-vp-motion-t .pc-vp-motion-i {
  display: block; font-style: normal; font-size: 11px; line-height: 1.4;
  color: var(--ink32); margin-top: 2px; max-width: 30ch;
}
.pc-vp-motion:has(.pc-vp-motion-i) { align-items: flex-start; }
/* ── The step controls (#323) ────────────────────────────────────────────────
   Same row shape as the toggles above them, so the group reads as one block of
   viewport settings rather than a control that arrived from somewhere else. */
/* (#324b: the toolbar's Quick capture and its .qc-devices glyph rules went with
   the button — Quick capture is a viewport control and lives in that mode's
   rail. The rail's own copy is .pc-quick-devs, which is a different element and
   is untouched.) */
/* (#325: .pc-vp-live, .pc-vp-step and their hint went with the stepper's
   controls — stepping is the arrow keys in the capture window now. The Capture
   viewport button that remains is a plain .pc-launch.is-quiet beside Quick
   capture, and needs no rules of its own.) */
/* The adoption slot is invisible plumbing — it must never add a box. */
.pc-stage-slot { display: contents; }

/* ── This session (empty) ─────────────────────────────────────────────────
   Named for the run, not the library — the left rail is already "Latest
   captures" and two headings with the same words over two different sets is
   the duplication Mark called out. */
.pc-lat { margin-top: 44px; }
.pc-lat-head { display: flex; align-items: center; margin: 0 0 6px; }
.pc-lat-title {
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink32); font-weight: 500;}
.pc-lat-all {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 0;
  background: transparent;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--ink50);
  cursor: pointer;
}
.pc-lat-all:hover { color: var(--ink); }
.pc-lat-empty {
  border-top: 1px solid var(--border);
  padding: 26px 0 0;
  display: flex;
  align-items: center;
  gap: 16px;
  justify-content: center;
  text-align: left;
}
.pc-lat-empty svg { flex: none; }
.pc-lat-tx b { display: block; font-size: 13px; font-weight: 500; margin: 0 0 3px; }
.pc-lat-tx > span { font-size: 12.5px; color: var(--ink50); line-height: 1.5; }

.pc-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 38px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--ink32);
}
.pc-foot svg { flex: none; }

/* Help sits at the very foot, after everything it is helping with. */
/* MUCH QUIETER (Mark, 15 Aug: "make the guide hint alot quieter, not in a
   rounded card") — the guide offer is one line of quiet text, not furniture:
   no border, no fill, no bulb badge. Offered, never insisted on. */
.pc-help {
  display: flex;
  align-items: center;
  gap: 7px;
  width: auto;
  margin: 18px 0 0;
  padding: 0;
  border: 0;
  background: transparent;
  text-align: left;
  font-family: var(--sans);
  color: var(--ink50);
  cursor: pointer;
}
.pc-help:hover { color: var(--ink); }
.pc-help .pc-help-b { display: none; }
.pc-help:hover { border-color: var(--bmed); }
.pc-help-b {
  width: 26px; height: 26px;
  border-radius: 8px;
  background: rgba(247, 206, 70, .28);
  flex: none;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink);
}
/* One line, one voice: the bold lead and the grey sub collapse into a single
   quiet sentence. */
.pc-help-t { display: inline; }
.pc-help-t b { display: inline; font-size: 12px; font-weight: 500; color: inherit; }
.pc-help-t > span { font-size: 12px; color: inherit; }
.pc-help-t > span::before { content: ' '; }
.pc-help-cv { margin-left: auto; color: var(--ink32); flex: none; }

/* ── The stage ────────────────────────────────────────────────────────────
   Full width of the work column; the door's 452px cap does not apply, because
   this is a gallery and a gallery wants the room. */
.pc-stage { display: flex; flex-direction: column; min-height: 0; padding: 0 4px; }
/* The grid takes the room that is left, so "fit the stage" has a stage to fit.
   Only in One — the All grid keeps growing and scrolling, which is right for a
   set you are comparing. */
/* "Fit the stage" needs a stage with a height. flex:1 was wrong: .main is a
   SCROLLING column and the panel's height is content-driven, so there was no
   spare height to take — the grid sized to its own content, the plate came out
   small, and everything below it was empty (Mark: "why is single view like
   this?"). A viewport measure is the honest one, and 72vh is the figure the
   styling overlay's own gallery already uses. Only in One; the All grid keeps
   growing and scrolling, which is right for a set you are comparing. */
.pc-stage .iso-caps-grid.is-one { min-height: min(72vh, 780px); }
.pc-stage[hidden] { display: none; }
.pc-gbar {
  display: flex;
  align-items: center;
  gap: 9px;
  /* ROUND 16 (Mark: buttons floating on the grey), REWORKED ROUND 17 (Mark:
     "its cut off at top"): the full-bleed band relied on negative margins
     matching the main's padding — and Page Capture's main pads 10px, not 24,
     so the band's top vanished above the scroll box. The toolbar is a
     CONTAINED card now — paper, hairline all round, rounded — which reads as
     a toolbar in every mode and cannot be clipped by any ancestor's padding,
     by construction. */
  margin: 0 0 20px;
  padding: 10px 14px;
  background: var(--paper);
  border: 1px solid var(--ink08);
  border-radius: 12px;
}
.pc-gbar-title {
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink32); font-weight: 500;}
.pc-gbar-count {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink50);
  background: var(--pill);
  border-radius: 5px;
  padding: 3px 6px;
}
.pc-gbar-count:empty { display: none; }
.pc-gbar-sp { flex: 1; }
/* One | All, said as pictures rather than words — the pair reads as a SHAPE
   the view takes, where One and All read as a quantity (Mark, 11 Aug 2026:
   "one and all toggles are a bit odd"). Only present with more than one
   capture: a toggle between "this one" and "this one" does nothing. */
.pc-gbar-views { display: inline-flex; border: 1px solid var(--bmed); border-radius: 9px; overflow: hidden; }
.pc-gbar-views:empty { display: none; }
.pc-gbar-view {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 28px;
  border: 0;
  background: var(--card-fill);
  color: var(--ink50);
  cursor: pointer;
}
.pc-gbar-view.is-on { background: var(--ink); color: var(--paper); }
.pc-gbar-more {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 28px;
  padding: 0 12px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  background: transparent;
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink65);
  cursor: pointer;
}
.pc-gbar-more:hover { border-color: var(--bstr); color: var(--ink); }

/* ── The plates ───────────────────────────────────────────────────────────
   The shipping preview-styling overlay's grid, verbatim: auto-fill from 210px,
   20/18 gap. THE FRAME IS THE PLATE — it carries padding% and background, and
   the IMAGE inside carries corners, border and shadow. That is what makes the
   cast belong to the capture rather than to a card around it.

   Its only chrome is a hairline, which is also what lets the plate read on
   paper now the stage is no longer tinted. */
.iso-caps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 20px 18px;
  align-content: start;
  min-width: 0;
}
/* ONE VIEW: fit, and the step control stays put. The grid solves the plate to
   the stage's height as well as its width (see redressAreaCaptures), so nothing
   here needs to scroll — and the arrows are pinned at the foot rather than
   riding under a tall plate. */
.iso-caps-grid.is-one {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  align-content: center;
  overflow: hidden;
  position: relative;
}
.iso-caps-grid.is-one .iso-cap { display: flex; flex-direction: column; align-items: center; }
@media (max-width: 700px) { .iso-caps-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); } }
.iso-cap { min-width: 0; margin: 0; cursor: pointer; }
.iso-cap-frame {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--card-fill);
  box-shadow: 0 0 0 1px rgba(14, 14, 12, .07);
  max-width: 100%;
  margin: 0 auto;
  transition: box-shadow .14s ease;
}
.iso-cap:hover .iso-cap-frame { box-shadow: 0 0 0 1px var(--ink32); }
/* NOTE (#307, 17 Aug 2026): 157 lines of this section were in the stylesheet
   TWICE, verbatim — this block and an identical copy ~620 lines further down.
   The later copy won every rule, so the earlier one was dead weight and, worse,
   a trap: editing the first copy and seeing nothing change is a bug that looks
   impossible. The duplicate is gone; the surviving copy is below, beside the
   styling rail it belongs to. */
/* Pills, like Style + export's own foot — a ghost one and a dark one, the two
   shapes the product ends every flow on (Mark: "The export button needs to be
   round pill plus we need save to library?"). */
.iso-caps-btn {
  height: 32px;
  padding: 0 16px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  /* ROUND 20 (Mark: "the style button in review needs to be a clear outline
     button. it is a different colour t background. plus the delete, clear,
     select all need to be consistent buttons.") — every secondary on the
     toolbar is the same clear outline; Export alone keeps the ink fill. */
  background: transparent;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--ink);
  cursor: pointer;
}
.iso-caps-btn:hover { border-color: var(--ink); }
.iso-caps-btn.is-primary { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.iso-caps-btn.is-primary:hover { opacity: .9; }
.iso-caps-btn:disabled { opacity: .45; cursor: default; }

/* ── The styling rail ─────────────────────────────────────────────────────
   Mark, 14 Aug 2026: "I didnt think the styling would be in a pop up. I thought
   the captures would appear like in the 'preview styling' overlay we had
   (except just in the app) with the styling side bar we have." So the rail IS
   the styling, and the overlay is left to do the one job the rail cannot —
   export at full resolution. The rows are the app's own option rows, in the app's
   own order, so nothing new has to be learned here. */
.iso-rail-empty {
  padding: 22px 2px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink50);
}
.iso-rail-empty b { display: block; font-size: 13px; font-weight: 500; color: var(--ink); margin: 0 0 4px; }
/* The scope readout wears the option-rows' own clothes — same label size,
   same mono value, same row rhythm and hairline — so "Applies to" reads as
   the first row of the rail rather than a stray caption (Mark, 15 Aug: "this
   button and applies to bit needs work"). Not an .option-summary, on purpose:
   that class is wired to the accordion toggle, and this row is a readout. */
.iso-rail-scope {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 11px 0;
  /* Borderless like the rows below it (Mark, 15 Aug: hairlines out). */
  font-size: 12.5px;
  color: var(--ink);
}
.iso-rail-scope .iso-rail-n {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink50);
}

/* The rail's rows ARE the app's option-rows — no size overrides here on
   purpose (Mark, 14 Aug 2026: "why are all the styling options so big. make
   consistent with everything else!"). I had given them bespoke padding, bigger
   labels and wrapping pills, which is exactly how one surface stops looking
   like the rest of the product. The only rule left is the grid the auto-fill
   template used to supply, now that the column count is computed. */
.iso-caps-grid { gap: 20px 18px; }

/* ── The door ─────────────────────────────────────────────────────────────
   Centred column, capped narrow. Everything on it is one of three things: the
   decision, the explanation, or the action — in that order, top to bottom. */
/* .is-ACTIVE, not bare .is-precapture. `.panel { display: none }` is what hides
   every inactive panel, and `.panel.is-precapture` matches it at the same
   specificity while sitting later in the file — so a bare rule here displayed
   the Area panel on TOP of the Library, the Guide and Settings (Mark, 14 Aug:
   "when i click libray or guide, it just has the area capture page"). Adding
   .is-active puts it a class above the hide rule, where it belongs. */
.panel.is-precapture.is-active { display: flex; flex-direction: column; min-height: 0; }
.pc-door {
  width: 100%;
  max-width: 452px;
  margin: 0 auto;
  padding: 8px 24px 0;
  text-align: center;
}
.pc-door[hidden] { display: none; }
/* The mode's own glyph, at the top, above its heading (Mark: "I think we
   should have the icon for the mode at the top with a heading"). Dashed, like
   every area-drawing affordance in the product. */
.pc-glyph {
  width: 46px; height: 46px;
  border: 1.6px dashed var(--ink32);
  border-radius: 11px;
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 22px;
}
/* (inner square removed — the glyph is the dashed area alone; Mark, 15 Aug) */
.pc-h {
  font-family: var(--sans);
  font-size: 31px;
  font-weight: 500;
  letter-spacing: -.035em;
  line-height: 1.12;
  margin: 0 0 11px;
}
.pc-sub {
  font-size: 13.5px;
  color: var(--ink50);
  line-height: 1.6;
  margin: 0 auto 32px;
  max-width: 40ch;
}

/* ── Devices ──────────────────────────────────────────────────────────────
   The toolbar's language, given room. Individual outline buttons; the live one
   filled ink. No "Device" heading above them (Mark: "no need") and no Custom
   (Mark: "we dont have custom. remove") — More sizes covers the real need. */
.pc-devs { margin: 0 0 30px; }
.pc-dev-row { display: flex; gap: 8px; margin: 0 0 10px; }
.pc-dev {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 14px 6px 12px;
  border: 1px solid var(--bmed);
  /* 10px + no fill — the door's one card rule (Mark, 15 Aug: "device cards
     are not background colour"): hairline alone, on the background. */
  border-radius: 10px;
  background: transparent;
  color: var(--ink50);
  font-family: var(--sans);
  cursor: pointer;
  transition: border-color .14s ease, background .14s ease;
}
.pc-dev:hover { border-color: var(--bstr); }
.pc-dev b { font-size: 12.5px; font-weight: 500; color: var(--ink); }
.pc-dev span { font-family: var(--mono); font-size: 10px; color: var(--ink32); }
.pc-dev.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.pc-dev.is-on b { color: var(--paper); }
.pc-dev.is-on span { color: rgba(255, 255, 255, .5); }
.pc-dev-more {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6px;
  width: 100%;
  border: 0;
  background: transparent;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink32);
  cursor: pointer;
  padding: 4px 0;
}
.pc-dev-more:hover { color: var(--ink65); }
.pc-dev-more .pc-dev-cv { transition: transform .16s ease; }
.pc-dev-more.is-open .pc-dev-cv { transform: rotate(180deg); }
/* More sizes: the same rows the topbar dropdown shows, unfolded in place
   rather than in a menu — there is room on this page and a dropdown over a
   near-empty column is a menu for the sake of one. */
.pc-sizes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; margin: 4px 0 0; }
.pc-ori { display: flex; gap: 6px; margin: 0 0 8px; justify-content: center; }
.pc-size {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 9px 11px;
  border: 1px solid var(--bmed);
  border-radius: 9px;
  background: var(--card-fill);
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.pc-size .pc-size-eg { display: block; font-size: 10px; color: var(--ink32); margin-top: 2px; }
.pc-size .pc-size-dims { margin-left: auto; font-family: var(--mono); font-size: 10px; color: var(--ink32); }
.pc-size.is-on { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }

/* ── The two ways ─────────────────────────────────────────────────────────
   Not decoration. Pressing one opens the window with that tool already armed,
   so the card is the shortcut as well as the explanation. */
.pc-ways { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 0; }
.pc-way {
  border: 1px solid var(--bmed);
  /* 10px — the app-wide card radius — and NO fill (Mark, 15 Aug: "cards are
     not background colour"): the hairline alone is the card, same rule the
     stage's capture cards follow. */
  border-radius: 10px;
  background: transparent;
  padding: 18px 14px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  text-align: center;
  font-family: var(--sans);
  color: var(--ink);
  transition: border-color .14s ease;
}
.pc-way:hover { border-color: var(--bstr); }
.pc-way:disabled { opacity: .42; cursor: default; }
.pc-way b { font-size: 13px; font-weight: 500; }
.pc-way > span { font-size: 11.5px; color: var(--ink50); line-height: 1.45; max-width: 22ch; }
.pc-way svg { margin-bottom: 2px; color: var(--ink); }

/* ── The one action ───────────────────────────────────────────────────────
   Full width and unmissable — everything above it is a preference, and this is
   the thing that starts the work. Its own rule, not .btn-primary: that class
   carries width:100% AND margin-top:24px, which squashed the label into a
   one-word column when it sat here. */
.pc-launch {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 54px;
  margin-top: 24px;
  border: 0;
  border-radius: 13px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: transform .16s cubic-bezier(.2, .7, .2, 1), opacity .15s ease;
}
.pc-launch:hover:not(:disabled) { transform: translateY(-1px); }
.pc-launch:disabled { opacity: .38; cursor: default; }
.pc-launch-sub {
  margin: 14px 0 0;
  text-align: center;
  font-size: 12.5px;
  color: var(--ink32);
  line-height: 1.5;
}

/* ── This session (empty) ─────────────────────────────────────────────────
   Named for the run, not the library — the left rail is already "Latest
   captures" and two headings with the same words over two different sets is
   the duplication Mark called out. */
.pc-lat { margin-top: 44px; }
.pc-lat-head { display: flex; align-items: center; margin: 0 0 6px; }
.pc-lat-title {
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink32); font-weight: 500;}
.pc-lat-all {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 0;
  background: transparent;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--ink50);
  cursor: pointer;
}
.pc-lat-all:hover { color: var(--ink); }
.pc-lat-empty {
  border-top: 1px solid var(--border);
  padding: 26px 0 0;
  display: flex;
  align-items: center;
  gap: 16px;
  justify-content: center;
  text-align: left;
}
.pc-lat-empty svg { flex: none; }
.pc-lat-tx b { display: block; font-size: 13px; font-weight: 500; margin: 0 0 3px; }
.pc-lat-tx > span { font-size: 12.5px; color: var(--ink50); line-height: 1.5; }

.pc-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 38px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--ink32);
}
.pc-foot svg { flex: none; }

/* Help sits at the very foot, after everything it is helping with. */
/* MUCH QUIETER (Mark, 15 Aug: "make the guide hint alot quieter, not in a
   rounded card") — the guide offer is one line of quiet text, not furniture:
   no border, no fill, no bulb badge. Offered, never insisted on. */
.pc-help {
  display: flex;
  align-items: center;
  gap: 7px;
  width: auto;
  margin: 18px 0 0;
  padding: 0;
  border: 0;
  background: transparent;
  text-align: left;
  font-family: var(--sans);
  color: var(--ink50);
  cursor: pointer;
}
.pc-help:hover { color: var(--ink); }
.pc-help .pc-help-b { display: none; }
.pc-help:hover { border-color: var(--bmed); }
.pc-help-b {
  width: 26px; height: 26px;
  border-radius: 8px;
  background: rgba(247, 206, 70, .28);
  flex: none;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink);
}
/* One line, one voice: the bold lead and the grey sub collapse into a single
   quiet sentence. */
.pc-help-t { display: inline; }
.pc-help-t b { display: inline; font-size: 12px; font-weight: 500; color: inherit; }
.pc-help-t > span { font-size: 12px; color: inherit; }
.pc-help-t > span::before { content: ' '; }
.pc-help-cv { margin-left: auto; color: var(--ink32); flex: none; }

/* ── The stage ────────────────────────────────────────────────────────────
   Full width of the work column; the door's 452px cap does not apply, because
   this is a gallery and a gallery wants the room. */
.pc-stage { display: flex; flex-direction: column; min-height: 0; padding: 0 4px; }
/* The grid takes the room that is left, so "fit the stage" has a stage to fit.
   Only in One — the All grid keeps growing and scrolling, which is right for a
   set you are comparing. */

.pc-stage[hidden] { display: none; }
.pc-gbar {
  display: flex;
  align-items: center;
  gap: 9px;
  /* ROUND 16 (Mark: buttons floating on the grey), REWORKED ROUND 17 (Mark:
     "its cut off at top"): the full-bleed band relied on negative margins
     matching the main's padding — and Page Capture's main pads 10px, not 24,
     so the band's top vanished above the scroll box. The toolbar is a
     CONTAINED card now — paper, hairline all round, rounded — which reads as
     a toolbar in every mode and cannot be clipped by any ancestor's padding,
     by construction. */
  margin: 0 0 20px;
  padding: 10px 14px;
  background: var(--paper);
  border: 1px solid var(--ink08);
  border-radius: 12px;
}
.pc-gbar-title {
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink32); font-weight: 500;}
.pc-gbar-count {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink50);
  background: var(--pill);
  border-radius: 5px;
  padding: 3px 6px;
}
.pc-gbar-count:empty { display: none; }
.pc-gbar-sp { flex: 1; }
/* One | All, said as pictures rather than words — the pair reads as a SHAPE
   the view takes, where One and All read as a quantity (Mark, 11 Aug 2026:
   "one and all toggles are a bit odd"). Only present with more than one
   capture: a toggle between "this one" and "this one" does nothing. */
.pc-gbar-views { display: inline-flex; border: 1px solid var(--bmed); border-radius: 9px; overflow: hidden; }
.pc-gbar-views:empty { display: none; }
.pc-gbar-view {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 28px;
  border: 0;
  background: var(--card-fill);
  color: var(--ink50);
  cursor: pointer;
}
.pc-gbar-view.is-on { background: var(--ink); color: var(--paper); }
.pc-gbar-more {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 28px;
  padding: 0 12px;
  border: 1px solid var(--bmed);
  border-radius: 100px;
  background: transparent;
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink65);
  cursor: pointer;
}
.pc-gbar-more:hover { border-color: var(--bstr); color: var(--ink); }

/* ── The plates ───────────────────────────────────────────────────────────
   The shipping preview-styling overlay's grid, verbatim: auto-fill from 210px,
   20/18 gap. THE FRAME IS THE PLATE — it carries padding% and background, and
   the IMAGE inside carries corners, border and shadow. That is what makes the
   cast belong to the capture rather than to a card around it.

   Its only chrome is a hairline, which is also what lets the plate read on
   paper now the stage is no longer tinted. */
.iso-caps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 20px 18px;
  align-content: start;
  min-width: 0;
}
/* ONE VIEW: fit, and the step control stays put. The grid solves the plate to
   the stage's height as well as its width (see redressAreaCaptures), so nothing
   here needs to scroll — and the arrows are pinned at the foot rather than
   riding under a tall plate. */
.iso-caps-grid.is-one {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  align-content: center;
  overflow: hidden;
  position: relative;
}
.iso-caps-grid.is-one .iso-cap { display: flex; flex-direction: column; align-items: center; }
@media (max-width: 700px) { .iso-caps-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); } }
.iso-cap { min-width: 0; margin: 0; cursor: pointer; }
.iso-cap-frame {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--card-fill);
  box-shadow: 0 0 0 1px rgba(14, 14, 12, .07);
  max-width: 100%;
  margin: 0 auto;
  transition: box-shadow .14s ease;
}
.iso-cap:hover .iso-cap-frame { box-shadow: 0 0 0 1px var(--ink32); }
/* ROUND 12: One view's frame clips at the height budget the solve computed
   (--iso-one-h, set in redressAreaCaptures) and SCROLLS when the plate is
   taller — the iframe's own grammar for a page taller than its device
   (Mark: "It needs to look like how the iframe looks"). A fitted plate never
   reaches the clamp, so the common case pays nothing. */
.iso-caps-grid.is-one.is-scroll .iso-cap-frame {
  max-height: var(--iso-one-h, none);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--ink32) transparent;
}
/* …and the pinned step control gets its own band: the card centres in the
   space ABOVE it, so a full-height plate's caption can never slide under the
   ‹ 1/2 › row (seen live the moment the frame learned to fill the budget). */
.iso-caps-grid.is-one { padding-bottom: 44px; }
/* SELECTION IS NOT A FRAME (Mark, 14 Aug 2026: "the black outline of selected
   looks like some styling"). It was a 2px ink ring hugging the plate, which is
   indistinguishable from Frame: Medium in black — the same confusion as the
   rounded corners and the card shadow, in a third place.
   Two changes fix it. HONEY, because honey is the one colour in this product
   that means "Loey is marking this" and is never a styling value — the
   background and frame swatches are white, black and whatever the user picks,
   so an accent ring can never be mistaken for one. And a PAPER ring underneath
   it, which puts a visible gap between the plate and the marking: a frame
   touches the artwork, a selection floats off it. */
.iso-cap.is-selected .iso-cap-frame {
  box-shadow: 0 0 0 1px rgba(14, 14, 12, .07), 0 0 0 3px var(--paper), 0 0 0 5px #F7CE46;
}
.iso-cap.is-selected .iso-cap-num,
.iso-cap.is-selected .iso-cap-name { color: var(--ink); font-weight: 500; }
/* Transparent is a checkerboard, not a colour — the same four-gradient tile
   the overlay uses, so the two surfaces cannot drift. */
.iso-cap-frame.is-transparent {
  background-color: #ECECEA;
  background-image:
    linear-gradient(45deg, #D3D3D0 25%, transparent 25%),
    linear-gradient(-45deg, #D3D3D0 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #D3D3D0 75%),
    linear-gradient(-45deg, transparent 75%, #D3D3D0 75%);
  background-size: 14px 14px;
  background-position: 0 0, 0 7px, 7px -7px, -7px 0;
}
.app.dark .iso-cap-frame.is-transparent {
  background-color: #3A3A38;
  background-image:
    linear-gradient(45deg, #2A2A28 25%, transparent 25%),
    linear-gradient(-45deg, #2A2A28 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #2A2A28 75%),
    linear-gradient(-45deg, transparent 75%, #2A2A28 75%);
}
.iso-cap-noimg {
  color: var(--ink32);
  font-family: var(--mono);
  font-size: 11px;
  padding: 26px 10px;
  text-align: center;
}
/* The caption is the mono index and the name. The index is what actually
   identifies a capture — names come from elementDisplayName, so a picked
   element carries the element's own and a drawn one is "Area 3" (Mark: "they
   wont have names will they?"). */
.iso-cap-cap {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 9px 2px 0;
  font-size: 12px;
  color: var(--ink65);
  min-width: 0;
}
.iso-cap-num { font-family: var(--mono); font-size: 10px; color: var(--ink50); flex: none; letter-spacing: .04em; }
.iso-cap-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.iso-cap-when { flex: none; margin-left: auto; font-family: var(--mono); font-size: 9.5px; color: var(--ink32); }
/* Style-this-one, on hover — the commonest case is dressing the single thing
   you are looking at, and selecting it first is a step for nothing. */
.iso-cap-style {
  position: absolute;
  right: 8px;
  bottom: 8px;
  height: 24px;
  padding: 0 10px;
  border: 0;
  border-radius: 7px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--sans);
  font-size: 11px;
  cursor: pointer;
  opacity: 0;
  transition: opacity .14s ease;
}
.iso-cap-hit { position: relative; }
.iso-cap-hit:hover .iso-cap-style,
.iso-cap-style:focus-visible { opacity: 1; }
/* Step arrows, in the One view. */
/* Pinned to the foot of the stage, not appended after the plate — a control
   that moves with the thing it controls is a control you have to find again
   every time it works. */
.iso-caps-step {
  position: absolute;
  left: 0; right: 0; bottom: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 0;
}
/* ONE ARROW BUTTON (#307, 17 Aug 2026). Mark: "we have 2 different types of
   arrow buttons. lets stick with the squarer ones from style. change the review
   ones." Review's were 28px circles; Style's pager is a 26px rounded square.
   Rather than copy Style's numbers here — which is how two shapes became two
   shapes in the first place — the review arrow JOINS Style's rule, up beside
   .styleset-pgbtn. What stays here is only what is genuinely review's: the
   hover, which reaches for a stronger border than Style's flat one because
   these sit on a grey plate rather than on paper. */
.iso-caps-arrow:hover { border-color: var(--ink); color: var(--ink); }
.iso-caps-pos { font-family: var(--mono); font-size: 11px; color: var(--ink50); }

/* An empty tray DEMONSTRATES the mode rather than apologising for itself. It
   only shows in the stage, which only exists once something has been captured
   and then deleted — the door covers the never-captured case. */
.iso-caps-blank {
  grid-column: 1 / -1;
  padding: 30px 0 34px;
  text-align: center;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink50);
}
.iso-caps-blank b { display: block; font-size: 13px; font-weight: 500; color: var(--ink); margin: 14px 0 4px; }
.iso-caps-blank-art { display: inline-block; }
.cb-page {
  position: relative;
  display: block;
  width: 96px; height: 62px;
  border: 1px solid var(--bmed);
  border-radius: 7px;
  background: var(--card-fill);
}
.cb-page .cb-a, .cb-page .cb-b {
  position: absolute;
  border: 1.5px solid var(--ink);
  border-radius: 3px;
  background: rgba(247, 206, 70, .18);
  animation: cbPulse 3.2s ease-in-out infinite;
}
.cb-page .cb-a { left: 8px; top: 9px; right: 8px; height: 13px; }
.cb-page .cb-b { left: 8px; top: 30px; width: 44px; height: 22px; animation-delay: .5s; }
@keyframes cbPulse {
  0%, 22% { opacity: 0; transform: scale(.96); }
  34%, 74% { opacity: 1; transform: scale(1); }
  92%, 100% { opacity: 0; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .cb-page .cb-a, .cb-page .cb-b { animation: none; opacity: 1; }
}

/* The bar appears only with a selection — a permanent action row with
   everything disabled is furniture that teaches nothing. */
.iso-caps-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.iso-caps-bar[hidden] { display: none; }
.iso-caps-sel { font-size: 12px; color: var(--ink65); }
.iso-caps-acts { margin-left: auto; display: flex; align-items: center; gap: 6px; }


/* ── The styling rail ─────────────────────────────────────────────────────
   Mark, 14 Aug 2026: "I didnt think the styling would be in a pop up. I thought
   the captures would appear like in the 'preview styling' overlay we had
   (except just in the app) with the styling side bar we have." So the rail IS
   the styling, and the overlay is left to do the one job the rail cannot —
   export at full resolution. The rows are the app's own option rows, in the app's
   own order, so nothing new has to be learned here. */
.iso-rail-empty {
  padding: 22px 2px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink50);
}
.iso-rail-empty b { display: block; font-size: 13px; font-weight: 500; color: var(--ink); margin: 0 0 4px; }
/* The scope readout wears the option-rows' own clothes — same label size,
   same mono value, same row rhythm and hairline — so "Applies to" reads as
   the first row of the rail rather than a stray caption (Mark, 15 Aug: "this
   button and applies to bit needs work"). Not an .option-summary, on purpose:
   that class is wired to the accordion toggle, and this row is a readout. */
.iso-rail-scope {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 11px 0;
  /* Borderless like the rows below it (Mark, 15 Aug: hairlines out). */
  font-size: 12.5px;
  color: var(--ink);
}
.iso-rail-scope .iso-rail-n {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink50);
}

/* The rail's rows ARE the app's option-rows — no size overrides here, on
   purpose (Mark, 14 Aug 2026: "why are all the styling options so big. make
   consistent with everything else!"). I had given them bespoke padding, larger
   labels and wrapping pills, which is exactly how one surface stops looking
   like the rest of the product. Nothing replaces them: the standard option-row
   rules already style every one of these controls, the same way they style
   Viewport's and Page Capture's.

   What DOES belong to this rail is one thing — no outer "Styling" fold (Mark:
   "I didnt mean expand all the drop downs in styling. I meant that styling
   itself shouldnt be a drop down"). That is markup, not CSS, and it lives in
   renderIsolatorPreview. */

/* The tools' hairline in the preview toolbar. They sit in the SAME bar as
   Reload and the device switcher (Molly: "theres 2 toolbars now"), so they need
   something to say where the controls every mode has end and this mode's own
   begin — a rule, not a second bar. */
/* The tools carry NO divider of their own. The chrome bar already has a system
   for this — each cluster draws a hairline on its left when the cluster before it
   is visible — and a second, differently-spaced rule next to it is exactly the
   inconsistency Mark called out. The tools sit inside .lab-chrome-tools, so that
   cluster's own hairline is theirs. */

/* ══════════════════════════════════════════════════════════════════════════
   THE CAPTURE BUTTON CONFIRMS (Mark, 13 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   "✓ 4 areas captured" for a beat, then "Capture again". The confirm state is
   the same honey button with a check on it, not a new colour — a green would be
   a fifth accent in a palette that has one, and the button has not become a
   different control, it has just finished. The width settles rather than snaps:
   the label is longer than the one it replaces, and a CTA that jumps twice in
   three seconds reads as a glitch rather than a confirmation.
   ══════════════════════════════════════════════════════════════════════════ */
[data-action="stills-component"] { transition: background-color .18s ease, color .18s ease; }
[data-action="stills-component"].is-captured {
  pointer-events: none;
  letter-spacing: .005em;
}



/* ══════════════════════════════════════════════════════════════════════════
   THE AREA ROW IS THE WHOLE CARD (Mark, 13 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   "Do we even need to edit an area … the only thing is adding click or hover
   action." No. So the expanded body is gone and the row carries what is left.

   Everything on it is a glyph, because the rail is about 280px and the name has
   to survive in it ("the card isnt that wide so cannot have the word 'action'
   and 'change' on. needs to be subtler"). Only the name grows; every control is
   a fixed 22px square, quiet until hovered.
   ══════════════════════════════════════════════════════════════════════════ */
.iso-section .tour-section-head { gap: 6px; }
/* The kind of area, as an icon rather than a caption. Not a button — it says
   what this is, it does not do anything. */
.iso-kind {
  flex: none;
  display: inline-flex;
  color: var(--ink50);
  cursor: default;
}
/* The name IS the field. Plain text until you put a caret in it — the box only
   appears on hover and focus, so a rail of names reads as a list, not a form. */
.iso-section .iso-row-name {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding: 3px 5px;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
}
.iso-section .iso-row-name:hover { border-color: var(--bmed); }
.iso-section .iso-row-name:focus {
  border-color: var(--ink);
  background: var(--paper);
  outline: none;
}
/* Every control on the row: one square, one register. */
.iso-row-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  color: var(--ink50);
  cursor: pointer;
}
.iso-row-btn:hover { color: var(--ink); border-color: var(--bmed); background: var(--ink08); }
.iso-row-btn svg { flex: none; }
/* The include tick, sized to match the glyphs beside it. */
.iso-row-tick {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  cursor: pointer;
}
.iso-row-tick input { margin: 0; accent-color: var(--ink); cursor: pointer; }
/* THE ACTION SLOT IS ONE BOX IN BOTH STATES. Quiet glyph = no action yet, press
   to add one. Inked glyph = there is one, press to fold its card. Same 22px
   either way, so a row gaining an action does not shove every row below it
   sideways — which is exactly what a chevron beside the glyph was doing. */
.iso-row-act svg { width: 13px; height: 13px; }
.iso-row-act.is-set { color: var(--ink); border-color: var(--bmed); }
.iso-row-act.is-set.is-folded { color: var(--ink65); border-color: transparent; background: var(--ink08); }
/* ⋯ menu — Change, Redraw, Show. Anchored to its own row. */
.iso-row-more { position: relative; flex: none; display: inline-flex; }
.iso-row-menu {
  position: absolute;
  top: calc(100% + 5px);
  right: 0;
  z-index: 60;
  min-width: 186px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 5px;
  background: var(--paper);
  border: 1px solid var(--bmed);
  border-radius: 10px;
  box-shadow: 0 12px 30px rgba(14, 14, 12, .16);
}
/* Our display:flex would otherwise beat the hidden attribute's UA display:none
   — the same trap the device menu documents two hundred lines up. */
.iso-row-menu[hidden] { display: none !important; }
.iso-row-menu button {
  text-align: left;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--ink65);
  padding: 7px 9px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
}
.iso-row-menu button:hover { background: var(--ink08); color: var(--ink); }

/* ══════════════════════════════════════════════════════════════════════════
   THE PAGE TOUR ROW IS THE WHOLE CARD (Mark, 13 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   Same move as Area capture. What survived the card: the stop's kind (a glyph,
   because the tool that made it carries the same one), its name, its hold, its
   actions, and a ⋯ for framing and the camera. Nothing expands.

   One difference from Area's rail, and it is deliberate: a stop HAS a number —
   how long it holds — so that number is on the row rather than behind a menu.
   It is four characters and it is the thing you tune most.
   ══════════════════════════════════════════════════════════════════════════ */
.tour-section.is-rowonly .tour-section-head { gap: 6px; }
.tour-section.is-rowonly .tour-section-head:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: 0; }
/* The kind of stop. Not a button — it says what this is, it does not do anything. */
.tour-kind { flex: none; display: inline-flex; color: var(--ink50); cursor: default; }
/* The name IS the field, same as the area rail: plain text until you put a caret
   in it. */
.tour-section.is-rowonly .iso-row-name {
  flex: 1; min-width: 0; margin: 0; padding: 3px 5px;
  border: 1px solid transparent; border-radius: 7px; background: transparent;
}
.tour-section.is-rowonly .iso-row-name:hover { border-color: var(--bmed); }
.tour-section.is-rowonly .iso-row-name:focus { border-color: var(--ink); background: var(--paper); outline: none; }
/* The hold, with its unit beside it — a number input cannot carry one, and a
   bare "1" in a rail of stops reads as an index rather than a second. The wrap is
   the hit area, so clicking the s puts the caret in the field. */
.tour-hold-wrap {
  flex: none;
  display: inline-flex;
  align-items: center;
  height: 22px;
  border: 1px solid transparent;
  border-radius: 7px;
  padding-right: 4px;
}
.tour-hold-wrap:hover { border-color: var(--bmed); }
.tour-hold-wrap:focus-within { border-color: var(--ink); background: var(--paper); }
.tour-hold-unit { font-family: var(--mono); font-size: 10.5px; color: var(--ink50); }
/* The hold. A numeral in the rail's mono register, sized to hold "30" and no
   wider — the column has to line up down the list whether a stop holds or glides. */
.tour-hold {
  flex: none;
  width: 26px;
  height: 20px;
  padding: 0 1px;
  font-family: var(--mono);
  font-size: 10.5px;
  text-align: right;
  color: var(--ink65);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  -moz-appearance: textfield;
}
.tour-hold::-webkit-outer-spin-button,
.tour-hold::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tour-hold:focus { color: var(--ink); outline: none; }
/* A slow-through has no hold, and the route's ends before they resolve. Same box,
   so nothing shifts. */
span.tour-hold.is-none {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; color: rgba(14, 14, 12, .22); cursor: default;
}
/* Placeholders keeping the column true where a row has no such control — a
   slow-through has no action slot, an end has no ×. */
.tour-row-gap { flex: none; width: 22px; }
.tour-row-gap.is-x { width: 14px; }
.tour-row-act svg { width: 13px; height: 13px; }
.tour-row-act.is-set { color: var(--ink); border-color: var(--bmed); }
.tour-row-act.is-set.is-folded { color: var(--ink65); border-color: transparent; background: var(--ink08); }
/* A menu item that is currently ON. */
.iso-row-menu button.is-on { color: var(--ink); font-weight: 500; }
.iso-row-menu button.is-on::after { content: '✓'; float: right; margin-left: 14px; }

/* The action sub-rail's foot: add another, and the hold that follows the last
   one. These were in the stop card's body; with the card gone they belong with
   the actions rather than on the row, which is a row. */
.tour-action-foot {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 2px 2px;
}
.tour-act-spacer { flex: 1; }
.tour-act-add { font-size: 11.5px; height: 24px; padding: 0 9px; }
.tour-act-cap { font-family: var(--mono); font-size: 10px; color: var(--ink45); letter-spacing: .05em; }
.tour-action-foot .wait-inline { font-size: 11.5px; color: var(--ink50); }

/* ══════════════════════════════════════════════════════════════════════════
   QUICK REHEARSE (Mark, 13 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   It is the only control in the bar that SHOWS you the thing you are building,
   and it was the quietest — borderless, in the middle of a cluster, ahead of
   controls that did not exist when its order was set.

   It now sits at the right end behind its own hairline, wears the bar's 28px
   box like everything else, and takes the honey on its play glyph. Honey is the
   accent, and this is the one action in here worth accenting — no new colour,
   just the one we have, on the one control that earns it.
   ══════════════════════════════════════════════════════════════════════════ */
.app.shell-modetop .lab-chrome-tools #tour-preview-play {
  position: relative;
  height: 28px;
  padding: 0 12px;
  margin-left: 10px;
  font-size: 12px;
  color: var(--ink);
  border: 1px solid var(--bmed);
  border-radius: 8px;
  background: transparent;
}
/* Its own hairline, at the same 10px from each neighbour as the bar's others.
   A pseudo-element rather than a border-left, because the button has a border. */
.app.shell-modetop .lab-chrome-tools #tour-preview-play::before {
  content: '';
  position: absolute;
  left: -10px;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 16px;
  background: var(--border);
}
.app.shell-modetop .lab-chrome-tools #tour-preview-play:hover {
  background: var(--ink08);
  border-color: var(--ink);
}
/* The play triangle carries the accent; the label stays ink. */
/* The play triangle carries the accent. It is its own span rather than
   ::first-letter, because the button is an inline-flex container and
   ::first-letter does not apply to those — it was silently doing nothing.
   No --honey token in this sheet: the accent is written literally everywhere
   else it appears (the favourite star, the layout guides), so it is here too. */
.app.shell-modetop .lab-chrome-tools #tour-preview-play { gap: 7px; }
.rehearse-glyph { color: #F7CE46; font-size: 10px; line-height: 1; }
/* Rehearsing: the square is ink, not accent — it is a stop, not an invitation. */
.rehearse-glyph.is-stop { color: var(--ink); font-size: 9px; }

/* ── Type on an Area plate (15 Aug 2026) ───────────────────────────────────
   Read-only: the words are here so the plate is honest about what the capture
   carries; the controls live in Style + export, where the stage IS the export.

   Two layers, because `behind` means under the capture and its shadow — the
   same split Layouts uses. The image is lifted to z-index 1 so an absolutely
   positioned canvas can sit under it; without that, an out-of-flow element
   paints over in-flow content whatever its DOM order.

   DELIBERATELY AT THE END OF THE FILE. The .iso-cap family is currently
   declared TWICE in this stylesheet (around lines 15857 and 16426 — a stale
   copy, flagged to Mark 15 Aug, not fixed here because deleting 470 lines of
   CSS in the middle of a feature is how the last hour got lost). Anything new
   put inside either block risks being shadowed by the other; here it cannot be. */
.iso-cap-frame { position: relative; }
.iso-cap-frame > img { position: relative; z-index: 1; }
.iso-cap-tx { position: absolute; inset: 0; pointer-events: none; width: 100%; height: 100%; }
.iso-cap-tx.is-behind { z-index: 0; }
.iso-cap-tx.is-front { z-index: 2; }

/* ── Boot cloak (15 Aug 2026) ─────────────────────────────────────────────
   app.html ships `stills-page` as the .is-active panel, but init() ALWAYS
   routes to Home — and it can only do that after three sequential
   chrome.storage reads (uiAuthorLoad, loadSettings, loadHomeDesignPref).
   Everything between the first paint and that call showed Page Capture in a
   shell with no .is-capture-mode and no theme class: empty rail, library
   padding, the "Preview" placeholder, white in dark mode. That composite is
   the "strange layout" flash on load. Hide the shell until init has decided
   the route and applied the theme. VISIBILITY, not display/opacity: layout
   still resolves, so the first render measures real boxes and nothing
   reflows on reveal. Removed by init() after showHome(), or by the 3s
   failsafe by DOMContentLoaded. */
.app.is-booting { visibility: hidden; }

/* ── Stage round 4→7 (15 Aug 2026) ────────────────────────────────────────
   The stage borrows the REAL topbar device control (#topbar-device-slot —
   .tb-dev icons, .tb-dev-more chevron, .tb-device-menu), relocated beside
   Capture more while Area's actions are up. One component, one look
   (Mark: "CONSISTENCY"). The id outguns the slot's hide rule on
   specificity, so no tie-break needed. */
.topbar-actions-slot .action-footer > #topbar-device-slot { display: flex; align-items: center; }
/* Finishing at the top: the actions live in the session bar, before the
   view toggle. */
.iso-gbar-acts { display: inline-flex; align-items: center; gap: 8px; margin-right: 12px; }
.iso-gbar-acts:empty { display: none; }
/* Apply to all: the SAME control Reset styling is (ghost-btn finish-reset) —
   the rail's one quiet-action shape — placed under the scope row it acts
   from. Its whole styling comes from finish-reset; only the spacing is its
   own. */
.iso-apply-all { margin: 12px 0 4px; }
/* Save to library — the product's one outline-button shape for this label
   (the Layouts topbar button is the reference). */
.iso-save-lib {
  display: inline-flex; align-items: center;
  padding: 7px 13px; border-radius: 9px;
  border: 1px solid var(--bmed); background: transparent;
  font-family: var(--sans); font-size: 13px; font-weight: 500; color: var(--ink);
  white-space: nowrap; cursor: pointer;
}
.iso-save-lib:hover { border-color: var(--ink); }
.iso-save-lib:disabled { opacity: .45; cursor: default; }

/* ── The plates become CARDS (Mark, 15 Aug, from the mock: "maybe slightly
   rounded thin border with select tick box?"). The library's own card
   language — 1px --bmed, the app-wide 10px radius, card fill — wrapped
   around the styled frame, with the caption row inside carrying the tick
   box, index, name, dims and age. On paper, not grey ("actually the grey
   isnt working"). */
.iso-cap.is-card {
  border: 1px solid var(--bmed);
  border-radius: 10px;
  /* Same colour as the work area (Mark, 15 Aug: "the cards need to be the
     same colour as the background") — the hairline alone is the card; a
     fill tint made a third surface again. Transparent, so it holds in dark
     mode too. */
  background: transparent;
  padding: 12px 12px 9px;
  transition: border-color .12s;
}
.iso-cap.is-card:hover { border-color: var(--ink32); }
.iso-cap.is-card.is-selected { border-color: var(--ink); }
/* The card carries the selection now — the frame keeps only its hover cue. */
.iso-cap.is-card.is-selected .iso-cap-frame { box-shadow: none; }
.iso-cap.is-card .iso-cap-cap { margin: 9px 1px 0; }
.iso-cap-check {
  flex: none; width: 16px; height: 16px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--bmed); border-radius: 4px;
  background: var(--paper, #fff); color: transparent; cursor: pointer;
  transition: border-color .12s, background .12s, color .12s;
}
.iso-cap-check:hover { border-color: var(--ink32); }
/* Checked: ink box, paper tick. */
.iso-cap-check[aria-checked="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper, #fff); }
.iso-cap-dims {
  flex: none; margin-left: auto;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .04em; color: var(--ink50);
}
/* Device glyph on the caption — the library card's own (lib-card-dev), sized
   to the caption's mono line. Only emitted in mixed-device sessions. */
/* .iso-cap-dev removed 17 Aug 2026 with the Review device glyph itself — a
   session is per device, so the icon had nothing true to say. The stage's
   filter host (.pc-gbar-devs below) stays: it is still in the markup and its
   :empty rule is what collapses it now that it renders nothing. */
.iso-cap.is-card .iso-cap-when { margin-left: 10px; }

/* ── One control tier in the session bar (Mark: "make the control buttons
   the same size, they are different, looks messy"). Every control in the
   bar — Select, Save to library, Export, the view toggle — sits at 32px. */
.iso-gbar-acts .lib-select-toggle,
.iso-gbar-acts .iso-save-lib,
.iso-gbar-acts .iso-caps-btn {
  height: 32px; padding: 0 16px; box-sizing: border-box;
  display: inline-flex; align-items: center;
  font-size: 12.5px; margin: 0;
  /* One radius for the bar (Mark, 15 Aug: "check the border radius of the
     cards and buttons. need to be consistent. seem too big") — the Export
     pill's 100px next to 8/9px neighbours was the "too big". 9px, the
     app's button tier; cards keep their 10. */
  border-radius: 9px;
}
.pc-gbar-views button { height: 32px; box-sizing: border-box; }
/* The device FILTER seg (renderIsoCapsDevs) is the library's lib-dev-seg
   verbatim; these lines only seat it on the bar's 32px / 9px control tier. */
.pc-gbar-devs { display: inline-flex; }
.pc-gbar-devs:empty { display: none; }
.pc-gbar-devs .lib-seg { border-radius: 9px; }
.pc-gbar-devs .lib-seg-btn.lib-dev-btn { height: 32px; box-sizing: border-box; }
/* The selected count rides beside the session title, mono like the count
   chip (the mock's "This session · 2 selected"). */
.iso-caps-selcount { font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; color: var(--ink50); margin-left: 4px; }
.iso-caps-selcount:empty { display: none; }
/* The quiet "already filed" note — mono, ink32, no chip weight. */
.iso-caps-savednote { font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink32); }
.iso-caps-savednote:empty { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   OVERHAUL PHASE 0 — THE LEFT RAIL RETIRES FROM CAPTURE (15 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   Molly's call, locked in the overhaul plan: "Latest captures" leaves the left
   rail. Recents already live in three honest places — Home, This Session on the
   stage after a capture, and the Library always — and a fourth copy down the
   side of every capture mode was the app answering a question nobody had asked
   mid-capture. It also took 200px off the width of the work, which is the one
   thing a capture mode genuinely needs.

   The rail is NOT deleted: Layouts and Sequence keep their left column, where
   the list stops being something you browse and becomes the source bin you
   pull from (#142). So this is a per-mode retirement, flagged .caps-gone by
   updateModeChrome — every capture mode except those two.

   Mechanism: column 1 is now var(--caps-w, <its old literal>) in every rule
   that sets it for a capture mode, so a single --caps-w: 0px collapses the
   track wherever it is declared — including the two !important rail-collapsed
   rules — without restating eleven different track lists. The literals stay as
   fallbacks, so nothing changes for the modes that keep the rail.

   The strip is hidden wherever it lives, not just as a workspace child: under
   mobile framing it relocates INTO #frameLeft (with #capsTools above it), and
   a rule bound to `.workspace > .caps-strip` would have missed it there and
   left the companion standing. #capsTools is deliberately NOT hidden — it is
   the framing layout's controls card, and it only ever sits inside the strip
   when framing is off, where it is empty anyway. */
.app.caps-gone { --caps-w: 0px; }

/* ── AND IT HAS TO OUTRANK THE RULES THAT SHOW IT ──────────────────────────
   Mark, first look: "still something in the left rail." Correct, and the
   screenshot said exactly what was wrong — the column HAD collapsed to zero
   (--caps-w worked) and the strip's contents were spilling over the stage,
   which is what a visible element in a 0px track looks like.

   A plain `.app.caps-gone .caps-strip { display: none }` is three classes.
   Two rules already set `display: flex` on this element and both are heavier:

     .app.shell-modetop.is-capture-mode .workspace > .caps-strip   → 5 classes
     .app.shell-modetop.is-mobile-frame #frameLeft > .caps-strip   → id + 4

   Specificity beats source order, so appending at the end of the file bought
   nothing. (`.app.mode-home .caps-strip` gets away with three classes only
   because Home is not a capture mode, so the flex rule never applies there —
   which is precisely why it was a misleading precedent to copy.)

   So each placement is answered in its own shape, one class heavier than the
   rule it is beating, and the three-class version stays last as the catch-all
   for anywhere the strip has not been relocated to. No !important: the
   selectors say what they mean, and this file has enough of those already. */
.app.caps-gone.shell-modetop.is-capture-mode .workspace > .caps-strip,
.app.caps-gone.shell-modetop.is-mobile-frame #frameLeft > .caps-strip,
.app.caps-gone .caps-strip { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   OVERHAUL PHASE 1 — VIEWPORT AND AREA BECOME ONE MODE: **CAPTURE**
   (15 Aug 2026, from the overhaul plan)
   ──────────────────────────────────────────────────────────────────────────
   Appended rather than edited in place, and deliberately: the door's base block
   is still duplicated verbatim in this file (the standing "door CSS dedupe"
   item), so an edit at the first copy is silently overruled by the second. Two
   copies to keep in step is exactly the drift the dedupe exists to end — until
   it happens, the honest place for a change is after both.

   #317 (18 Aug 2026) UNDOES THE HALF OF THIS BLOCK THAT WAS ABOUT THE MERGE.
   Mark reversed the decision: Viewport and Area are two modes again. The
   .pc-door.is-viewport rules above go from DEAD back to LOAD-BEARING —
   adoptStageFor sets the class again — which is the payoff for the phase 1
   note that called them "dead, not wrong" and left them alone.

   THE THIRD WAY GOES BACK TO BEING A MODE. .pc-ways.is-three and its two
   companions are deleted rather than left dead: unlike the .is-viewport rules
   they describe a LAYOUT that no longer occurs, and a three-column rule sitting
   in the file is an invitation to put a third card back on a door that now has
   a neighbour whose whole job that card was. The two-column base rule is what
   .pc-ways was always written for. */

/* THE GLYPH SAYS ONE THING AGAIN (#317). The merged mark was a solid frame with
   a dashed region inside it — the view AND the part — and it was right for a
   mode that did both. Two modes, two marks, which is what the door swapped
   between before phase 1 and what the mode menu shows in the picker:

     viewport  solid frame          the whole screen, at the device's size
     area      dashed frame         a region you draw or pick

   The solid override is now scoped to the viewport door instead of applying to
   every door, and the dashed inner region goes entirely — a mark that claims
   the neighbouring mode's job is worse than a plain one. Area keeps the base
   .pc-glyph, which has always been the dashed rectangle. */
.pc-door.is-viewport .pc-glyph::before { content: none; }

/* ══════════════════════════════════════════════════════════════════════════
   OVERHAUL PHASE 2 — THE SESSION STAGE BECOMES **REVIEW** (15 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   Review is a receipt: the actual pixels, big, with two ways forward (Style,
   Export) and one way back (Capture more). It has no properties, so it has no
   right rail — the layout grammar's "ABSENT is a valid state", finally taken
   at its word instead of filling the column for symmetry.

   THE SIGNAL IS .workspace.no-preview, which this file has understood since
   the Library and the Guide: renderPreviewPanel drops 'stills-component' from
   PREVIEW_PANELS and the class lands. What it did NOT do was win — capture
   mode pins three columns and forces the preview panel visible, at four and
   five classes respectively, against no-preview's two. Same lesson as phase 0,
   so the same treatment: answer each rule in its own shape, one class heavier.
   Column 1 keeps var(--caps-w) so the phase 0 retirement still applies, and
   the .mode-area variant is spelled out because it ties with the base rule and
   a tie decided by source order is a rule waiting to be moved. */
.app.shell-modetop.is-capture-mode .workspace.no-preview,
.app.shell-modetop.is-capture-mode.mode-area .workspace.no-preview,
.app.caps-collapsed.shell-modetop.is-capture-mode .workspace.no-preview,
.app.sidebar-collapsed.shell-modetop.is-capture-mode .workspace.no-preview {
  grid-template-columns: var(--caps-w, 200px) minmax(0, 1fr);
}
.app.shell-modetop.is-capture-mode .workspace.no-preview > .preview-panel { display: none; }

/* The ··· on the selection bar. The library's kebab is styled only inside
   .lib-thumb-actions, where it is a dark pill floating over a thumbnail — on
   Review it sits on paper in a row of quiet ghost buttons, so it borrows THEIR
   weight rather than the thumbnail overlay's. Same glyph, same menu component
   (.lib-menu-wrap / .lib-menu, opened by the existing [data-card-menu]
   handler); only the resting appearance is local. */
.iso-sel-kebab {
  width: 28px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; border-radius: 7px;
  color: var(--ink50); cursor: pointer;
  transition: color .14s ease, background .14s ease;
}
.iso-sel-kebab:hover { color: var(--ink); background: var(--btn2bg); }
.lib-menu-wrap.open .iso-sel-kebab { color: var(--ink); background: var(--btn2bg); }
/* The bar is the last thing above the grid, so a menu dropping out of it must
   sit over the plates rather than under them. */
.lib-select-bar .lib-menu { z-index: 12; }

/* ══════════════════════════════════════════════════════════════════════════
   OVERHAUL PHASE 3 — **STYLE** IS A MODE (15 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   The Style + export modal, promoted. Same markup, same renderer, same export
   engine — mounted into the Style panel instead of into a fixed scrimmed
   overlay. These rules do nothing but undo the modal-ness: no fixed position,
   no scrim, no blur, no 1240×780 cap. Written as overrides rather than by
   splitting .styleset-overlay into two rules, because ONE of these surfaces is
   going away and the other is not, and the diff should say which. */
.styleset-overlay.is-inline {
  position: static; inset: auto; z-index: auto;
  background: none; -webkit-backdrop-filter: none; backdrop-filter: none;
  display: block; padding: 0;
  height: 100%; min-height: 0;
}
/* THE RAIL LEFT THE SURFACE (15 Aug 2026). With .styleset-side relocated into
   #previewPanel, the body inside the panel is the canvas alone — so it takes
   the whole width instead of reserving 308px for a column that is no longer
   its child. And the rail, now sitting in the app's own properties column,
   drops the width and padding it carried as an internal panel: the column
   owns those, exactly as it does for Layouts. */
/* With the rail gone from the body, the canvas IS the body — it takes the whole
   width and the whole height, the way the Layouts board does. The head is
   hidden (its title repeats the top bar and its × is modal furniture; Done went
   to the topbar), so the work starts at the top of the panel. */
.styleset-overlay.is-inline .styleset-body { gap: 0; }
.styleset-overlay.is-inline .styleset-canvas { flex: 1 1 auto; min-height: 0; }
.styleset-overlay.is-inline .styleset-head[hidden] { display: none; }
.styleset-overlay.is-inline .styleset-stage { flex: 1 1 auto; min-height: 0; }
/* In the properties column the rail is the column's content, so it drops the
   width and padding it carried as a panel inside the modal. */
#previewPanel > .styleset-side { width: auto; flex: none; padding: 0; overflow: visible; }
/* The outputs sit under the settings that produce them, in the same column. */
#previewPanel > .styleset-foot { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }
#previewPanel > .styleset-foot .styleset-note:empty { display: none; }
/* ROUND 25 (Mark: "does style have any button in top bar? if not then export
   and save should do in top bar and be styled accordingly. black and
   outline."): the foot rides the topbar slot while Style is up — Save as the
   bar's outline pill, Export as its ink pill, the note hidden (it re-shows
   wherever the foot goes home to). */
.topbar-actions-slot > .styleset-foot { display: flex; align-items: center; gap: 12px; margin: 0; }
.topbar-actions-slot > .styleset-foot .styleset-note { display: none; }
.topbar-actions-slot > .styleset-foot [data-ss-save] {
  display: inline-flex; align-items: center; margin: 0;
  font-family: var(--sans); font-size: 13px; font-weight: 500;
  padding: 7px 13px; border-radius: 9px; white-space: nowrap;
  color: var(--ink); background: transparent; border: 1px solid var(--bmed); cursor: pointer;
}
.topbar-actions-slot > .styleset-foot [data-ss-save]:hover { border-color: var(--ink); }
.topbar-actions-slot > .styleset-foot [data-ss-export] {
  margin: 0; width: auto; white-space: nowrap;
  font-family: var(--sans); font-size: 13px; font-weight: 500;
  padding: 9px 20px; border-radius: 100px; border: 1px solid var(--ink);
  background: var(--ink); color: var(--bg); cursor: pointer;
}
.topbar-actions-slot > .styleset-foot [data-ss-export]:disabled,
.topbar-actions-slot > .styleset-foot [data-ss-save]:disabled { opacity: .45; cursor: default; }
#previewPanel > .styleset-foot button { width: 100%; justify-content: center; }
.styleset-overlay.is-inline .styleset-modal {
  width: 100%; height: 100%; min-height: 0;
  /* The card chrome goes with the modal. Inside a panel this IS the page, and a
     bordered rounded card floating on the work surface would be the app drawing
     a window inside its own window. */
  border: 0; border-radius: 0; background: none; box-shadow: none; padding: 0;
}
/* Done, not ×. A destination is left by finishing, not by dismissing — so the
   control wears a word and the weight of a quiet button rather than a glyph. */
.styleset-x.is-done {
  font-size: 12px; font-family: var(--sans); line-height: 1;
  padding: 7px 13px; border: 1px solid var(--bmed); border-radius: 8px;
  color: var(--ink);
}
.styleset-x.is-done:hover { border-color: var(--ink); }
.ss-panel-slot { height: 100%; min-height: 0; display: flex; flex-direction: column; }
/* .is-active IS LOAD-BEARING HERE, and its absence cost Mark a blank Home.
   Panels are hidden by `.panel { display: none }` — one class. An attribute
   selector counts as a class too, so `.panel[data-panel="stills-style"]` is TWO
   and quietly outranked the rule that hides every inactive panel. The Style
   panel was therefore `display: flex` on every page in the app, sitting in the
   document above whatever you had actually navigated to.
   Third time this file has taught the same lesson (see phases 0 and 2): a new
   rule has to be measured against the rules it lands beside, not just written. */
/* HEIGHT COMES FROM FLEX, NOT FROM 100% — measured, not assumed. `.main` is a
   grid item whose height is a STRETCH, not a specified length, so a percentage
   height on a child has nothing to resolve against and falls back to auto: the
   panel grew to its content (1044px inside an 838px column) and pushed the work
   off the bottom. Making .main a flex column for this one mode gives the panel
   a real box to fill. */
.app.mode-style .workspace > .main { display: flex; flex-direction: column; min-height: 0; }
/* .main-inner is the wrapper between them, and it was where the chain actually
   broke: as a BLOCK flex item its automatic minimum size is its content, so it
   refused to shrink to the column and carried the whole surface past the
   bottom edge. Found by walking the box tree in the probe rather than by
   reading the stylesheet — three wrong guesses had preceded it. */
.app.mode-style .main-inner { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.panel[data-panel="stills-style"].is-active { flex: 1 1 auto; height: auto; min-height: 0; display: flex; flex-direction: column; }
.panel[data-panel="stills-style"].is-active .status { flex: none; }
.app.mode-style .ss-panel-slot { flex: 1 1 auto; height: auto; }
.app.mode-style .styleset-overlay.is-inline { height: auto; flex: 1 1 auto; display: flex; flex-direction: column; }
.app.mode-style .styleset-overlay.is-inline .styleset-modal { height: auto; flex: 1 1 auto; }

/* ── THE SET STRIP ────────────────────────────────────────────────────────
   The set you are dressing, in the order the pager walks and the export
   numbers — NOT newest-first, which is the library's order and would put the
   export's last image at the top of the list you are working down. The lit one
   is the plate on the canvas.

   It lived in the left rail from phase 3 to 17 Aug 2026; see .styleset-qstrip
   below for where it went and why. These rules are what the cells look like,
   which did not change with the move. */
.style-thumb {
  display: flex; align-items: center; gap: 9px;
  padding: 6px; border: 1px solid transparent; border-radius: 9px;
  background: transparent; cursor: pointer; text-align: left;
}
.style-thumb:hover { border-color: var(--bmed); }
.style-thumb.is-on { border-color: var(--ink); }
.style-thumb-pic {
  width: 46px; height: 34px; flex: none; overflow: hidden;
  border-radius: 5px; border: 1px solid var(--border); background: var(--paper);
  display: flex; align-items: center; justify-content: center;
}
.style-thumb-pic img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
.style-thumb-t { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.style-thumb-n { font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em; color: var(--ink32); flex: none; }
.style-thumb-name { font-family: var(--sans); font-size: 11.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ══ THE SET STRIP DOCKS ON THE CANVAS (#287, 17 Aug 2026) ═════════════════
   Mark: "the thumbnails and plus button in a floating bar that is at bottom for
   wide aspects but then on the right hand side for narrow aspects. They should
   probably be the same?" — and, on scope: "I only meant Style should be laid
   out like [Layouts]."

   THE GEOMETRY IS .layouts-qstrip's, ON PURPOSE. That component has answered
   this exact question since 11 Aug and is the thing Mark was pointing at. Its
   numbers are repeated here rather than shared because the two strips hold
   different objects — Layouts has drag-reorder, numerals, a queue maximum and a
   count; Style has a pager, a remove and a picker — and a single ruleset
   carrying both sets of exceptions would be worse than two honest ones. What IS
   shared is the only thing they must never disagree about: plateWantsSideDock,
   one answer to "is this plate narrow", called by both.

   WHAT ONLY ONE OF THEM FLOATS. Under a wide plate this is a normal flex child
   of .styleset-canvas, sitting between the stage and the pager — `margin: 10px
   auto 0; flex: none`, exactly .layouts-qstrip.is-under. Only the SIDE dock is
   absolutely positioned, and Layouts' own note says why it has to be: an
   absolutely placed strip does not change the box the fit measures.

   Round 29 floated BOTH docks and put the bottom bar straight on top of the
   pager's ‹ ›. It carried a rule meant to push the pager clear —
   `~ .styleset-pager { bottom: 84px }` — which could never fire, because
   .styleset-pager is `position: static`. In flow, the collision cannot be
   written: the strip is above the pager in the source, so it is above it on
   screen. That is the argument for copying a working component rather than
   inventing a generic one.

   The border is not decoration. Layouts' strip is --paper on the grey layouts
   stage and reads without one; this sits on the modal's own paper, so a
   hairline is the only thing separating the bar from the surface behind it. */
.styleset-qstrip {
  display: flex; align-items: center; gap: 7px;
  padding: 9px 11px; background: var(--paper);
  /* --border, not a literal: the strip's background is --paper, which inverts
     in dark mode, so a hardcoded ink hairline would vanish against #2A2A28 —
     the exact failure the dark-theme block records for the old solid border. */
  border: 1px solid var(--border); border-radius: 10px;
  z-index: 5; max-width: calc(100% - 32px);
}
.styleset-qstrip[hidden] { display: none; }
/* The bottom margin is not symmetry, it is the floor (#288, Mark: "on wide
   aspects, the bar is a bit too close to the bottom"). .styleset-canvas has a
   10px flex gap, which sat the strip 10px above whatever followed — and for a
   set of one nothing follows, because the pager only renders above one capture.
   So the bar was hard against the bottom of the canvas in exactly the case that
   opens most often. The margin gives it a floor whether the pager is there or
   not, rather than relying on a sibling that may not exist. */
.styleset-qstrip.is-under { margin: 2px auto 14px; flex: none; }
/* min-height:0 as well as min-width:0 — a flex child defaults to min-*:auto and
   would refuse to shrink, so the rail would push the strip past the canvas
   instead of scrolling inside it. (Layouts' note, and it is true here too.) */
/* THE RAIL SHOWS ITS SCROLLBAR, unlike Layouts' — and the difference is not
   taste. Layouts caps its queue at reelMax (12) with 52×34 cells and never
   overflows; Style's picker takes up to 60, and the + that grows the set is the
   LAST thing in the rail, so on a full-ish set it is the first thing to go off
   the end. A hidden scrollbar there means the only control that adds a capture
   is off screen with nothing saying the rail moves. Thin and untinted until
   hover, like the other scrollers in this app. */
.styleset-qstrip .ss-qrail {
  display: flex; align-items: center; gap: 7px; min-width: 0;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin; scrollbar-color: transparent transparent;
}
.styleset-qstrip:hover .ss-qrail { scrollbar-color: var(--ink18) transparent; }
.styleset-qstrip .ss-qrail::-webkit-scrollbar { width: 6px; height: 6px; }
.styleset-qstrip .ss-qrail::-webkit-scrollbar-track { background: transparent; }
.styleset-qstrip .ss-qrail::-webkit-scrollbar-thumb { background: transparent; border-radius: 8px; }
.styleset-qstrip:hover .ss-qrail::-webkit-scrollbar-thumb { background: var(--ink18); }
.styleset-qstrip.is-side .ss-qrail {
  flex-direction: column; overflow-x: hidden; overflow-y: auto; min-height: 0; max-height: 100%;
}
/* NARROW PLATE → a column against the right edge. The 9:16 case, and the one
   that made this worth doing: a story plate leaves a whole column of canvas
   beside it and nothing underneath. Absolute against .styleset-canvas, which
   is why that box became a containing block below. */
.styleset-qstrip.is-side {
  position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
  flex-direction: column; max-height: calc(100% - 40px);
}
/* THE CELLS, SIZED FOR A STRIP RATHER THAN A 148px COLUMN. Round 8's "bigger
   thumbnails, no text" and round 12's 76px both described a picture filling the
   width of a rail; on a strip the binding measure is height, and a fixed box is
   what keeps a row of mixed-aspect captures from reading as a ragged edge.
   Those two rail rules are gone rather than out-specified — round 29 left them
   standing and a 4-class `.app.has-leftnav.mode-style .style-thumb-pic` quietly
   beat the new 2-class one, so every 54px tile held a 76px picture. */
.styleset-qstrip .style-thumb { flex: none; padding: 5px; }
.styleset-qstrip .style-thumb-pic { width: 64px; height: 44px; border-radius: 6px; }
/* The + keeps the strip's own height rather than a rail's 40px slab, so it
   reads as the last cell in the row instead of a button parked beside it. */
.styleset-qstrip .style-thumb-addtile {
  width: 46px; height: auto; min-height: 44px; align-self: stretch;
  margin: 0; flex: none; border-radius: 9px;
}
/* ON THE SIDE IT IS A BAR, NOT A TILE (#288, Mark: "button not good when on
   side"). `align-self: stretch` is right in a row — it makes the + the same
   height as the cells beside it — and wrong in a column, where it stretches
   across the full width while min-height keeps it 44px tall, so the + became a
   dashed square nearly the size of a thumbnail. It should read as the quiet
   end of the column: full width, because that is what stacking gives it, and
   short, because it is one control and not another picture. */
.styleset-qstrip.is-side .style-thumb-addtile {
  width: auto; align-self: stretch; min-height: 0; height: 30px;
}
/* The containing block the side dock needs. Nothing else in here was relying on
   .styleset-canvas being unpositioned: every absolutely placed descendant
   (.ss-tx, .ss-vpiece-bg, .ss-vpiece-tx) sits inside .ss-piece or .ss-vpiece,
   and both of those are position:relative in their own right. */
.styleset-canvas { position: relative; }

/* Style's empty state borrows Capture's door — same grammar, a mode with
   nothing in it is waiting rather than broken. Its glyph carries the mode's own
   mark, so the dashed-region ::before phase 1 gave the Capture glyph is off. */
.pc-glyph.is-mode::before { content: none; }
.pc-glyph.is-mode svg { width: 22px; height: 22px; color: var(--ink50); }

/* ══════════════════════════════════════════════════════════════════════════
   OVERHAUL PHASE 5 — VIDEOS CONVERGE (15 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   Recordings are raw and dressed in Style, like everything else. Three things
   need styling: the video plate in Style, the play mark on a Review card, and
   the doors the four video panels grew. */

/* THE VIDEO PLATE. The finish is CSS here and a re-encode at export — the two
   read the same seven values through styleToVideoFinish, which is what lets the
   preview claim to be the file. The wrapper carries padding/background/aspect
   (the FIELD); the <video> carries corners/frame/shadow (the PICTURE) — the
   same division the stills plate uses, so a shadow is cast by the capture
   rather than by a card around it. */
.ss-vpiece { display: block; max-width: 100%; max-height: 100%; box-sizing: border-box; position: relative; }
.ss-vpiece video { display: block; width: 100%; height: auto; max-height: 100%; object-fit: contain; position: relative; z-index: 1; }
/* The backdrop canvas paints UNDER the clip, edge to edge (round 25). */
.ss-vpiece-bg { position: absolute; inset: 0; }
/* Type on a recording (#284). Two canvases straddling the clip, at the plate's
   own box — behind sits over the backdrop and under the <video> (z-index 1),
   front over it and under the editing layer (.ss-tx, z-index 4). The z-order
   here IS the encoder's compositing order in offscreen's bakeSrc; they are the
   same stack described twice, which is the only reason "behind" can mean one
   thing in the preview and in the file. */
.ss-vpiece-tx { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
.ss-vpiece-tx.is-behind { z-index: 0; }
.ss-vpiece-tx.is-front { z-index: 2; }
/* The clip's own corner rounding must not clip the type: the canvases are the
   PLATE's box, not the clip's, and a caption in the padding lives outside the
   clip entirely. Nothing to do but say so — no overflow rule belongs here. */
.reel-txnote { margin: 10px 0 0; font-size: 11px; line-height: 1.45; color: var(--ink-40, rgba(14,14,12,0.5)); }
.reel-txnote b { font-weight: 500; }
.styleset-stage .ss-vpiece { margin: 0 auto; }

/* THE PLAY MARK on a Review card. A still frame of a clip is indistinguishable
   from a screenshot, and the glyph answers "why is my clip a picture?" before
   it is asked. CENTRED and CLICKABLE (31 Aug 2026 — Mark: "review still has
   blurry image, not the video with play icon"): the old bottom-left rule was
   written for a 15px receipt badge and carried pointer-events:none, so the
   button the markup had become could never take the click. A play control
   sits centre-frame — that is where every player on earth puts it. */
.iso-cap-frame { position: relative; }
/* z-index 3 (1 Sep 2026): the frame's img sits at z-index 1 and the text canvas at 2, so a
   play button with no z-index painted UNDERNEATH the poster — Mark saw only the blurry still. */
.iso-cap-play { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 3; display: flex; pointer-events: auto; opacity: .92; transition: opacity .14s ease, transform .14s ease; filter: drop-shadow(0 2px 8px rgba(14, 14, 12, .35)); }
.iso-cap-play:hover { opacity: 1; transform: translate(-50%, -50%) scale(1.07); }

/* Page Capture's door. The four video doors were reverted (Mark: "no video
   modes should have changed... put back") — their authoring is a list you keep
   working, not a door you pass through once. Named so the hidden state is
   explicit rather than relying on the UA default surviving a future rule. */
.page-door[hidden] { display: none; }
/* The video doors, back with Review (16 Aug 2026) — same one-liner. */
.vid-door[hidden] { display: none; }

/* Image output on the Capture door — a two-way segmented control beside the
   motion toggle, same row grammar. Quality is capture-time (baked in via
   clip.scale), which is why it lives before the shot and not in Style. */
.pc-vp-quality .seg-q { display: inline-flex; border: 1px solid var(--bmed); border-radius: 9px; overflow: hidden; }
.pc-vp-quality .seg-q button {
  font: inherit; font-family: var(--sans); font-size: 11.5px; padding: 6px 12px;
  background: transparent; border: 0; color: var(--ink65); cursor: pointer;
}
.pc-vp-quality .seg-q button.is-on { background: var(--ink); color: var(--paper); }

/* Done on the Review bar — quiet, because it acknowledges rather than acts:
   the captures were filed the moment they were taken, which is what the
   "saved to library" note beside the title says. It sits left of Style and
   Export so the row reads finish · style · export, left to right. */
/* Round 20: the quiet tier joins the one outline — "consistent buttons". */
.iso-caps-btn.is-quiet { border-color: var(--bmed); color: var(--ink65); }
.iso-caps-btn.is-quiet:hover { border-color: var(--ink); color: var(--ink); }


/* ══════════════════════════════════════════════════════════════════════════
   CAPTURE IS A VISUAL EDITOR MODE (16 Aug 2026)
   ──────────────────────────────────────────────────────────────────────────
   Mark: "it needs to look similar to page capture, device selector in
   toolbar but with the ways to capture and settings in side bar." The door's
   centred-column look retires while the editor is up: the door becomes a
   transparent host for the canvas pane, the ways + settings relocate to
   #canvas-settings (the same machinery as every other rail), and the frame
   takes no pointer events — preview and source selection only. */
.app.canvas-mode .panel.is-active #pc-door {
  max-width: none;
  padding: 0;
  text-align: initial;
}
/* The frame WAS a picture (pointer-events: none) for one round. Mark, 16 Aug:
   "the iframe doesnt scroll" — and the direction is the frame-as-source, so
   it is browsable now, the old viewport's freeBrowse model. Capture tools
   still live in the capture window only. */
/* The ways, stacked in the rail — the door's three-across grid cannot fit a
   300px column. Same cards, one per row, left-aligned interiors. */
#canvas-settings .pc-ways {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  margin: 0 0 12px;
}
#canvas-settings .pc-way {
  display: grid;
  grid-template-columns: 44px 1fr;
  grid-template-rows: auto auto;
  column-gap: 12px;
  align-items: center;
  text-align: left;
  padding: 10px 12px;
}
/* ── THE TWO GROUP HEADS MATCH (28 Aug 2026) ───────────────────────────────
   Mark: "so 'In Loey's capture window' is a header but 'On any website in the
   browser' is eyebrow in the box. should these be the same?"

   They should, and my earlier answer — that they sit at different levels, one a
   section name and one a box's own label — does not survive looking at it. The
   whole point of naming both groups was that neither should read as the
   exception. A sans heading over the cards and a mono eyebrow inside the other
   block presents them as a level apart, which is the same fault in a quieter
   form.

   So the ambient block becomes what the card group already is: a heading, then
   a bordered thing under it. The label leaves the box visually (it stays INSIDE
   .pc-vp-ambient in the DOM on purpose — that class carries the loose rail-foot
   placement, and a sibling label would be filed with the group above and left
   behind), the container drops its border, and the ROW takes it. Same numbers
   as .pc-way: 10px 12px, 10px radius, 12px gutter.

   Rail only. On the door these heads are .step-label proper — 18px mono with a
   numeral — which is the door's own grammar and not this one. */
#canvas-settings .pc-anytab { border: 0; border-radius: 0; padding: 0; }
#canvas-settings .pc-anytab-lbl {
  font-family: var(--sans); font-size: 13px; font-weight: 500;
  letter-spacing: 0; text-transform: none; color: var(--ink);
  margin: 0 0 8px;
}
#canvas-settings .pc-anytab-row {
  border: 1px solid var(--bmed); border-radius: 10px; padding: 10px 12px; gap: 12px;
}
#canvas-settings .pc-way svg { grid-row: 1 / span 2; }
#canvas-settings .pc-way b { font-size: 12.5px; }
/* ── ONE SET OF NUMBERS FOR EVERY BLOCK ON THIS DOOR (28 Aug 2026) ─────────
   Mark: "make all consistent. plus the others in area capture mode."
   Two leftovers, both here, both in BOTH modes (these cards are Select element
   and Draw area in Area, Capture at device size in Viewport):

   • row-gap. The base .pc-way is a centred flex column with `gap: 9px` between
     its three parts; the rail turns it into a grid and reset column-gap to 12
     but never row-gap, so the title sat 9px off its own description while every
     other block on the door sat at 2. It was the loose one.
   • max-width: 22ch on the description. A measure cap authored for the door's
     two-across grid, where a card is half the width of the page. In a 300px
     rail it just wraps the line earlier than its neighbours for no reason the
     eye can find. The column is the measure here. */
#canvas-settings .pc-way { row-gap: 2px; }
#canvas-settings .pc-way span { font-size: 11.5px; max-width: none; }
/* ── QUICK CAPTURE JOINS THE SHAPE (28 Aug 2026) ───────────────────────────
   Mark: "the quick capture one is different from the rest of these. make it
   the same style."

   It was, and it was the only one: a centred row of glyphs-plus-title with the
   hint on a second full-width line under it, between a .pc-way card and the On
   any tab block that both read mark-left / text-column-right, title over
   description. Two things were out — the centring, and the hint starting at the
   card's edge instead of under its own title.

   So it takes the .pc-way card's exact rail treatment above: the same
   two-column grid, the same 12px gutter, the same left-aligned interior, the
   same 10px 12px padding and 10px radius. The one difference it is entitled to
   is the width of column one — three device glyphs need more room than one
   frame — so the column is `auto` rather than 44px, and the trio keeps the size
   it was given on 27 Aug ("the icons in the rail are really tiny"). */
/* 13www: the rail's grid dress for this card is retired — the card is a pill
   + a line now (see THE TWO WAYS at the foot of this file). Only the [hidden]
   guard survives, because .pc-launch's own display would still beat it. */
#canvas-settings .pc-vp-quick-card[hidden] { display: none; }
/* A grid of one (#343, 18 Aug 2026). Viewport's way in is a single .pc-way
   card; the base .pc-ways is two across, which would leave it at half width
   beside nothing on the door. In the rail every .pc-ways is already one
   column, so this only ever changes the door. */
.pc-ways.is-one { grid-template-columns: 1fr; }
/* The status line at the rail's foot (#342, 18 Aug 2026). moveOptionsToSide
   appends it after every band, loose — see canvasNodeExcluded. It keeps the
   base .status display rules (hidden until it has something to say); this only
   gives it air below the last accordion. */
#canvas-settings > .status { margin: 16px 0 2px; }
#canvas-settings .pc-launch { width: 100%; justify-content: center; }
#canvas-settings .pc-launch.is-quiet { margin-top: 8px; }
#canvas-settings .pc-launch-sub { text-align: center; }
/* 16 Aug round 11 (Mark: "Settings in the settings drop down need to look
   like they do in all other modes."). The rows were authored for the door,
   where they sat centred under a launch button; in the rail's accordion they
   kept that centring and read as strays. Here they wear the rail's own
   opt-toggle-row grammar: label left in ink, control pinned right,
   space-between, the 9px rhythm and the hairline between rows. */
#canvas-settings .pc-vp-motion {
  width: 100%;
  justify-content: space-between;
  margin-top: 0;
  padding: 9px 0;
  border-top: 1px solid var(--ink08);
}
/* The first row follows the (hidden) step-label — no hairline over it, same
   as opt-toggle-row:first-child. (:first-of-type would match the label div.) */
#canvas-settings .step-label + .pc-vp-motion { border-top: 0; }
#canvas-settings .pc-vp-motion-t { font-size: 12.5px; color: var(--ink); }
/* Round 13 (Mark: "the options pills are not what we have in the side bars.
   make consistent.") — in the rail, Image output wears the sidebar's own pill
   grammar: separate round pills, hairline border, ink fill when selected —
   the exact register of #canvas-settings .pill-select .pill above. The door's
   joined seg box stays the door's. The Freeze switch drops to the rail's
   scale, the same 0.85 the opt-switch rows use. */
#canvas-settings .pc-vp-quality .seg-q { border: 0; border-radius: 0; overflow: visible; gap: 3px; }
#canvas-settings .pc-vp-quality .seg-q button {
  border: 1px solid var(--bmed);
  border-radius: 100px;
  padding: 5px 12px;
  font-size: 11px;
  color: var(--ink50);
  white-space: nowrap;
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
#canvas-settings .pc-vp-quality .seg-q button:hover { color: var(--ink); border-color: var(--bstr); }
#canvas-settings .pc-vp-quality .seg-q button.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
#canvas-settings .pc-vp-motion .toggle { transform: scale(0.85); transform-origin: right center; }

/* ══════════════════════════════════════════════════════════════════════════
   THE PERMANENT LEFT PANEL (16 Aug 2026, round 5 — Molly)
   ──────────────────────────────────────────────────────────────────────────
   One extra column on the APP grid, not the workspace grid — the workspace
   keeps every internal column rule it has. The topbar spans both columns so
   the source pill stays centred on the window, not on the work area. */
/* ROUND 13 (Mark: "i meant left side bar to be collapsable, not permanently
   like that. good to have collapse/expand icon.") — round 12's always-icons
   rail becomes the COLLAPSED state of a two-state nav. Expanded (default) is
   the round-5 labelled panel; .nav-icons (persisted, toggled by the chevron
   at the nav's foot) is the 64px icon rail, labels in the hover bubble. */
.app.has-leftnav {
  grid-template-rows: auto 1fr;
  grid-template-columns: 200px minmax(0, 1fr);
}
.app.has-leftnav.nav-icons { grid-template-columns: 64px minmax(0, 1fr); }
.app.has-leftnav > .topbar { grid-column: 1 / -1; }
.app.has-leftnav > .left-nav { grid-row: 2; grid-column: 1; }
.app.has-leftnav > .workspace { grid-row: 2; grid-column: 2; min-width: 0; }
/* The mode dropdown and the topbar Library button retire — the nav IS the
   mode picker now, and two Libraries would be two answers to one question. */
.app.has-leftnav #modeMenu { display: none; }
.app.has-leftnav #topnav-library { display: none; }

.left-nav {
  background: var(--bg);   /* the page's own colour: paper in light, the dark black in dark (21 Sep 2026, Mark: "the nav and pages need to be the dark black") */
  border-right: 1px solid var(--ink08);
  padding: 18px 12px 12px;
  overflow-y: auto;
  scrollbar-width: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.left-nav::-webkit-scrollbar { display: none; }
.left-nav[hidden] { display: none; }
/* ── THE TERRITORY NAMES USE THE TITLE VOICE (#295, 17 Aug 2026) ───────────
   Mark: "why are the headings mono on home page! no mono for headings"
   — and this is the second time. On 23 July the same call retired mono from
   the home page's own territory labels: ".home-grp-lbl … Territory labels use
   the TITLE voice, not caps/tracking (Mark: 'mono should not be used for
   titles')", alongside ".home .eyebrow { font-family: var(--sans) }" whose note
   reads "Headings use sans — mono is reserved for small meta (counts,
   timestamps)."

   It came back because the rule was applied to the words rather than to the
   ROLE. Stills / Video / Compose name the same three territories in four
   places, and only the home page's copy was fixed: the left nav, the mode
   menu's group eyebrow, the top bar's mode chip and the home-v3 rail all kept
   mono caps. Round 33 made that impossible to miss by putting those exact words
   in the nav — the same word, twice on one screen, in two voices.

   So the treatment follows the role now, wherever the role appears. Mono stays
   where it earns its place: counts, timestamps, nums, tags, chips — the small
   meta the 23 July note names. A word that titles a group is a title.

   Sizes are unchanged in weight of hierarchy: these are still small labels, just
   not typographically shouted. */
/* CAPS, IN THE BODY FACE (#299, 17 Aug 2026). Mark, on #295's sentence-case
   result: "dont want them back to mono caps but what about caps but body font."
   The objection was never to capitals — it was to the MONO, which in a
   navigation reads as a code comment rather than as a place. Capitals do the
   dividing work a nav wants and the sans keeps it in the app's own voice.
   Tracking comes back with them: caps set solid close up and read as a block.
   Section TITLES elsewhere stay sentence case (#297) — a divider in a list and
   a heading over content are not the same job. */
.ln-group {
  font-family: var(--sans);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink45);
  margin: 16px 10px 6px;
}
.ln-group:first-child { margin-top: 2px; }
.ln-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--ink-soft);
  font-family: var(--sans);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.ln-item svg { width: 16px; height: 16px; flex: none; }
.ln-item:hover { background: var(--ink04); color: var(--ink); }
.ln-item.is-active { background: var(--ink); color: var(--paper); }
.ln-item.is-active:hover { background: var(--ink); color: var(--paper); }
/* The toggle — pinned at the foot, the caps-collapse glyph family. */
.ln-collapse {
  margin-top: auto;
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--ink32);
  cursor: pointer;
}
.ln-collapse:hover { background: var(--ink04); color: var(--ink); }
/* ── The collapsed form: round 12's icon rail, now a STATE ─────────────── */
.app.nav-icons .left-nav { padding: 14px 9px 12px; align-items: center; gap: 4px; }
/* The heading text is still in the DOM (readers, and the expanded state) —
   clipped to a hairline here. */
.app.nav-icons .ln-group {
  align-self: stretch;
  height: 0;
  overflow: hidden;
  border-top: 1px solid var(--ink08);
  margin: 10px 6px;
  padding: 0;
  font-size: 0;
}
.app.nav-icons .ln-group:first-child { margin: 2px 6px 8px; border-top: 0; }
.app.nav-icons .ln-item {
  justify-content: center;
  gap: 0;
  width: 44px;
  height: 40px;
  padding: 0;
  border-radius: 10px;
}
.app.nav-icons .ln-item span { display: none; }
.app.nav-icons .ln-item svg { width: 18px; height: 18px; }
.app.nav-icons .ln-collapse { align-self: center; }

/* Home's own mode list retires while the nav is on — it is the exact list
   the nav now carries permanently (it is what Molly modelled the panel on),
   and two copies side by side is the drop-down problem reborn sideways.
   Home keeps its hero, the Layouts/Sequence explore cards and recents. */
.app.has-leftnav .home-cols { display: none; }

/* ── The template gallery — Layouts' door (round 5) ────────────────────── */
.layouts-gallery {
  overflow-y: auto;
  height: 100%;
  padding: 26px 34px 40px;
  box-sizing: border-box;
  /* Paper, not the canvas grey (25 Aug 2026, Mark: "lets make the layouts
     background the paper #fafafa") — the grey plates read against it. */
  background: var(--bg);   /* the page's own colour: paper in light, the dark black in dark (21 Sep 2026, Mark: "the nav and pages need to be the dark black") */
}
/* …and the frame AROUND the gallery too (Mark: "there is grey border
   around. change to paper.") — .main's canvas wash and its padding gutter
   go paper while the gallery is the surface. The board keeps the canvas.
   THE SPECIFICITY MATTERS (25 Aug 2026, Mark again: "layouts main page needs
   to remove the grey outer bit so it is all paper colour"). The line below
   was being beaten by `.app.shell-modetop.is-capture-mode .workspace > .main
   { background: var(--canvas) }` — five classes to its four — so the grey was
   still winning and the paper never reached the gutter. Matched shape, one
   class deeper. */
.app.lay-gal-open.mode-lay .main { background: var(--bg); }
.app.shell-modetop.is-capture-mode.lay-gal-open.mode-lay .workspace > .main { background: var(--bg); }
.layouts-gallery[hidden] { display: none; }
.layouts-stage[hidden] { display: none; }
/* ROUND 22 — THE MOCK'S FORMAT (25 Aug 2026, Mark: "lets make the layout
   like this… keep what we have but lets have this format. Not sure about the
   background colour yet. keep our grey for now."): wide gallery, text tabs
   on a ruled row with the search, flat card grid — grey thumb plate, caption
   below, selection ring + check on the plate. */
.lay-gal-in { max-width: 1240px; margin: 0 auto; }
.lay-gal-h {
  font-family: var(--serif, var(--sans));
  font-size: 32px;
  font-weight: 500;
  margin: 0 0 6px;
}
.lay-gal-sub { margin: 0 0 26px; font-size: 13.5px; color: var(--ink50); }
.lay-gal-bar { display: flex; align-items: flex-end; gap: 20px; margin: 0 0 6px; border-bottom: 1px solid var(--ink08); }
.lay-gal-tabs { display: flex; align-items: center; gap: 26px; flex-wrap: wrap; }
.lay-gal-tab {
  padding: 4px 0 13px; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  background: transparent; color: var(--ink65);
  font-family: var(--sans); font-size: 13px; cursor: pointer;
  transition: color .12s ease, border-color .12s ease;
}
.lay-gal-tab:hover { color: var(--ink); }
.lay-gal-tab.is-on { color: var(--ink); font-weight: 500; border-bottom-color: var(--ink); }
.lay-gal-searchwrap { position: relative; margin-left: auto; margin-bottom: 8px; }
.lay-gal-searchwrap svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--ink50); pointer-events: none; }
.lay-gal-search {
  width: 220px; height: 34px; padding: 0 12px 0 33px;
  border: 1px solid var(--bmed); border-radius: 9px;
  background: var(--paper); color: var(--ink);
  font-family: var(--sans); font-size: 12.5px;
}
.lay-gal-search:focus { outline: none; border-color: var(--ink32); }
.lay-gal-none { padding: 40px 0; font-size: 13px; color: var(--ink50); }
/* The card: a plate you look at, a caption you read. */
.lay-card { background: transparent; border: 0; padding: 0; text-align: left; cursor: pointer; min-width: 0; }
.lay-card-thumb {
  position: relative; aspect-ratio: 4 / 5; border-radius: 12px;   /* 4:5 (Mark, 25 Aug 2026) */
  background: var(--ink08); display: flex; align-items: center; justify-content: center;
  overflow: hidden; transition: transform .15s ease, box-shadow .15s ease;
}
.lay-card:hover .lay-card-thumb { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0, 0, 0, .10); }
.lay-card.sel .lay-card-thumb { box-shadow: 0 0 0 2px var(--ink); }
.lay-card.sel:hover .lay-card-thumb { box-shadow: 0 0 0 2px var(--ink), 0 8px 22px rgba(0, 0, 0, .10); }
.lay-card-thumb .layouts-lay-dg { transform: scale(1.55); }
/* The real thumbnail (layout-thumbs/<key>.webp) covers the plate when it
   exists; on 404 the img removes itself and the glyph shows. */
.lay-thumb-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
/* The hover loop (<key>.mp4) sits over the still, fading in once playable —
   any export ratio works, cover crops it to the 4:5 plate. */
.lay-thumb-vid { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 2; opacity: 0; transition: opacity .18s ease; pointer-events: none; }
.lay-thumb-vid.is-ready { opacity: 1; }
.lay-card .lay-card-check { z-index: 3; }
.lay-card-check {
  position: absolute; top: 10px; right: 10px; width: 22px; height: 22px; border-radius: 50%;
  background: var(--ink); color: var(--paper); display: flex; align-items: center; justify-content: center;
}
.lay-card-name { margin-top: 12px; font-size: 13.5px; font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lay-card-cat { margin-top: 2px; font-size: 12px; color: var(--ink-mute); }
/* ROUND 21 (Mark: "not a fan of the circle and chevron actually"): the foot
   is the name over a hairline, nothing else — the whole card is the button. */
.lay-gal-foot {
  display: flex; align-items: center; gap: 8px;
  width: 100%; margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--ink08);
}
.lay-gal-name { font-size: 12.5px; color: var(--ink); text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ROUND 21 (Mark: "there is no space between the diferent sections"): the
   gap between shelves belongs to the GROUP, so the first label doesn't
   inherit a stray top margin from the old lbl rule. */
.lay-gal-group { margin: 0 0 46px; }
.lay-gal-group:last-child { margin-bottom: 0; }
.lay-gal-lbl { margin: 0 0 12px; }
.lay-gal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 30px 24px;
  margin-top: 30px;
}

/* Gallery up = door up: the bin column belongs to the BOARD (you pull
   captures onto a board, not onto a gallery), and the properties column is
   already emptied — so both tracks collapse and the templates get the room.
   ROUND 11 (16 Aug 2026, Mark: "Layouts: Blank right side bar still" /
   "Side bars seem really messed up. Fix this.") — two faults lived in these
   three selectors:
   1. The grid rule matched the round-6 .mode-lay rule below at six classes
      and sat EARLIER in the file, so it LOST on source order — the gallery
      rendered against a dead minmax(264px,300px) third track: the blank
      right side bar, measured live (0/1100/300 where 0/1400/0 was authored).
      .has-leftnav joins the selector (the gallery only exists under the nav)
      and the rule now outranks it at seven classes, not by order.
   2. The hide rules were scoped .mode-layouts — the SHARED board chrome that
      Sequence and Style also wear. lay-gal-open went stale on the app
      (leaving the gallery for another mode never removed it — the JS half of
      this fix is syncLayoutsGallery running on EVERY showPanel), and the
      stale class display:none'd the caps strip and preview panel in Sequence
      and Style — the vanishing template rail and Style's missing right rail.
      Scoped to .mode-lay (Layouts alone), the class cannot reach another
      mode whatever its lifecycle does: true by construction, the phase-0
      lesson again. */
.app.has-leftnav.shell-modetop.is-capture-mode.mode-lay.lay-gal-open .workspace {
  /* var(--caps-w, 0px): phase 0's invariant, same note as the round-6 rule —
     Layouts is caps-keeps so the var is unset and the 0px fallback IS the
     collapse; the checker stays honest without a second exemption. */
  grid-template-columns: var(--caps-w, 0px) minmax(0, 1fr) 0px;
}
.app.lay-gal-open.mode-lay .workspace > .caps-strip { display: none; }
.app.lay-gal-open.mode-lay .workspace > .preview-panel { display: none; }

/* Style's own door wears the same grammar (round 11): with nothing loaded the
   mode shows its door in the middle, and a door has no properties and no set
   — so BOTH flanking tracks collapse rather than standing as two blank
   300px columns beside it. .style-empty is toggled by renderPreviewPanel's
   stills-style branch (the one sync point every Style render funnels
   through); it can linger harmlessly on the app because every rule here also
   requires .mode-style. */
.app.has-leftnav.shell-modetop.is-capture-mode.mode-style.style-empty .workspace {
  /* var(--caps-w, 0px): phase 0's invariant — Style is caps-keeps, var unset,
     fallback IS the collapse (the round-6 rule's own note). */
  grid-template-columns: var(--caps-w, 0px) minmax(0, 1fr) 0px;
}
.app.has-leftnav.mode-style.style-empty .workspace > .caps-strip { display: none; }
.app.has-leftnav.mode-style.style-empty .workspace > .preview-panel { display: none; }

/* ── Round 22 (Mark picked A + C from the empty-Style mocks) ───────────────
   A: the ghost set — faint dashed desktop + phone frames, the set you're
   about to make, in the dashed grammar every drawing affordance shares.
   C: the seed row — the latest library captures as tap-to-seed thumbs. */
/* The door centres in the stage (the mock's proportion — content sat at the
   panel top otherwise, all the room beneath it). Scoped to the empty state,
   so a mounted set's own layout is untouched. */
.app.mode-style.style-empty #style-panel-slot {
  display: flex; align-items: center; justify-content: center;
  min-height: calc(100vh - 200px);
}
.style-ghost {
  display: flex; align-items: flex-end; justify-content: center;
  gap: 18px; margin: 0 0 30px;
}
.style-ghost-f {
  display: block; border: 1.6px dashed var(--ink18); background: transparent;
}
.style-ghost-f.is-desk { width: 176px; height: 114px; border-radius: 8px; }
.style-ghost-f.is-deskS { width: 120px; height: 82px; border-radius: 8px; }
.style-ghost-f.is-phone { width: 58px; height: 114px; border-radius: 10px; }
.style-ghost-f > span {
  display: block; margin: 10px auto 0; width: 46%; height: 5px;
  border-radius: 3px; background: var(--ink08);
}
.style-ghost-f > span + span { width: 30%; margin-top: 5px; }
/* The gap above rides the SLOT, not the label — the label is a :first-child
   eyebrow, and the panel-wide "eyebrow:first-child sits at y0" rule outguns
   any label margin here. */
.style-seed-slot:not(:empty) { margin-top: 34px; }
.style-seed-lbl { margin: 0 0 12px; text-align: center; color: var(--ink32); }
.style-seed-row { display: flex; gap: 12px; justify-content: center; }
/* ROUND 23 (Mark: "no hover animation. mkae thumbnails bigger."): bigger
   thumbs, no motion — the hover cue is the border inking, instantly. The
   door widens for the row; the launch button keeps its old measure. */
.pc-door.is-style-door { max-width: 640px; }
.pc-door.is-style-door .pc-launch { max-width: 404px; margin-left: auto; margin-right: auto; }
.style-seed {
  width: 132px; height: 88px; padding: 0; border: 1px solid var(--ink08);
  border-radius: 10px; overflow: hidden; background: var(--paper);
  cursor: pointer;
}
.style-seed:hover { border-color: var(--ink32); }
.style-seed img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }

/* ── Round 6 (Mark: "home page now has 2 left side bars") ──────────────────
   home-v3 keeps its mode list in the workspace sidebar — which IS the list
   the permanent nav now carries. Under the nav, Home goes full-width like
   Guide and Settings: one nav, one content column. One class heavier than
   the home-v3 rules above, so it wins on specificity, not order. */
.app.has-leftnav.home-v3.mode-home .workspace { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
.app.has-leftnav.home-v3.mode-home .workspace > .sidebar-col { display: none; }
.app.has-leftnav.home-v3.mode-home .workspace > .main { grid-column: 1; }

/* ── Round 6 (Mark on the captures bin: "I think we just need to do it so we
   can see") — the Layouts board goes nav | board | properties. The bin column
   (caps strip) leaves the mode entirely; captures are added through the
   board's own "+ Add captures". Sequence kept its first column while the
   template rail was the only way a layout reached a sequence; round 10 built
   Add → Layout, Mark confirmed it works, and ROUND 12 retires the rail — so
   Sequence joins this rule and reads nav | timeline | properties too
   ("so sequence has the layouts side bar as well as home?"). */
/* .mode-lay marks the LAYOUTS panel alone, .mode-edits the Sequence editor —
   .mode-layouts is the shared board-chrome class that Style ALSO wears, and
   Style's thumbs strip still lives in that first column, so the shared class
   must stay out of these selectors. */
.app.has-leftnav.shell-modetop.is-capture-mode.mode-lay .workspace,
.app.has-leftnav.shell-modetop.is-capture-mode.mode-edits .workspace {
  /* var(--caps-w, 0px): phase 0's invariant — every capture-mode column 1
     routes through the variable. Layouts is caps-keeps so the var is unset
     here and the 0px fallback IS the value; Sequence is caps-gone now, which
     sets the var to the same 0px. The checker stays honest either way. */
  grid-template-columns: var(--caps-w, 0px) minmax(0, 1fr) minmax(264px, 300px);
}
.app.has-leftnav.mode-lay .workspace > .caps-strip,
.app.has-leftnav.mode-edits .workspace > .caps-strip { display: none; }

/* ROUND 12 (Mark: "I think the left side bar with thumbnails is too wide.")
   narrowed Style's first column to 148px — a true thumbs strip, because the
   pictures are the labels (round 8) and pictures don't need 300px of column to
   be a pager. #287 (17 Aug 2026) takes the last step: the set is on the canvas,
   so the column holds nothing and Style is caps-gone like the rest.
   var(--caps-w, 0px): phase-0's invariant. The fallback is 0px rather than
   round 12's 148px because there is no longer a state in which Style wants that
   column — a fallback naming a width nobody can reach is a rule that reads as
   live and is not. Column 3 is untouched: the styling rail is still there. */
.app.has-leftnav.shell-modetop.is-capture-mode.mode-style .workspace {
  grid-template-columns: var(--caps-w, 0px) minmax(0, 1fr) minmax(264px, 300px);
}
.app.has-leftnav.mode-style .caps-strip { padding-left: 10px; padding-right: 10px; }
/* Round 12's `.app.has-leftnav.mode-style .style-thumb-pic { height: 76px }`
   and round 13's `.caps-collapse` / `.rail-style-thumbs .hg-hd` rules retire
   here with the column they were written for. The 76px one is not merely
   obsolete but actively harmful now: at four classes it outranks the strip's
   own sizing, which is exactly how round 29 ended up painting 76px pictures
   into 54px tiles. See .styleset-qstrip. */

/* ══════════════════════════════════════════════════════════════════════════
   REVIEW, FIRST DESIGN PASS (16 Aug 2026, round 7 — for Molly to iterate)
   ──────────────────────────────────────────────────────────────────────────
   Mark: "The captures I think should have no border or styling at all. It
   doesnt look good when there are different size captures. Big one next to
   small one means there is lots of empty space."
   Two changes: the plates go BARE — no hairline, no fill, the pixels are the
   object (hover + selection still speak, on the hover ring only) — and the
   grid becomes a MASONRY: column-packed, so a tall page capture and a small
   area sit flush instead of stretching a shared row. CSS columns give the
   packing for free; cards keep natural aspect and equal widths. */
.iso-caps-grid:not(.is-one) {
  display: block;
  /* Round 12 (Mark: "Why if there are 2 or 3 for example, they are small.
     They should fill as much of the screen as possible."): the fixed 218px
     column width silently overrode the count-aware column solver in
     redressAreaCaptures — the masonry now takes its COUNT from the inline
     column-count that solver sets, and this width is only the no-JS
     fallback. */
  columns: 218px auto;
  column-gap: 18px;
}
.iso-caps-grid:not(.is-one) .iso-cap {
  break-inside: avoid;
  margin: 0 0 18px;
  width: 100%;
}
/* Tall captures (full pages) show their TOP at a bounded height instead of
   running the whole column — a gallery card is a receipt, not the reading
   copy; One view and the lightbox hold the full plate. cover + top keeps the
   crop honest (no squash) with zero dress. */
/* …AND ONLY WHEN IT IS ACTUALLY TALL (#338, 18 Aug 2026). Mark: "phone
   captures in review are not showing fully and in the right ratio."

   This applied to EVERY plate. A phone capture is 390×844 — in a 300px column
   it wants 649px and got 460: seventy-one per cent of the shot, top-cropped,
   inside a frame still drawn at the full 390×844 proportion.

   The comment above is right about full pages and says nothing about how tall
   tall is, because a stylesheet cannot know. isoCapGeom decides now and marks
   the frame .is-tall — taller than any DEVICE in the table, with headroom. A
   1440×6000 page is still cropped; a phone is a phone. */
.iso-caps-grid:not(.is-one) .iso-cap-frame.is-tall img {
  /* The top-crop bound scales with the solved column (round 12) — 460px was
     right for 218px slivers; a half-stage column earns a taller window.
     redressAreaCaptures sets --iso-gal-maxh from the solved cell width. */
  max-height: var(--iso-gal-maxh, 460px);
  object-fit: cover;
  object-position: top;
}
/* ROUND 15 (Mark: "why in gallery mode, are the tick boxes not aligned with
   the capture?"): the frame centred itself in the column (margin auto, from
   the base rule) while the caption row sat flush left — a plate narrower
   than its column drifted off its own tick. Plate and caption share the
   left edge now. One view keeps the centred stage. */
.iso-caps-grid:not(.is-one) .iso-cap-frame { margin: 0; }
.iso-cap-frame {
  background: transparent;
  /* A thin hairline earns its place back (Mark, round 9: light captures are
     hard to see against paper); hover darkens it, selection inks it. */
  box-shadow: 0 0 0 1px var(--ink08);
}
/* The round-17 CARD goes with it — no hairline box, no rounded padding ring;
   the caption row (tick · number · name · size · when) stays, under bare
   pixels. Selection speaks through the tick + the caption, and a hover ring
   on the IMAGE says clickable without dressing anything. */
.iso-cap.is-card {
  border: 0;
  border-radius: 0;
  padding: 0;
  background: transparent;
}
.iso-cap.is-card:hover { border-color: transparent; }
.iso-cap.is-card.is-selected { border-color: transparent; }
.iso-cap:hover .iso-cap-frame { box-shadow: 0 0 0 1px var(--ink32); }
.iso-cap.is-card.is-selected .iso-cap-frame { box-shadow: 0 0 0 1.5px var(--ink); }
/* Gallery captions carry the tick + number (+ device glyph in mixed
   sessions); name, dimensions and time belong to the One view (Mark,
   round 9: "Remove the dimensions, hour and the name on gallery view"). */
.iso-caps-grid:not(.is-one) .iso-cap-name,
.iso-caps-grid:not(.is-one) .iso-cap-dims,
.iso-caps-grid:not(.is-one) .iso-cap-when { display: none; }

/* ── Round 8 (Mark) ──────────────────────────────────────────────────────── */
/* (The round-8 select-bar overlay retired in round 9 — the set operations
   live in the gbar button row now; #iso-select-bar stays hidden.) */
/* Round 8 ("bigger thumbnails, no text") sized the rail's thumbs to fill a
   148px column: `display: block; padding: 4px` with a full-width 96px picture.
   Retired 17 Aug 2026 (#287) with the column. The instruction survives — the
   strip's cells are still pictures and nothing else, and still larger than
   Layouts' 52×34 queue cells — but a width:100% picture inside a row of cells
   has no width to be 100% of. The sizes live with the strip now. */
/* The × on a thumb (round 19) — hover-revealed, top right, removes the
   capture from the set without paging to it. */
.style-thumb { position: relative; }
/* ── A DRAWN CROSS, NOT THE × GLYPH (#299, 17 Aug 2026) ────────────────────
   Mark: "its odd that the x for deleting thumbnails in style, layouts,
   sequence etc are not in the middle."

   They were not, and no amount of centring was going to fix it. A text × is
   laid out by the FONT's metrics — the line box, the baseline, the em square —
   and centring puts the LINE BOX in the middle of the button. The glyph's ink
   sits wherever that face decides within it, which for Söhne's × is measurably
   above the middle. Every one of these buttons was correctly centred and
   visibly wrong, which is why it reads as odd rather than as broken.

   So the mark is drawn instead: a two-stroke path on a symmetric viewBox,
   centred by construction at any size, in any face. The buttons keep flex
   centring — an SVG has no baseline to fight. */
.style-thumb-x {
  position: absolute; top: 6px; right: 6px;
  width: 18px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(16,16,14,0.78); color: var(--paper);
  opacity: 0; transition: opacity .12s ease;
  cursor: pointer;
}
.style-thumb-x svg { display: block; }
.style-thumb:hover .style-thumb-x,
.style-thumb-x:focus-visible { opacity: 1; }
.style-thumb-x:hover { background: rgba(16,16,14,1); }
/* The + tile (round 18) — dashed, quiet, the picker's own door. It is the ONLY
   control that adds to a live set: "Choose captures" belongs to the door, and a
   mounted surface has replaced the door. Round 29 hid it for a set of one on
   the belief that the bar carried a picker too; it does not, and Style sealed
   shut. Whatever else changes about this tile, it renders whenever the set
   does. Sizing rides the strip (#287). */
.style-thumb-addtile {
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px dashed var(--ink18); border-radius: 9px;
  background: transparent; color: var(--ink50); cursor: pointer;
  transition: border-color .14s ease, color .14s ease;
}
.style-thumb-addtile:hover { border-color: var(--ink32); color: var(--ink); }
/* Style pager: arrows only, lifted clear of the bottom edge. */
.styleset-pager { justify-content: center; padding: 2px 0 14px; }

/* ── The living gallery (round 8 — Mark: "make this actually a feature…
   placeholders in them and motion on hover"). The vignettes are placeholder
   plates already; on hover they MOVE — a gentle drift for boards, a glide for
   showreels — so the card demonstrates what the template does. Stagger via
   nth-child; stilled entirely under prefers-reduced-motion. */
.layouts-gallery .layouts-lay { padding: 18px 12px 12px; }
.layouts-gallery .layouts-lay .layouts-lay-dg { transform: scale(1.35); transform-origin: center 42%; margin: 8px 0 16px; }
/* (Round-8's vignette hover motion removed in round 9 — Mark: "I meant
   actually the real motion of it on hover with actual placeholders. we will
   come to that later when the actual function of the app is working.") */


/* ==== VIDEO TIMELINE BEGIN ========================================================
   Layout only. Everything that carries a LOOK — the transport row, the play
   disc, the progress bar, the ruler, its segment bars and the playhead — is
   Sequence's own (.reel-tbar / .reel-icon-btn / .reel-tprog / .reel-ttime /
   .reel-ruler-row / .reel-ruler / .reel-segb / .reel-ph), reused rather than
   copied so the two timelines cannot drift. Mark: "It also needs to look like
   the sequence timeline. You must be consistent."
   ================================================================================ */
/* The pane is a flex column with EXPLICIT order values (canvas 1, toolbar 2), so
   an appended element defaults to order 0 and renders ABOVE the preview. */
#video-timeline { order: 3; margin-top: 10px; min-width: 0; }

/* ONE LINE. The ruler sits inside the transport row and takes all the slack, so
   the disc, the film and the total read left-to-right instead of stacking two
   bands under the preview (Mark). Sequence keeps its two rows — it has four
   controls and a scrolling rail to fit; this has a disc and a ruler. */
#video-timeline .reel-tbar { padding: 0; align-items: center; gap: 10px; }
#video-timeline .reel-ruler-row { flex: 1; min-width: 0; padding: 0; margin: 0; overflow: visible; }
#video-timeline .reel-ruler { height: 26px; }
#video-timeline .reel-ttime { flex: none; }
/* The second half of the transport. Quiet — Play here is the one you press
   twenty times an hour; this is the one you press before you record. */
#video-timeline .vtl-real {
  display: inline-block; vertical-align: baseline; border: 1px solid var(--rule-faint); background: transparent;
  border-radius: 6px; padding: 2px 7px; margin: 0 1px; font-family: var(--sans); font-size: 11px; line-height: 1.3;
  color: var(--ink-soft); cursor: pointer; white-space: nowrap;
}
#video-timeline .vtl-real:hover { border-color: var(--ink); color: var(--ink); }
#video-timeline .vtl-real[hidden] { display: none; }
/* Which run you are watching (21 Aug 2026): real pointer input, or the
   synthetic fallback. Mono small-caps like the other labels; the simulated
   state carries the warning tint because it is the one that explains a menu
   that never opened. */
#video-timeline .vtl-trust {
  flex: none; font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; padding: 4px 8px; border-radius: 6px; white-space: nowrap;
  color: var(--ink-mute); background: var(--canvas);
}
#video-timeline .vtl-trust.is-real { color: var(--ink); }
#video-timeline .vtl-trust.is-simulated { color: #8a4b00; background: rgba(247, 206, 70, .28); }
#video-timeline .vtl-trust[hidden] { display: none; }
/* The standing note under the transport (21 Aug 2026): what the preview can
   and cannot show. Quiet type, one line, always there in Page Tour and Page
   Clip — it is a fact about the surface, not a warning about this run. */
#video-timeline .vtl-standing {
  font-family: var(--sans); font-size: 11px; line-height: 1.4; color: var(--ink-mute);
  padding: 6px 2px 0; text-align: center;
}
#video-timeline .vtl-standing[hidden] { display: none; }
/* THREE KINDS, THREE LOOKS (25 Aug 2026, Mark: "should we have frames,
   holds, travelling all distinct?"). A FRAME is a card of the film — paper,
   crisp hairline. A plain HOLD (Page Scroll's start/end, a flow's wait) keeps
   the grey. TRAVEL and the scroll are movement — lightest, with the faint
   diagonal stripe of motion, and a softer edge so they read as the gaps. */
#video-timeline .reel-segb { border-radius: 6px; box-shadow: inset 0 0 0 1px var(--bmed); background: var(--ink08); }
#video-timeline .reel-segb.is-travel,
#video-timeline .reel-segb.is-scroll {
  background: var(--pill);
  background-image: repeating-linear-gradient(-60deg, transparent 0 5px, var(--ink08) 5px 7px);
  box-shadow: inset 0 0 0 1px var(--ink08);
}
#video-timeline .reel-segb.is-stop { background: var(--paper); box-shadow: inset 0 0 0 1px var(--bmed); }
/* A FLOW'S PAGES, ON THE RULER (26 Aug 2026, Mark: "The timeline needs to
   represent this and show the pages on the timeline."). The ruler is every
   page end to end now, so it needs a seam and a name. The seam is an ink
   hairline on the first block of each page after the first — cheaper and
   quieter than a block of its own — and the name is a thin label riding
   above that page's share of the width. */
#video-timeline .reel-segb.is-pagestart { box-shadow: inset 2px 0 0 0 var(--ink), inset 0 0 0 1px var(--bmed); }
/* The page's name rides its first block, in the mono meta register, ahead of
   the block's own label. No band above the bar — .reel-ruler-row clips. */
#video-timeline .reel-segb b {
  position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
  font-family: var(--mono); font-size: 8.5px; font-weight: 400; letter-spacing: .08em;
  color: var(--ink32);
}
#video-timeline .reel-segb b + span { left: 26px; }
#video-timeline .reel-segb.is-scroll span { color: var(--ink); }
/* The empty-state message sits IN the ruler's lane, vertically centred, so the
   bar has one height whatever it is showing and the frame above it never
   re-fits (which is what made the phone and tablet jump). */
.vtl-msg {
  position: absolute; inset: 0; display: flex; align-items: center;
  font-size: 11.5px; color: var(--ink-mute); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; pointer-events: none;
}

/* A STOP BLOCK IS PRESSABLE; A TRAVEL BLOCK IS NOT. Travel is the gap between
   shots — there is nothing to park on — so only stops take a cursor, a hover
   lift and the viewing ring. The ring is the SAME black ring the rail's card
   wears (.is-active-clip), because they mark the same thing. */
#video-timeline .reel-segb.is-stop { cursor: pointer; }
#video-timeline .reel-segb.is-stop:hover { box-shadow: inset 0 0 0 1px var(--ink32); }
#video-timeline .reel-segb.is-stop:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
#video-timeline .reel-segb.is-stop.is-viewing { box-shadow: inset 0 0 0 1.5px var(--ink); }
#video-timeline .reel-segb.is-travel { box-shadow: inset 0 0 0 1px var(--rule-faint); }
#video-timeline .reel-segb.is-travel span,
#video-timeline .reel-segb.is-travel i { color: var(--ink-mute); }

/* ONE PROGRESS BAR. The thin .reel-tprog is not rendered here — the ruler is
   always the whole film at full width, so it showed the same information twice
   and was the smaller of the two targets (Mark). Sequence keeps its thin bar
   because ITS ruler is a beats-wide rail that scrolls and can be clipped, so
   there the two are not duplicates. The spacer takes the room the bar used to,
   so the total stays right-aligned. */
/* The ruler IS the transport: press or drag anywhere on it. user-select is off
   because a scrub drag across the blocks otherwise highlights their labels
   blue, which looks like a bug and is one. */
#video-timeline .reel-ruler {
  cursor: pointer; touch-action: none;
  user-select: none; -webkit-user-select: none;
}
#video-timeline .reel-segb span,
#video-timeline .reel-segb i { pointer-events: none; }
/* z-index ONLY — .reel-segb is position:absolute in Sequence's own stylesheet
   and z-index applies to it as-is. An earlier pass added position:relative here
   "to make z-index work", which un-positioned every block and collapsed the
   ruler into one grey bar. Do not reintroduce it. */
#video-timeline .reel-segb { z-index: 1; }
/* Moved with a transform, so it composites instead of forcing layout on the
   ruler and every block inside it on each frame. will-change promotes it once
   rather than on the first move, which is where a visible hitch would land. */
#video-timeline .reel-ph { z-index: 2; will-change: transform; }

/* UNDER THE PREVIEW ON EVERY DEVICE. A previous pass moved it beside the frame
   on phones because it fell below the fold; Mark: "forget the side of phone,
   needs to be that the page doesnt scroll. timeline and phone preview fit on
   page." So there is no side layout — the FRAME is what gives way. See the
   VIDEO TIMELINE block in applyTourPreviewZoom, which reserves this
   element's height in _belowCanvas the same way the caption bar is reserved. */

/* Quick rehearse leaves the toolbar for this mode — the timeline's play button
   is the one place to press. The button stays in the DOM: it still owns the
   start/stop toggle the timeline delegates to. */
#tour-preview-play.vtl-hide-qr { display: none !important; }
/* The caption row under the preview, while it has no caption. Flex margins do
   not collapse, so an empty one is 20px of gap between the frame and the bar. */
#tour-preview-bar.vtl-hide-capbar { display: none !important; }
/* ==== VIDEO TIMELINE END ==== */

/* ==== PAGE TOUR ROWS BEGIN =======================================================
   The frame head, and the few row parts a pause needs that an action row did
   not. Everything else is the action row's existing dress — .tour-evrow and its
   chip grid — because a pause becoming a row is a change of model, not of look.
   Behind capt_tour_rows; delete this fence with the JS block of the same name.
   ================================================================================ */
/* PADDING 0, AND IT HAS TO OUT-SPECIFY AN ID.
   This rule has read `padding: 0` since the frame card was written and has never
   once applied: `#previewPanel.is-tour-sequence .tour-section-card` sets 12px,
   and one id beats two classes. So every frame card carried a 12px ring INSIDE
   its border that nothing wanted — the head and the rows bring their own padding
   — which is both the "left and right padding that we could use" Mark spotted
   and 24px of a collapsed card's height.
   Fixed by matching the id, so it wins on the cascade rather than !important. */
#previewPanel.is-tour-sequence .tour-section.is-frame > .tour-section-card,
.tour-section.is-frame > .tour-section-card { padding: 0; }
/* A tour with nothing in it. No seeded ends any more, so this is the real first
   state — and it has one job: name the decision (where does the film open) and
   the gesture that answers it. */
.tour-frames-empty {
  display: block; padding: 16px 14px;
  border: 1px dashed var(--bmed); border-radius: 10px;
  background: transparent;
}
.tour-frames-empty b {
  display: block; font-family: var(--sans); font-size: 12.5px;
  font-weight: 500; color: var(--ink); margin-bottom: 5px;
}
.tour-frames-empty span {
  display: block; font-family: var(--sans); font-size: 11.5px;
  line-height: 1.5; color: var(--ink-mute);
}
/* ONE LINE, ALWAYS. flex-wrap was left over from the "ends after" field, which
   deliberately took its own row and has been gone since v28 — with it still set,
   the × dropped to a line of its own as soon as the head filled up, which is
   what Mark saw ("collapsing breaks the x on to row below") and what made a
   collapsed card 72px tall instead of ~30px. Nothing in the head wants a second
   line: the name is the only long part and it truncates. */
.tour-frame-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  padding: 8px 10px 8px 12px;
  border-bottom: 1px solid var(--ink08);
}
/* FRAME n — the head names the VIEW, never an element and never a kind of
   stop, so "a click with clicks inside it" cannot be read off the rail.
   It is also the drag grip: the card's own name, so reordering needs no new
   control on a head that has no room for one. */
.tour-frame-n {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--ink-mute); flex: none;
}
.tour-frame-n[draggable="true"] { cursor: grab; }
.tour-frame-n[draggable="true"]:active { cursor: grabbing; }
#tour-sections .tour-section.is-frame.is-dragging,
#page-clips-sections .tour-section.is-frame.is-dragging,
#tour-multi-pages .tour-section.is-frame.is-dragging,
#manrec-markers .tour-section.is-frame.is-dragging { opacity: .45; }
/* THE GAP, NOT THE CARD. A ring around the card you are over cannot say whether
   you mean above it or below it, which is why reordering felt unpredictable
   (Mark). Sequence draws a line in the gap — reel.js's own note reads "the
   per-card edge shadow read wrong (Molly)" — so this is .reel-drop-ind turned on
   its side, since these cards stack where that strip runs across. */
#tour-sections,
#page-clips-sections,
#tour-multi-pages,
#manrec-markers { position: relative; }
#tour-sections .tour-drop-ind,
#page-clips-sections .tour-drop-ind,
#tour-multi-pages .tour-drop-ind,
#manrec-markers .tour-drop-ind {
  position: absolute; left: 0; right: 0; height: 3px; border-radius: 2px;
  background: var(--ink); pointer-events: none; z-index: 3;
}
#tour-sections .tour-drop-ind[hidden],
#page-clips-sections .tour-drop-ind[hidden],
#tour-multi-pages .tour-drop-ind[hidden],
#manrec-markers .tour-drop-ind[hidden] { display: none; }
.tour-frame-where {
  font-family: var(--sans); font-size: 12px; color: var(--ink-soft);
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Reframe belongs to the FRAME — every row in it comes along — so it sits in
   the head rather than on any one row. */
.tour-frame-reframe {
  flex: none; border: 1px solid var(--rule-faint); background: transparent;
  border-radius: 7px; padding: 3px 9px; font-family: var(--sans); font-size: 11px;
  color: var(--ink-soft); cursor: pointer;
}
.tour-frame-reframe:hover { border-color: var(--ink32); color: var(--ink); }
/* Waiting for a view. The button is now the confirm, so it reads as the thing
   to press — and the head says what it is waiting for. */
.tour-frame-reframe.is-reframing {
  background: var(--ink); border-color: var(--ink); color: var(--paper);
}
.tour-frame-where.is-reframing { color: var(--ink); }
/* The frame's shape in words — only shown when the rows are hidden, where it
   is the card's whole content. Open, the rows say it better. */
.tour-frame-sum { display: none; }
#tour-sections.is-frames-collapsed .tour-frame-sum,
#page-clips-sections.is-frames-collapsed .tour-frame-sum,
#tour-multi-pages.is-frames-collapsed .tour-frame-sum,
#manrec-markers.is-frames-collapsed .tour-frame-sum {
  display: block; flex: none;
  font-family: var(--sans); font-size: 11.5px; color: var(--ink-mute);
}
#tour-sections.is-frames-collapsed .tour-frame-rows,
#page-clips-sections.is-frames-collapsed .tour-frame-rows,
#tour-multi-pages.is-frames-collapsed .tour-frame-rows,
#manrec-markers.is-frames-collapsed .tour-frame-rows { display: none; }
#tour-sections.is-frames-collapsed .tour-frame-head,
#page-clips-sections.is-frames-collapsed .tour-frame-head,
#tour-multi-pages.is-frames-collapsed .tour-frame-head,
#manrec-markers.is-frames-collapsed .tour-frame-head { border-bottom: 0; padding: 6px 10px 6px 12px; }
/* Collapsed, "where" is the long part of a one-line card — shrink it first. */
#tour-sections.is-frames-collapsed .tour-frame-where,
#page-clips-sections.is-frames-collapsed .tour-frame-where,
#tour-multi-pages.is-frames-collapsed .tour-frame-where,
#manrec-markers.is-frames-collapsed .tour-frame-where { flex: 1 1 auto; min-width: 0; }
/* One toggle above the list. */
.tour-rail-tools { display: flex; justify-content: flex-end; margin: 0 0 6px; }
.tour-collapse-all {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0;
  border: 1px solid var(--rule-faint); background: transparent; border-radius: 7px;
  color: var(--ink-mute); cursor: pointer;
}
.tour-collapse-all:hover { border-color: var(--ink32); color: var(--ink); }
.tour-collapse-all.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.tour-collapse-all.is-on svg { transform: rotate(180deg); }
/* THE BEAT'S NUMBER, IN A DISC (Mark: "make the numbers on the card a bit bigger
   and easier to see. maybe have them in a black circle").
   They were 9.5px mute-grey glyphs doing two jobs badly — the beat's identity in
   the film AND the play trigger — and at that size they read as list bullets
   rather than as either. Ink disc, paper numeral: the number is now the most
   findable thing in the row, which is right, because it is what a mark on the
   page is named after. An ACTION's disc swaps the numeral for a play triangle on
   hover; a pause wears the same disc and does nothing, because there is nothing
   to perform. */
#tour-sections .tour-ev-n,
#page-clips-sections .tour-ev-n,
#tour-multi-pages .tour-ev-n,
#manrec-markers .tour-ev-n {
  position: relative; flex: none;
  width: 18px; height: 18px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ink); color: var(--paper);
  font-family: var(--mono); font-size: 10px; line-height: 1; text-align: center;
}
#tour-sections .tour-ev-n.is-fire,
#page-clips-sections .tour-ev-n.is-fire,
#tour-multi-pages .tour-ev-n.is-fire,
#manrec-markers .tour-ev-n.is-fire { padding: 0; border: 0; cursor: pointer; }
#tour-sections .tour-ev-n .ev-n-play,
#page-clips-sections .tour-ev-n .ev-n-play,
#tour-multi-pages .tour-ev-n .ev-n-play,
#manrec-markers .tour-ev-n .ev-n-play { position: absolute; inset: 0; margin: auto; opacity: 0; }
#tour-sections .tour-ev-n.is-fire:hover .ev-n-num,
#page-clips-sections .tour-ev-n.is-fire:hover .ev-n-num,
#tour-multi-pages .tour-ev-n.is-fire:hover .ev-n-num,
#manrec-markers .tour-ev-n.is-fire:hover .ev-n-num { opacity: 0; }
#tour-sections .tour-ev-n.is-fire:hover .ev-n-play,
#page-clips-sections .tour-ev-n.is-fire:hover .ev-n-play,
#tour-multi-pages .tour-ev-n.is-fire:hover .ev-n-play,
#manrec-markers .tour-ev-n.is-fire:hover .ev-n-play { opacity: 1; }
/* A pause's disc is the same shape at a lower voice — it is a number, not a
   button, and nothing about it should invite a press. */
#tour-sections .tour-ev-n:not(.is-fire),
#page-clips-sections .tour-ev-n:not(.is-fire),
#tour-multi-pages .tour-ev-n:not(.is-fire),
#manrec-markers .tour-ev-n:not(.is-fire) { background: var(--ink32); color: var(--paper); }
/* A row whose target is not in its frame, or not on the page at all. Stated,
   not shouted: the tour still records, and this is the thing to fix, not an
   error to dismiss. The note takes the row's second line so it cannot squeeze
   the name — the whole point of the width work. */
#tour-sections .tour-evrow.is-outofframe,
#tour-sections .tour-evrow.is-notfound,
#page-clips-sections .tour-evrow.is-outofframe,
#page-clips-sections .tour-evrow.is-notfound,
#tour-multi-pages .tour-evrow.is-outofframe,
#tour-multi-pages .tour-evrow.is-notfound,
#manrec-markers .tour-evrow.is-outofframe,
#manrec-markers .tour-evrow.is-notfound { position: relative; }
#tour-sections .tour-evrow.is-outofframe::after,
#tour-sections .tour-evrow.is-notfound::after,
#page-clips-sections .tour-evrow.is-outofframe::after,
#page-clips-sections .tour-evrow.is-notfound::after,
#tour-multi-pages .tour-evrow.is-outofframe::after,
#tour-multi-pages .tour-evrow.is-notfound::after,
#manrec-markers .tour-evrow.is-outofframe::after,
#manrec-markers .tour-evrow.is-notfound::after {
  content: attr(data-fit);
  grid-column: 1 / -1;
  font-family: var(--sans); font-size: 10.5px; line-height: 1.3;
  color: var(--ink-mute); padding: 0 0 3px 15px;
}
#tour-sections .tour-evrow.is-outofframe .tour-ev-tag,
#tour-sections .tour-evrow.is-notfound .tour-ev-tag,
#page-clips-sections .tour-evrow.is-outofframe .tour-ev-tag,
#page-clips-sections .tour-evrow.is-notfound .tour-ev-tag,
#tour-multi-pages .tour-evrow.is-outofframe .tour-ev-tag,
#tour-multi-pages .tour-evrow.is-notfound .tour-ev-tag,
#manrec-markers .tour-evrow.is-outofframe .tour-ev-tag,
#manrec-markers .tour-evrow.is-notfound .tour-ev-tag { border-style: dashed; }
/* A beat the last in-preview rehearse could not deliver (21 Aug 2026). Same
   voice as the two above — the tour still records; this says where the preview
   fell short of the page, and the capture window is the answer. Both rails. */
#tour-sections .tour-evrow.is-notinpreview,
#page-clips-sections .tour-evrow.is-notinpreview,
#page-clips-sections .tour-evrow.is-outofframe,
#page-clips-sections .tour-evrow.is-notfound,
#page-clips-sections .tour-evrow.is-notinpreview,
#tour-multi-pages .tour-evrow.is-notinpreview,
#manrec-markers .tour-evrow.is-notinpreview{ position: relative; }
#tour-sections .tour-evrow.is-notinpreview::after,
#page-clips-sections .tour-evrow.is-notinpreview::after,
#page-clips-sections .tour-evrow.is-outofframe::after,
#page-clips-sections .tour-evrow.is-notfound::after,
#page-clips-sections .tour-evrow.is-notinpreview::after,
#tour-multi-pages .tour-evrow.is-notinpreview::after,
#manrec-markers .tour-evrow.is-notinpreview::after{
  content: attr(data-fit);
  grid-column: 1 / -1;
  font-family: var(--sans); font-size: 10.5px; line-height: 1.3;
  color: var(--ink-mute); padding: 0 0 3px 15px;
}
#tour-sections .tour-evrow.is-notinpreview .tour-ev-tag,
#page-clips-sections .tour-evrow.is-notinpreview .tour-ev-tag,
#page-clips-sections .tour-evrow.is-notinpreview .tour-ev-tag,
#tour-multi-pages .tour-evrow.is-notinpreview .tour-ev-tag,
#manrec-markers .tour-evrow.is-notinpreview .tour-ev-tag{ border-style: dashed; }
/* Play this frame on its own — DRESSED AS THE TRANSPORT IT IS (Mark: "the play
   button on card should probably be the same style as the main play button").
   It was a bordered square in a head full of bordered squares, so the one
   control that starts the film looked like the ones that edit it. .reel-icon-btn
   is Sequence's disc — ink ground, paper glyph, no border — and this is the same
   button doing the same job on a shorter route, so it wears the same clothes at
   the head's size. */
.tour-frame-play {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0;
  border: 0; background: var(--ink); color: var(--paper); border-radius: 50%;
  cursor: pointer;
}
.tour-frame-play svg { width: 10px; height: 10px; display: block; }
.tour-frame-play:hover { opacity: .82; }
/* Playing shows the stop square; the disc itself never changes, exactly as the
   transport under the preview behaves. */
.tour-frame-play.is-playing { background: var(--ink); color: var(--paper); }
.tour-frame-rows { padding: 2px 10px 6px 12px; }
/* A pause has no target to pick, so its tag is a label, not a button. */
.tour-ev-tag.is-pause {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--ink-mute); cursor: default;
}
/* An action's chip column stays occupied even when it has nothing to say, so
   the times below it keep one exact column. */
.tour-tchip.is-blank { visibility: hidden; }
/* − / + on every row time. The app's stepper, at the row's size. */
.tour-tchip.is-step {
  display: inline-flex; align-items: stretch; gap: 0;
  border: 1px solid var(--bmed); border-radius: 7px; overflow: hidden;
  background: var(--bg2); padding: 0;
}
.tour-tchip.is-step:focus-within { border-color: var(--ink); }
.row-step {
  border: 0; background: transparent; cursor: pointer; width: 20px;
  color: var(--ink-mute); font-size: 13px; line-height: 1; padding: 0;
}
.row-step:hover { background: var(--ink08); color: var(--ink); }
.tour-tchip.is-step .wait-inp {
  width: 34px; text-align: center; border: 0; background: transparent;
  font-family: var(--mono); font-size: 11px; padding: 3px 0;
}
.tour-tchip.is-step .wait-inp::-webkit-outer-spin-button,
.tour-tchip.is-step .wait-inp::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* (.tour-frame-ends — the "ends after n s" field — is gone. The frame's tail is
   a trailing pause ROW now, so it uses the same chip as every other length and
   the header is free to say only where the frame is.) */
/* ×n is a fact about the row, not a field — no chip border, so it cannot be
   mistaken for something to type in. */
.tour-rep {
  font-family: var(--mono); font-size: 11px; color: var(--ink-soft);
  justify-self: end; align-self: center;
}
.tour-tchip.is-total {
  font-family: var(--mono); font-size: 11px; color: var(--ink-mute);
  justify-content: flex-end; border: 0; background: transparent;
}
.tour-ev-add {
  border: 0; background: transparent; padding: 0 8px 0 0; cursor: pointer;
  font-family: var(--sans); font-size: 11.5px; color: var(--ink-mute);
}
.tour-ev-add:hover { color: var(--ink); text-decoration: underline; }
/* The bubble: what you pointed at, and the three things it can be. App-sized
   and absolutely placed on the canvas, so the frame's CSS scale never reaches
   it — the alternative is drawing in the frame and counter-scaling everything,
   which is what content.js's pick pill has to do. */
#tour-row-bubble {
  position: absolute; z-index: 9; min-width: 168px;
  background: var(--paper); border: 1px solid var(--bmed); border-radius: 10px;
  box-shadow: 0 2px 4px rgba(14,14,12,.06), 0 10px 28px rgba(14,14,12,.12);
  padding: 9px 10px 8px 12px;
}
#tour-row-bubble[hidden] { display: none; }
#tour-row-bubble.is-centred { left: 50%; top: 50%; transform: translate(-50%, -50%); }
.trb-what {
  font-family: var(--sans); font-size: 11.5px; color: var(--ink-mute);
  margin: 0 18px 6px 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.trb-opts { display: flex; gap: 6px; }
.trb-opt {
  border: 1px solid var(--rule-faint); background: transparent; border-radius: 7px;
  padding: 5px 10px; font-family: var(--sans); font-size: 12px; color: var(--ink);
  cursor: pointer; white-space: nowrap;
}
.trb-opt:hover { border-color: var(--ink); background: var(--ink); color: var(--paper); }
/* THE LINK OUT LEADS AND LOOKS LIKE IT (22 Aug 2026). Mark: "make these
   different from each other or at least the transition link to be more obvious."
   Go to is the thing you came to point at on a flow page; ink, like the Go to
   tag in the rail. The two reveals and the start stay quiet. */
.trb-opt.is-lead { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.trb-opt.is-lead:hover { opacity: .85; }
.trb-opt.is-lead + .trb-opt { margin-left: 6px; }
.trb-x {
  position: absolute; top: 5px; right: 6px; width: 20px; height: 20px;
  border: 0; background: transparent; color: var(--ink-mute); cursor: pointer;
  font-size: 15px; line-height: 1; border-radius: 5px;
}
.trb-x:hover { background: var(--ink08); color: var(--ink); }
/* (THE HINT BAR IS GONE. It said "right-click anything on the page to add it"
   above every frame, forever — and the empty state already says it at the one
   moment it is needed. Mark: "this is duplicated." Removed rather than hidden;
   renderTourRowMode now only cleans up a bar left by an older render.)
   NOTE: the comment that lived here closed itself early — an edit left a
   comment terminator mid-sentence — and two lines of prose sat loose in the
   stylesheet. Harmless by luck: the parser drops junk until the next block. It
   could as easily have eaten the rule that followed. Never put a comment
   terminator inside a CSS comment, not even quoted; there is no escaping it. */
/* THE FRAME EDGE. Scrolling is what sets the frame, so scrolling is what shows
   it — an inset ring on the viewport while you move, honey while it is parked
   on a frame you already have. Honey is the marks-on-the-preview accent (Mark:
   "the things on the preview can be honey"), never chrome. */
/* An OVERLAY, not an inset shadow: the iframe fills the viewport and paints
   over anything drawn on the parent, so the ring showed only where a hairline
   of the parent peeked out at the top. */
#tour-preview-viewport.is-shotedge::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 3;
  border: 2px solid var(--ink32);
}
#tour-preview-viewport.is-shotedge.is-parked::after { border-color: #F7CE46; }
/* ── PAGE CLIP ON THE TOUR RAIL (21 Aug 2026) ──────────────────────────────
   The clip's rail is this rail. Only its own few things are dressed here: the
   kind pills above the frames, the scroll clip's three settings below them,
   and what a scroll frame hides — it has no beats, so no rows and no play. */
/* The bubble's second line, for the one case that needs a reason: a beat that
   would take the clip out of its own view. (21 Aug 2026.) */
#tour-row-bubble .trb-why {
  font-family: var(--sans); font-size: 11px; line-height: 1.4; color: var(--ink-mute);
  margin: -2px 0 6px; max-width: 220px;
}
#tour-row-bubble .trb-opt.is-quiet { color: var(--ink-mute); }
/* The hairline between the bubble's two groups — the thing's actions and the
   view's declarations (30 Aug 2026, one menu in two groups). */
#tour-row-bubble .trb-cut { height: 1px; background: var(--bmed); margin: 7px 0; }
/* The kind sits on the panel's own toggle row — same label type, same pill
   size, same 14px rhythm as Speed below it. (It was two full-width lozenges:
   a shape nothing else in the app wears — Mark.) */
#page-clips-sections .pc-kind { margin: 0 0 12px; }
#page-clips-sections .tour-frames-empty.is-next { margin-top: 8px; padding: 10px 14px; }
#page-clips-sections .tour-frames-empty.is-next b { margin: 0; }
#page-clips-sections .pc-scroll-opts .opt-toggle-row { margin-top: 6px; }
#page-clips-sections.is-scroll-clip .tour-frame-rows,
#page-clips-sections.is-scroll-clip .tour-frame-play,
#page-clips-sections.is-scroll-clip .tour-frame-sum,
#page-clips-sections.is-scroll-clip .tour-rail-tools { display: none; }
/* A clip's frame is not draggable between others — there is one, or a Start
   and an End whose order is the page's. The grip reads as a label. */
#page-clips-sections .tour-frame-n { cursor: default; }
/* ==== PAGE TOUR ROWS END ==== */

/* ==== A FINISHED CLIP'S ONE WAY ON ==== */
.clip-review-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.clip-review-acts:empty { display: none; }
/* ==== A FINISHED CLIP'S ONE WAY ON END ==== */

/* The clip's length against the cap a layout slot enforces. Muted while it
   fits — it is a fact, not a warning — and only takes weight once it doesn't. */
#page-clips-cap {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--ink-mute);
  align-self: center;
}
#page-clips-cap.is-over { color: var(--ink); font-weight: 500; letter-spacing: 0; font-family: var(--sans); }

/* ==== THE CLIP'S CROP ==== */
/* Page capture's rail language, borrowed whole: a small mono heading, pills of
   .ghost-btn.is-choice, a full-width outline button for the action, then the
   summary. Nothing here restyles those — it only spaces them. */
#page-clips-sections .pc-crop-opts {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--rule-faint);
}
#page-clips-sections .pc-crop-opts .see-render-btn { margin-top: 9px; width: 100%; }
#page-clips-sections .pc-crop-clear { margin-top: 7px; }
#page-clips-sections .pc-kind { margin-top: 0; }
#page-clips-sections .pc-scroll-opts { margin-top: 14px; }
#page-clips-sections .page-pills { margin-bottom: 2px; }
/* The summary, in Page capture's own two-line shape: what it is, then the
   pixels — the second line is the one a layout slot cares about. */
#page-clips-sections .pc-crop-line { margin-top: 12px; display: block; }
#page-clips-sections .pc-crop-what { display: block; font-size: 13px; color: var(--ink); }
#page-clips-sections .pc-crop-px {
  display: block;
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-mute);
}
#page-clips-sections .pc-crop-line.is-warn .pc-crop-what { color: var(--ink); font-weight: 500; }
/* ==== THE CLIP'S CROP END ==== */

/* Site Flow's one question, when the link needs something opened first. A
   sentence in the rail rather than a dialog over the page — it is a state of
   the page card, not an interruption. */
.flow-ask {
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px solid var(--rule-faint);
  background: var(--work-bg);
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink);
}

/* ══ SITE FLOW'S RAIL — FIVE KINDS, FIVE LOOKS ══════════════════════════════
   Mark: "all the things are the same looking buttons… The headings 'starts at'
   'opens with' etc get lost. we need clearer hierarchy and to differentiate the
   different things."

   A heading, the thing you picked, a two-way setting and an action were all one
   pill, so the eye had nothing to sort by and the headings — the only thing
   that says WHICH question you are answering — were the quietest marks on the
   card. Reading order now goes heading → value → setting → action → note, and
   each kind is a different object. */

/* 1 · THE HEADING. A rule above it does the separating, so the groups read as
   three questions rather than one long list. Ink, not mute: it is the label for
   everything under it. */
#tour-multi-pages .tf-lbl, #manrec-markers .tf-lbl {
  margin: 20px 0 9px;
  padding-top: 14px;
  border-top: 1px solid var(--rule-faint);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink);
}
#tour-multi-pages .tf-lbl:first-child, #manrec-markers .tf-lbl:first-child { margin-top: 2px; padding-top: 0; border-top: 0; }
/* The when-you-need-it aside, in sentence case so it reads as help, not label. */
#tour-multi-pages .tf-hint, #manrec-markers .tf-hint {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-mute);
  margin-left: 8px;
}
/* 2 · THE VALUE — what you picked. Not a button: you cannot press your own
   answer. A calm block of readable text, one line, truncating rather than
   wrapping a selector across three. */
#tour-multi-pages .tf-value, #manrec-markers .tf-value {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 9px 11px;
  background: var(--work-bg);
  border: 1px solid var(--rule-faint);
  border-radius: 7px;
  font-size: 14px;
  line-height: 1.35;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The path under the name — the same fact, less loudly. */
#tour-multi-pages .tf-value-sub, #manrec-markers .tf-value-sub {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-mute);
  flex: none;
}
/* 3 · THE SETTING — a two-way choice, which is what pills are for. */
#tour-multi-pages .tf-choice, #manrec-markers .tf-choice { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 8px; }
#tour-multi-pages .tf-choice:first-child, #manrec-markers .tf-choice:first-child { margin-top: 0; }
/* 4 · THE ACTIONS — things you DO. Quiet text: they are always available and
   should never compete with the answer above them. */
#tour-multi-pages .tf-acts, #manrec-markers .tf-acts { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 9px; }
#tour-multi-pages .tf-act, #manrec-markers .tf-act {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 12px;
  color: var(--ink65);
  cursor: pointer;
  text-decoration: none;
}
#tour-multi-pages .tf-act:hover, #manrec-markers .tf-act:hover { color: var(--ink); text-decoration: underline; }
/* …except the one that starts a step nobody has done yet. */
#tour-multi-pages .tf-act.is-lead, #manrec-markers .tf-act.is-lead { color: var(--ink); font-weight: 500; }
/* 5 · THE NOTE — what Loey observed, in the mono the rest of the app uses for
   measurements. Never a sentence you have to act on; those are .flow-ask. */
#tour-multi-pages .tf-note, #manrec-markers .tf-note {
  margin-top: 8px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-mute);
}
#tour-multi-pages .flow-url-row, #manrec-markers .flow-url-row { display: flex; gap: 6px; margin-top: 8px; }
#tour-multi-pages .flow-url-row .text-input, #manrec-markers .flow-url-row .text-input { flex: 1; min-width: 0; }

/* Site Flow's rows ARE the tour's rows — .tour-evrow does the work. Only two
   local notes: the value never wraps a long selector across three lines, and
   Test sits with the × rather than competing with the row's own name. */
#tour-multi-pages .flow-rows .tour-ev-what, #manrec-markers .flow-rows .tour-ev-what { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#tour-multi-pages .flow-rows .tour-ev-tools, #manrec-markers .flow-rows .tour-ev-tools { display: flex; align-items: center; gap: 10px; }
#tour-multi-pages .flow-rows .tf-act, #manrec-markers .flow-rows .tf-act { font-size: 11.5px; }

/* ==== SITE FLOW — THE PAGE IS THE FRAME (22 Aug 2026) BEGIN =====================
   Site Flow's rail is the tour's rail: every rule above that names #tour-sections
   now names #tour-multi-pages too (same aliasing v53 gave #page-clips-sections).
   What is left here is the flow card's own few parts — the page name in the
   head, GO TO, the line where a page is left — and the overrides that beat the
   old Site Flow card rules still scoped to #tour-multi-pages by id.
   ================================================================================ */
/* The card: the tour frame's dress, out-specifying the old nested-card rules
   (#tour-multi-pages .tour-section-card at 14px/18px, and the is-tour-sequence
   one that painted it --bg). */
#tour-multi-pages .tour-section.is-frame > .tour-section-card,
#previewPanel.is-tour-sequence #tour-multi-pages .tour-section.is-frame > .tour-section-card,
#manrec-markers .tour-section.is-frame > .tour-section-card,
#previewPanel.is-tour-sequence #manrec-markers .tour-section.is-frame > .tour-section-card {
  padding: 0; background: var(--card-fill);
  border: 1px solid var(--bmed); border-radius: 10px; box-shadow: none;
}
#previewPanel.is-tour-sequence #tour-multi-pages .tour-section.is-frame.is-active-clip > .tour-section-card, #previewPanel.is-tour-sequence #manrec-markers .tour-section.is-frame.is-active-clip > .tour-section-card {
  border-color: var(--ink); box-shadow: 0 0 0 .5px var(--ink);
}
#tour-multi-pages .tour-section.is-frame, #manrec-markers .tour-section.is-frame { cursor: pointer; }
#tour-multi-pages .tour-frame-head .flow-page-name, #manrec-markers .tour-frame-head .flow-page-name { font-weight: 500; color: var(--ink); }
#tour-multi-pages .tour-frame-head .flow-where-sep, #manrec-markers .tour-frame-head .flow-where-sep { color: var(--ink-mute); margin: 0 5px; }
/* Page 1 has no ×; hold its width so every head's play and Reframe line up. */
#tour-multi-pages .tour-frame-head .flow-head-sp, #manrec-markers .tour-frame-head .flow-head-sp { width: 22px; flex: none; }
#tour-multi-pages .tour-frame-head .tour-section-remove, #manrec-markers .tour-frame-head .tour-section-remove { margin-left: 0; }
/* GO TO — the one word Site Flow adds. Same tag as Click / Hover; ink, because
   it is a click, and a label, because there is nothing to flip it to. */
#tour-multi-pages .tour-ev-tag.is-goto, #manrec-markers .tour-ev-tag.is-goto { color: var(--ink); border-color: var(--ink32); cursor: default; }
#tour-multi-pages .tour-evrow .tf-value-sub, #manrec-markers .tour-evrow .tf-value-sub { font-family: var(--mono); font-size: 10px; color: var(--ink-mute); margin-left: 4px; }
/* The "leaving the page" hairline above the reveal/Go to was --ink32 and read
   as a different, darker rule than the tour's row separators (Mark, 23 Aug
   2026: "why does site flow have a dark hairline under pause and page tour
   does not?"). One rail, one hairline — the rows separate exactly as the
   tour's do, via the aliased #tour-sections rules. */
/* A row that only speaks keeps the number column so its text lines up. */
#tour-multi-pages .tour-ev-n.is-ghost, #manrec-markers .tour-ev-n.is-ghost { visibility: hidden; }
#tour-multi-pages .tour-ev-quiet .tf-act, #manrec-markers .tour-ev-quiet .tf-act { margin-left: 4px; }
#tour-multi-pages .flow-rows .flow-url-row, #manrec-markers .flow-rows .flow-url-row { margin: 4px 0 6px; }
#tour-multi-pages .tour-section-card > .tf-note,
#tour-multi-pages .tour-section-card > .flow-ask,
#manrec-markers .tour-section-card > .tf-note,
#manrec-markers .tour-section-card > .flow-ask { margin: 0 12px 8px; }
/* The rail itself: the tour's stack, its gap and its connector. The aliasing
   above skipped the rules that already named #tour-multi-pages (for the old
   .tour-group-sections), so the card-level resets are restated here. */
#tour-multi-pages, #manrec-markers { display: flex; flex-direction: column; gap: 10px; position: relative; }
#tour-multi-pages > .tour-section.is-frame, #manrec-markers > .tour-section.is-frame { display: block; position: relative; border: 0; padding: 0; margin: 0; }
#tour-multi-pages > .tour-section.is-frame + .tour-section.is-frame::before, #manrec-markers > .tour-section.is-frame + .tour-section.is-frame::before {
  content: ''; position: absolute; left: 50%; transform: translateX(-50%);
  top: -10px; height: 10px; width: 1px; background: var(--bmed);
}
/* The reveal and the link out are beats — ink discs, like a tour's actions.
   Not buttons: the preview plays a flow page whole (the page's ▶). */
#tour-multi-pages .tour-ev-n.is-beat, #manrec-markers .tour-ev-n.is-beat { background: var(--ink); color: var(--paper); }
/* A quiet row is a sentence — give it the whole width, not the name column. */
#tour-multi-pages .tour-evrow.is-quietrow, #manrec-markers .tour-evrow.is-quietrow { display: block; }
#tour-multi-pages .tour-evrow.is-quietrow .tour-ev-main, #manrec-markers .tour-evrow.is-quietrow .tour-ev-main { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
#tour-multi-pages .flow-url-row[hidden], #manrec-markers .flow-url-row[hidden] { display: none !important; }
/* An untouched page: the tour's empty box inside the card, without its own
   border — the card is the border. */
#tour-multi-pages .flow-rows .tour-frames-empty, #manrec-markers .flow-rows .tour-frames-empty { border: 0; padding: 10px 2px 6px; }
#tour-multi-pages .flow-rows .tour-frames-empty .tf-acts, #manrec-markers .flow-rows .tour-frames-empty .tf-acts { margin-top: 6px; }
/* ==== SITE FLOW — THE PAGE IS THE FRAME END ==== */

/* ==== OVER THE PREVIEW — ONE FAMILY (22 Aug 2026) BEGIN ===========================
   Everything that floats over the preview is one object in one of two dresses,
   chosen by the PAGE, never by the user: an ink pill over a light page, a paper
   card over a dark one (Mark: "can it change depending on the darkness of the
   site?"). The tone is read by readPreviewTone and stamped on the canvas as
   data-page-tone (the dock) and on the bubble as data-tone (its own element).
   One voice: sans 12px — mono is for labels, not sentences. Frosted glass is
   gone: at 72% paper over a white page there was nothing to see (Molly).
   These rules out-specify the frosted .app.shell-modetop rules above by id and
   order; the old rules are left in place so the revert is one fence.
   ================================================================================ */
.app {
  --ov-bg: #1A1A18; --ov-fg: #FBFAF8; --ov-mute: rgba(251, 250, 248, .62);
  --ov-line: rgba(251, 250, 248, .32); --ov-line-strong: rgba(251, 250, 248, .6);
  --ov-lead-bg: #FBFAF8; --ov-lead-fg: #1A1A18; --ov-hover: rgba(251, 250, 248, .12);
  --ov-shadow: 0 2px 4px rgba(14, 14, 12, .12), 0 12px 30px rgba(14, 14, 12, .28);
}
.app:has(#tour-preview-canvas[data-page-tone="dark"]),
#tour-row-bubble[data-tone="dark"] {
  --ov-bg: #FBFAF8; --ov-fg: #0E0E0C; --ov-mute: rgba(14, 14, 12, .6);
  --ov-line: rgba(14, 14, 12, .22); --ov-line-strong: rgba(14, 14, 12, .5);
  --ov-lead-bg: #0E0E0C; --ov-lead-fg: #FBFAF8; --ov-hover: rgba(14, 14, 12, .06);
  --ov-shadow: 0 2px 4px rgba(0, 0, 0, .25), 0 12px 30px rgba(0, 0, 0, .45);
}
/* The bubble reads its own element; when it has not (no rect), it follows the dock. */
#tour-row-bubble[data-tone="light"] {
  --ov-bg: #1A1A18; --ov-fg: #FBFAF8; --ov-mute: rgba(251, 250, 248, .62);
  --ov-line: rgba(251, 250, 248, .32); --ov-line-strong: rgba(251, 250, 248, .6);
  --ov-lead-bg: #FBFAF8; --ov-lead-fg: #1A1A18; --ov-hover: rgba(251, 250, 248, .12);
  --ov-shadow: 0 2px 4px rgba(14, 14, 12, .12), 0 12px 30px rgba(14, 14, 12, .28);
}
/* ── the docked sentence: pick banner, off-source offer, status toast, hint ── */
.app.shell-modetop .tour-preview-banner,
.app.dark.shell-modetop .tour-preview-banner,
.tour-preview-banner,
.app.shell-modetop #canvas-status-toast,
.app.dark.shell-modetop #canvas-status-toast,
#canvas-status-toast,
.app.shell-modetop.canvas-mode #tour-preview-hint,
.app.dark.shell-modetop.canvas-mode #tour-preview-hint,
#stage-side-msgs.is-over-canvas > *,
.app.dark #stage-side-msgs.is-over-canvas > * {
  background: var(--ov-bg); color: var(--ov-fg);
  border: 1px solid var(--ov-line); border-radius: 10px;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  box-shadow: var(--ov-shadow);
  font-family: var(--sans); font-size: 12px; line-height: 1.4; font-weight: 400;
}
.app.shell-modetop #canvas-status-toast.is-error { background: #7a2e22; border-color: rgba(255, 255, 255, .2); color: #FBEAE6; }
.tour-preview-banner { padding: 7px 8px 7px 14px; gap: 10px; }
.tour-preview-banner.is-offsource { padding: 12px 14px; }
.tour-preview-banner-text,
.app.shell-modetop .tour-preview-banner-text { font-family: var(--sans); font-size: 12px; letter-spacing: 0; line-height: 1.4; color: var(--ov-fg); }
.tour-preview-banner-btn,
.app.shell-modetop .tour-preview-banner-btn,
.app.dark.shell-modetop .tour-preview-banner-btn {
  font-family: var(--sans); font-size: 12px; border: 1px solid var(--ov-line-strong);
  background: transparent; color: var(--ov-fg); border-radius: 7px; padding: 4px 11px;
}
.tour-preview-banner-btn:hover,
.app.shell-modetop .tour-preview-banner-btn:hover { background: var(--ov-hover); color: var(--ov-fg); border-color: var(--ov-fg); }
.tour-preview-banner-btn.is-primary,
.app.shell-modetop .tour-preview-banner-btn.is-primary,
.app.dark.shell-modetop .tour-preview-banner-btn.is-primary {
  background: var(--ov-lead-bg); color: var(--ov-lead-fg); border-color: var(--ov-lead-bg); font-weight: 500;
}
.tour-preview-banner-btn.is-primary:hover,
.app.shell-modetop .tour-preview-banner-btn.is-primary:hover { opacity: .86; background: var(--ov-lead-bg); color: var(--ov-lead-fg); border-color: var(--ov-lead-bg); }
#stage-side-msgs.is-over-canvas .lab-status:not([data-blank]):not(:empty),
.app.dark #stage-side-msgs.is-over-canvas .lab-status:not([data-blank]):not(:empty),
#stage-side-msgs.is-over-canvas #tour-preview-hint,
.app.dark #stage-side-msgs.is-over-canvas #tour-preview-hint { color: var(--ov-fg); font-size: 12px; }
/* ── the bubble: anchored to its element, same dress ── */
#tour-row-bubble {
  background: var(--ov-bg); color: var(--ov-fg); border: 1px solid var(--ov-line);
  border-radius: 10px; box-shadow: var(--ov-shadow);
}
#tour-row-bubble .trb-what { color: var(--ov-mute); font-family: var(--sans); }
#tour-row-bubble .trb-why { color: var(--ov-mute); }
#tour-row-bubble .trb-cut { background: var(--ov-line); }
#tour-row-bubble .trb-opt { border-color: var(--ov-line-strong); color: var(--ov-fg); background: transparent; }
#tour-row-bubble .trb-opt:hover { background: var(--ov-hover); color: var(--ov-fg); border-color: var(--ov-fg); }
#tour-row-bubble .trb-opt.is-lead { background: var(--ov-lead-bg); color: var(--ov-lead-fg); border-color: var(--ov-lead-bg); }
#tour-row-bubble .trb-opt.is-lead:hover { opacity: .86; }
#tour-row-bubble .trb-opt.is-quiet { color: var(--ov-mute); }
#tour-row-bubble .trb-x { color: var(--ov-mute); }
#tour-row-bubble .trb-x:hover { background: var(--ov-hover); color: var(--ov-fg); }
/* ==== OVER THE PREVIEW — ONE FAMILY END ==== */

/* ==== PAGE SCROLL RANGE (22 Aug 2026) ==== */
/* (v100: the Range readouts became the SCROLL card — see #pagescroll-rail.) */
#pagescroll-range .ghost-btn.is-readout { cursor: default; }
#pagescroll-range .ghost-btn.is-readout .ps-where { font-style: normal; font-family: var(--mono); font-size: 10px; opacity: .75; margin-left: 2px; }
#pagescroll-range .tour-section-remove { margin-left: 0; }
#pagescroll-range-note { margin-top: 8px; }
/* Page Scroll's card (v100): the aliased rail rules dress it via .tour-sections;
   the read-only length chip is dashed, like an unfilled value. */
#pagescroll-rail .tour-tchip.is-quiet-chip { border-style: dashed; cursor: default; }
#pagescroll-rail .tour-tchip.is-quiet-chip em { font-style: normal; font-family: var(--mono); font-size: 10.5px; color: var(--ink50); }
#pagescroll-rail .tour-section { --tour-chipw: 66px; --tour-tailw: 20px; display: block; border: 0; padding: 0; }
#pagescroll-rail .tour-section.is-frame > .tour-section-card { padding: 0; background: var(--card-fill); border: 1px solid var(--bmed); border-radius: 10px; }
#pagescroll-rail .tour-evrow { display: grid; grid-template-columns: minmax(0, 1fr) var(--tour-chipw) var(--tour-tailw); align-items: center; column-gap: 5px; min-height: 32px; padding: 2px 0; }
#pagescroll-rail .tour-evrow + .tour-evrow { border-top: 1px solid rgba(14,14,12,.06); }
#pagescroll-rail .tour-evrow.is-quietrow { display: block; }
#pagescroll-rail .tour-ev-main { display: flex; align-items: center; min-width: 0; gap: 6px; }
#pagescroll-rail .tour-ev-n { position: relative; flex: none; width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--ink32); color: var(--paper); font-family: var(--mono); font-size: 10px; }
#pagescroll-rail .tour-ev-n.is-beat { background: var(--ink); }
#pagescroll-rail .tour-ev-n.is-ghost { visibility: hidden; }
#pagescroll-rail .tour-ev-tag { flex: none; display: inline-flex; align-items: center; gap: 5px; height: 21px; padding: 0 8px; font-family: var(--sans); font-size: 10.5px; color: var(--ink65); background: transparent; border: 1px solid var(--bmed); border-radius: 100px; }
#pagescroll-rail .tour-ev-quiet { font-size: 11px; color: var(--ink50); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#pagescroll-rail .tour-tchip { display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px; height: 22px; padding: 0 6px 0 7px; border: 1px solid var(--bmed); border-radius: 7px; }
#pagescroll-rail .tour-tchip input { width: 26px; height: 18px; padding: 0; border: 0; background: transparent; font-family: var(--mono); font-size: 10.5px; text-align: right; color: var(--ink65); -moz-appearance: textfield; }
#pagescroll-rail .tour-tchip input::-webkit-outer-spin-button, #pagescroll-rail .tour-tchip input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
#pagescroll-rail .row-step { width: 17px; }
#pagescroll-rail .tour-frame-rows { padding: 2px 10px 6px 12px; }
#pagescroll-rail [data-ps-row] { cursor: pointer; }

/* How this works — behind the title row's ? (25 Aug 2026; the fold's own
   summary row is gone: one head row, cards start immediately). The panel
   drops in below the title while the ? is on. */
.rail-how2 { margin: -2px 0 14px; padding: 11px 13px 13px; border-radius: 10px; background: var(--ink08); }
.rail-how2[hidden] { display: none; }
.rail-how2 .tour-frames-empty.is-how { border: 0; padding: 0; background: transparent; }
/* The rail's icon cluster docked on the title row (tourRailDockTools). */
#previewPanel .rail-collapse-head { align-items: center; margin: 0 0 14px; }
#previewPanel .rail-collapse-head .rail-mode-title { margin-bottom: 0; }
#previewPanel .rail-collapse-head .rail-head-tools { flex: none; }
.rail-head-tools .tour-rail-tools { margin: 0; }
/* The ? never rotates — that flip belongs to the collapse chevrons only. */
.tour-collapse-all.rail-how-btn.is-on svg { transform: none; }
#tour-sections .tour-frames-empty.is-lite,
#tour-multi-pages .tour-frames-empty.is-lite,
#manrec-markers .tour-frames-empty.is-lite { padding: 12px 14px; }
#tour-sections .tour-frames-empty.is-lite b,
#tour-multi-pages .tour-frames-empty.is-lite b,
#manrec-markers .tour-frames-empty.is-lite b { margin-bottom: 0; font-weight: 450; }

/* The flow's empty state, in steps (23 Aug 2026). The number wears the rail's
   disc so the steps read as beats-to-be. */
#tour-multi-pages .tour-frames-empty.is-steps b em,
#pagescroll-rail .tour-frames-empty.is-steps b em,
#tour-sections .tour-frames-empty.is-steps b em,
#manrec-markers .tour-frames-empty.is-steps b em { font-style: normal; font-weight: 400; color: var(--ink-mute); font-size: 11px; }
#tour-multi-pages .tour-frames-empty.is-steps .fe-step,
#pagescroll-rail .tour-frames-empty.is-steps .fe-step,
#tour-sections .tour-frames-empty.is-steps .fe-step,
#manrec-markers .tour-frames-empty.is-steps .fe-step { display: flex; align-items: flex-start; gap: 8px; margin-top: 9px; }
#tour-multi-pages .tour-frames-empty.is-steps .fe-n,
#pagescroll-rail .tour-frames-empty.is-steps .fe-n,
#tour-sections .tour-frames-empty.is-steps .fe-n,
#manrec-markers .tour-frames-empty.is-steps .fe-n {
  flex: none; width: 18px; height: 18px; border-radius: 50%; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ink); color: var(--paper); font-family: var(--mono); font-size: 10px;
}
#tour-multi-pages .tour-frames-empty.is-steps .fe-t,
#pagescroll-rail .tour-frames-empty.is-steps .fe-t,
#tour-sections .tour-frames-empty.is-steps .fe-t,
#manrec-markers .tour-frames-empty.is-steps .fe-t { font-size: 11.5px; line-height: 1.45; color: var(--ink-soft); }
#tour-multi-pages .tour-frames-empty.is-steps .fe-t b,
#pagescroll-rail .tour-frames-empty.is-steps .fe-t b,
#tour-sections .tour-frames-empty.is-steps .fe-t b,
#manrec-markers .tour-frames-empty.is-steps .fe-t b { display: inline; font-size: 11.5px; margin: 0; color: var(--ink); }

/* ==== PAGE SCROLL RANGE END ==== */

/* The rail's Clear × (23 Aug 2026) — the collapse toggle's dress, a quiet
   danger: it confirms before doing anything. */
.tour-rail-tools { gap: 6px; }
.tour-clear-x:hover { border-color: #b3564a; color: #b3564a; background: transparent; }
/* One slot, continued: a status toast still on screen yields the moment a
   banner arrives. */
.app:has(#tour-preview-canvas .tour-preview-banner:not([hidden])) #canvas-status-toast { opacity: 0 !important; }


/* ==== MANUAL RECORDING KEYS (25 Aug 2026) ====
   The keys rail — rows in the shared grammar; #manrec-markers inherits the
   frame-card dress via the selector twins added from #tour-multi-pages.
   Only the pieces unique to this mode live here. */
/* The position readout at the row's tail — mono, quiet, never wraps. */
#manrec-markers [data-manrec-row] { cursor: pointer; }
#manrec-markers .mr-where { flex: none; font-family: var(--mono); font-size: 10px; color: var(--ink-mute); margin-right: 6px; white-space: nowrap; }
/* An unnamed key invites a name rather than pretending to have one. */
#manrec-markers .tour-ev-what.is-unnamed { color: var(--ink-mute); font-style: italic; }
/* The in-place rename input sits where the name sat. */
#manrec-markers .mr-name-input { flex: 1; min-width: 0; height: 22px; padding: 0 6px; border: 1px solid var(--bmed); border-radius: 6px; background: var(--paper); font-family: var(--sans); font-size: 11.5px; color: var(--ink); }
/* The add-page end card — the dashed grammar, with the URL row inside it.
   The footer is a flex row by an older, more specific rule
   (#previewPanel .tour-list-footer), which shrank the card to its content —
   the card claims the full lane whatever the footer's display is (25 Aug
   2026, Mark: "why is the 'move to another page' bit not full width?"). */
#manrec-list-footer { display: block; }
#manrec-list-footer .mr-addpage { flex: 1 1 100%; width: 100%; box-sizing: border-box; }
/* You browsed somewhere new — the sentence and its own real button. */
#manrec-list-footer .mr-offpage { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
#manrec-list-footer .mr-offpage-what { flex: 1; min-width: 0; font-size: 11.5px; color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#manrec-list-footer .mr-offpage .mr-offpage-what b { display: inline; margin: 0; font-weight: 500; color: var(--ink); font-size: 11.5px; }
#manrec-list-footer .mr-offpage .ghost-btn { flex: none; }
#manrec-list-footer .mr-addpage b { margin-bottom: 6px; }
#manrec-list-footer .mr-url-row { display: flex; gap: 6px; margin-top: 8px; }
#manrec-list-footer .mr-url-row .text-input { flex: 1; min-width: 0; }
/* The dashed empty/lite dress is host-scoped in the shared rules; the footer
   sits OUTSIDE #manrec-markers, so the end card carries its own copy. */
#manrec-list-footer .tour-frames-empty { border: 1.5px dashed var(--bmed); border-radius: 12px; padding: 12px 14px; font-size: 12px; }
#manrec-list-footer .tour-frames-empty b { display: block; font-weight: 450; color: var(--ink); font-size: 12px; }
#manrec-list-footer .tf-acts { margin-top: 4px; }
/* ==== MANUAL RECORDING KEYS END ==== */


/* ==== LAYOUT DETAIL POPUP (25 Aug 2026) ====
   Click a card → the detail; hover keeps the quick Use on the plate. */
.lay-card .ld-use-quick {
  position: absolute; right: 10px; bottom: 10px; z-index: 3;
  padding: 6px 14px; border-radius: 9px;
  background: var(--ink); color: var(--paper);
  font-family: var(--sans); font-size: 12px; font-weight: 500;
  opacity: 0; transform: translateY(4px); pointer-events: none;
  transition: opacity .14s ease, transform .14s ease;
}
.lay-card:hover .ld-use-quick, .lay-card:focus-visible .ld-use-quick { opacity: 1; transform: none; pointer-events: auto; }
/* (A "Saved" shelf of your own boards lived in the gallery for one round —
   25 Aug 2026. The Layouts screen is Loey's templates only now; saved boards
   are re-opened from the Library.) */
#layout-detail-overlay {
  position: fixed; inset: 0; z-index: 12000;
  background: rgba(14, 14, 12, .38);
  display: flex; align-items: center; justify-content: center; padding: 30px;
}
#layout-detail-overlay .ld-pop {
  position: relative; display: flex; gap: 28px; width: min(780px, 94vw);
  max-height: min(640px, 92vh); padding: 22px;
  background: var(--paper); border: 1px solid var(--ink16, rgba(0, 0, 0, .14)); border-radius: 16px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, .18);
}
.ld-x {
  position: absolute; top: 12px; right: 14px; width: 26px; height: 26px; border: 0; border-radius: 8px;
  background: transparent; color: var(--ink50); font-size: 16px; cursor: pointer; z-index: 2;
}
.ld-x:hover { background: var(--ink08); color: var(--ink); }
.ld-preview {
  position: relative; flex: none; width: 300px; max-width: 42%; aspect-ratio: 4 / 5; border-radius: 12px;
  background: var(--ink08); display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.ld-preview .layouts-lay-dg { transform: scale(1.55); }
.ld-media { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .18s ease; }
.ld-media.is-ready { opacity: 1; }
.ld-playing {
  position: absolute; left: 10px; bottom: 10px; z-index: 2;
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink65); background: var(--paper); border-radius: 100px; padding: 4px 10px;
}
.ld-info { flex: 1; min-width: 0; padding: 6px 6px 2px 0; display: flex; flex-direction: column; overflow: auto; }
/* (The mono eyebrow above the name is gone — 25 Aug 2026, Mark: "dont need
   the tiny subtitle above the main title". The card the popup opened from
   already said the category.) */
.ld-name { margin: 0 0 8px; font-size: 21px; font-weight: 500; color: var(--ink); }
.ld-desc { margin: 0 0 18px; font-size: 13px; line-height: 1.55; color: var(--ink-soft); }
.ld-anim-lbl { margin: 0 0 8px; font-size: 12.5px; font-weight: 500; color: var(--ink); }
.ld-anim-note { font-weight: 400; color: var(--ink-mute); font-size: 11.5px; }
.ld-anims { display: flex; flex-wrap: wrap; gap: 7px; }
.ld-anim {
  padding: 6px 13px; border: 1px solid var(--bmed); border-radius: 9px; background: transparent;
  color: var(--ink65); font-family: var(--sans); font-size: 12px; cursor: pointer;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.ld-anim:hover { color: var(--ink); border-color: var(--ink32); }
.ld-anim.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
/* Still leads the row and is NOT one of the animations — the same separation
   the board's Animate menu draws with a rule under its first item. A little
   extra air after it says so without a second graphic element. */
.ld-anim.is-still { margin-right: 7px; }
/* The beat chips are a second group, not more animations — so they take their
   OWN ROW, flush with the one above (25 Aug 2026, Mark: "why are the medium,
   fast buttons indented like that"). They were separated with a left margin,
   which reads as an indent the moment the row wraps. */
.ld-anims.is-beats { margin-top: 7px; }
.ld-actions { margin-top: auto; padding-top: 20px; display: flex; align-items: center; gap: 14px; }
#layout-detail-overlay .btn-primary.ld-usebtn { flex: none; width: auto; padding: 11px 22px; }
.ld-usenote { font-size: 11.5px; color: var(--ink-mute); }
/* ==== LAYOUT DETAIL POPUP END ==== */


/* ==== FREE CANVAS (25 Aug 2026, phase 1) ====
   Items on the plate: move by drag, resize by the corner nib, ring on the
   selected one. The plate itself is the existing .layouts-plate. */
.layouts-slots.canvasfree .layouts-slot.is-cv-free { cursor: move; }
.layouts-slots.canvasfree .layouts-slot.is-cv-locked { cursor: default; }
.layouts-slots.canvasfree .layouts-slot.is-cv-sel { box-shadow: 0 0 0 2px var(--ink); }
/* Caught by the marquee, live — the guides' amber, so "Loey is pointing at
   this" is one colour everywhere on the board. (What used to show here was
   Chrome's own text-selection blue, which is now prevented outright.) */
.layouts-slots.canvasfree .layouts-slot.is-cv-band { box-shadow: 0 0 0 2px #F7CE46; }
.layouts-slots.canvasfree .layouts-slot.is-cv-band.is-cv-sel { box-shadow: 0 0 0 2px #F7CE46, 0 0 0 4px var(--ink); }
/* Nothing on this board is text to be selected: a drag across it is always a
   gesture, never a highlight. */
.layouts-slots.canvasfree, .layouts-slots.canvasfree * { user-select: none; -webkit-user-select: none; }
.layouts-slots.canvasfree img { -webkit-user-drag: none; }
.layouts-slots.canvasfree .layouts-slot.is-cv-sel.is-cv-locked { box-shadow: 0 0 0 2px var(--ink50); }
/* The nib, third fitting (25 Aug 2026 — "really small" at a third of its
   size, then "massive!" at 20px once counter-scaled): a quiet 12px square at
   TRUE screen size, always on the selected item — and the usability lives in
   the invisible hit area, which stays generous (±10px) so it is caught
   without aiming. */
/* ── Crop mode, in the lightbox (27 Aug 2026) ───────────────────────────────
   The shared picker anatomy: 8px black-and-white handles, dashed centre
   guides, mono dims. Buttons are rounded squares — pills are for capture
   actions alone (Mark). */
.lb-crop-rect {
  position: absolute;
  box-shadow: inset 0 0 0 2px var(--ink), 0 0 0 9999px rgba(250, 250, 250, 0.72);
  cursor: move;
}
.app.dark .lb-crop-rect { box-shadow: inset 0 0 0 2px #FAFAFA, 0 0 0 9999px rgba(14, 14, 12, 0.62); }
.lb-crop-gv { position: absolute; left: 50%; top: 0; bottom: 0; width: 0; border-left: 1px dashed rgba(14,14,12,.4); pointer-events: none; }
.lb-crop-gh { position: absolute; top: 50%; left: 0; right: 0; height: 0; border-top: 1px dashed rgba(14,14,12,.4); pointer-events: none; }
.app.dark .lb-crop-gv { border-left-color: rgba(250,250,250,.45); }
.app.dark .lb-crop-gh { border-top-color: rgba(250,250,250,.45); }
.lb-crop-dims {
  position: absolute; right: -2px; bottom: -26px;
  font-family: var(--mono); font-size: 10px; color: var(--paper); background: var(--ink);
  border-radius: 100px; padding: 2px 8px; white-space: nowrap; pointer-events: none;
}
.lb-crop-h {
  position: absolute; width: 8px; height: 8px; background: #FFFFFF;
  box-shadow: inset 0 0 0 1px #000, 0 0 0 1px #000;
}
.lb-crop-h.is-nw { left: -4px; top: -4px; cursor: nwse-resize; }
.lb-crop-h.is-n  { left: 50%; top: -4px; margin-left: -4px; cursor: ns-resize; }
.lb-crop-h.is-ne { right: -4px; top: -4px; cursor: nesw-resize; }
.lb-crop-h.is-e  { right: -4px; top: 50%; margin-top: -4px; cursor: ew-resize; }
.lb-crop-h.is-se { right: -4px; bottom: -4px; cursor: nwse-resize; }
.lb-crop-h.is-s  { left: 50%; bottom: -4px; margin-left: -4px; cursor: ns-resize; }
.lb-crop-h.is-sw { left: -4px; bottom: -4px; cursor: nesw-resize; }
.lb-crop-h.is-w  { left: -4px; top: 50%; margin-top: -4px; cursor: ew-resize; }
.lb-crop-bar {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 12px;
  display: flex; flex-direction: column; align-items: center; gap: 8px; z-index: 7;
}
.lb-crop-ratios, .lb-crop-acts { display: flex; gap: 6px; }
.lb-crop-btn.is-ratio { font-family: var(--mono); font-size: 10.5px; padding: 6px 10px; color: var(--ink65); }
.lb-crop-btn.is-ratio.is-on { border-color: var(--ink); color: var(--ink); background: var(--ink04); font-weight: 500; }
/* The image-centre snap lines — visible only while snapped, so seeing one
   MEANS being centred on that axis. */
.lb-crop-snap-v { position: absolute; left: 50%; top: 0; bottom: 0; width: 0; border-left: 1px solid rgba(14,14,12,.55); display: none; pointer-events: none; z-index: 5; }
.lb-crop-snap-h { position: absolute; top: 50%; left: 0; right: 0; height: 0; border-top: 1px solid rgba(14,14,12,.55); display: none; pointer-events: none; z-index: 5; }
.app.dark .lb-crop-snap-v { border-left-color: rgba(250,250,250,.6); }
.app.dark .lb-crop-snap-h { border-top-color: rgba(250,250,250,.6); }
.lb-crop-btn {
  border: 1px solid var(--bmed); border-radius: 9px; background: var(--paper); color: var(--ink);
  font-family: var(--sans); font-size: 12px; padding: 8px 14px; cursor: pointer;
}
.lb-crop-btn.is-primary { background: var(--ink); border-color: var(--ink); color: var(--paper); font-weight: 500; }

/* ── The free canvas reference grid (27 Aug 2026, Mark) ─────────────────────
   Preview only — exports paint from state, never from this DOM. Hairlines at
   1/20 of the plate's width, square cells; a slightly firmer line every
   fourth so the eye can count without counting. */
/* ink08/ink18 read as nothing on the tinted board (Mark: "very hard to see.
   very faint") — the grid is a WORKING guide, not a texture, so it gets
   working ink: 18% minors, 32% majors. Still preview-only.

   28 Aug 2026 — three fixes in one pass:
   • Two pitches, not one. The grid divides the plate (20 columns; rows a
     multiple of four), so the cells are whole in BOTH directions and the last
     major band closes on the bottom edge instead of a stub.
   • MAJORS ON TOP. They were listed last, i.e. painted UNDER the minors, so
     wherever the two rasterised a pixel apart the major smeared into a pair of
     lines. On top, the major covers its own minor and the band stays one line.
   • --lcv-line carries a counter-scaled hairline (see layoutsFitBoard), so a
     line is a true 1px on screen at any fit or zoom. */
.layouts-slots.canvasfree.is-grid {
  background-image:
    repeating-linear-gradient(to right, var(--ink32) 0 var(--lcv-line, 1px), transparent var(--lcv-line, 1px) calc(var(--lcv-grid-x, 48px) * 4)),
    repeating-linear-gradient(to bottom, var(--ink32) 0 var(--lcv-line, 1px), transparent var(--lcv-line, 1px) calc(var(--lcv-grid-y, 48px) * 4)),
    repeating-linear-gradient(to right, var(--ink18) 0 var(--lcv-line, 1px), transparent var(--lcv-line, 1px) var(--lcv-grid-x, 48px)),
    repeating-linear-gradient(to bottom, var(--ink18) 0 var(--lcv-line, 1px), transparent var(--lcv-line, 1px) var(--lcv-grid-y, 48px));
  background-position: 0 0;
}
.lt-icon-btn { display: inline-flex; align-items: center; justify-content: center; padding: 4px 7px; border: 0; background: transparent; color: var(--ink50); border-radius: 7px; cursor: pointer; }
.lt-icon-btn.is-on { background: var(--ink08); color: var(--ink); }
.lcv-handle {
  position: absolute; right: -5px; bottom: -5px; width: 9px; height: 9px;
  border-radius: 3px; background: var(--paper); border: 1px solid var(--ink);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .14);
  cursor: nwse-resize; z-index: 6; opacity: 0; transition: opacity .12s ease;
}
.lcv-handle::after { content: ''; position: absolute; inset: -10px; }
/* The second nib, TOP-LEFT — opposite the bottom-right one, and on the other
   side of the capture from the Replace/Remove cluster, which owns top-right
   (Mark: "gets in the way of the delete. i think we need them on opposite
   sides"). The number chip steps right to clear it, on this board only. */
.lcv-handle.is-nw { right: auto; bottom: auto; left: -5px; top: -5px; }
.lcv-handle.is-ne { bottom: auto; top: -5px; cursor: nesw-resize; }
.layouts-slots.canvasfree .layouts-slot-index { left: 22px; }
/* ON THE SELECTED CAPTURE ONLY (25 Aug 2026, Mark: "they look big and ugly").
   Two nibs flashing on every capture the pointer crossed was most of the
   noise; selection is one click away, and a selected capture is the only one
   a resize could be about. */
.layouts-slots.canvasfree .layouts-slot.is-cv-sel .lcv-handle { opacity: 1; }
/* MULTISELECT (25 Aug 2026): a hairline round the whole selection, carrying
   the group's nibs. It never takes the pointer — the captures under it stay
   clickable — but its nibs do. */
.lcv-mbox {
  position: absolute; z-index: 997; pointer-events: none;
  border: 1px solid var(--ink); border-radius: 2px;
}
.lcv-mbox .lcv-handle { opacity: 1; pointer-events: auto; }
/* The marquee — the selection box you drag on the bare plate. The same ink
   hairline as the group box, with the faintest wash inside so it reads as an
   area rather than an outline. */
.lcv-band {
  position: absolute; z-index: 999; pointer-events: none;
  border: 1px solid var(--ink); background: var(--ink08); border-radius: 2px;
}
/* With a group up, the group's nibs are the only nibs — an item's own would
   promise a resize of that one capture, which is not what they would do. */
.layouts-slots.canvasfree.is-cv-multi .layouts-slot .lcv-handle { display: none; }
/* The anchor row — the one the keyboard and the next click speak to. */
.lcv-lrow.is-on.is-anchor { background: var(--ink08); }
/* The corner cluster grows on the canvas — let it wrap on narrow items. */
.layouts-slots.canvasfree .layouts-slot-corner { flex-wrap: wrap; justify-content: flex-end; }
/* The snap guides — the marker amber, 2px, full strength (25 Aug 2026,
   Mark: "snap lines are very thin"): the same #F7CE46 the preview's marker
   lines wear, so "a line Loey drew for you" is one colour everywhere. */
/* 1 true px (Mark: "lines a bit too thick as well") — the amber carries the
   visibility now, not the weight. */
.lcv-guide { position: absolute; z-index: 998; pointer-events: none; background: #F7CE46; }
.lcv-guide-v { top: 0; bottom: 0; width: 2px; margin-left: -1px; }
.lcv-guide-h { left: 0; right: 0; height: 2px; margin-top: -1px; }
/* The canvas's floating thumbnail bar: the queue strip's dress, plus the
   selected item's ring — the strip is also the z-order editor here. */
.layouts-qstrip.is-cvstrip .qs-cell { cursor: pointer; }
.layouts-qstrip.is-cvstrip .qs-cell.is-cv-on { outline: 2px solid var(--ink); outline-offset: 1px; border-radius: 4px; }
/* The resize readout — the size you are making it, in per-cent of the plate,
   beside the nib you are dragging. True screen size, like the nibs. */
.lcv-size {
  position: absolute; z-index: 1000; pointer-events: none;
  padding: 3px 6px; border-radius: 6px;
  background: var(--ink); color: var(--paper);
  font-family: var(--mono); font-size: 10.5px; line-height: 1; white-space: nowrap;
}
/* Styling a recording that cannot play here (26 Aug 2026): the plate shows
   its sharpest stored frame, and this says so rather than leaving a soft
   picture to be read as a bug. */
.ss-vpiece .ss-vstill {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 6;
  padding: 7px 10px; font-family: var(--sans); font-size: 10.5px; line-height: 1.35;
  color: var(--paper); background: rgba(14, 14, 12, .62);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  pointer-events: none; text-align: center;
}
/* The equal-gap measure bars — the text layer's distance vocabulary. */
.lcv-gapbar { position: absolute; z-index: 998; pointer-events: none; background: #F7CE46; border-radius: 2px; }
/* THE LAYERS PANEL (25 Aug 2026, "layers") — front-first rows in the right
   panel; ONE z-editor, replacing the canvas's floating strip. */
.lcv-layers { margin: 4px 0 2px; }
.lcv-layers-h { margin: 14px 0 6px; }
.lcv-lrow {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 6px; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent;
}
.lcv-lrow:hover { background: var(--ink08); }
.lcv-lrow.is-on { border-color: var(--ink); }
.lcv-lrow.is-locked .lcv-lname { color: var(--ink50); }
.lcv-lrow.is-lifting { opacity: .45; }
.lcv-lrow.drop-above { box-shadow: 0 -2px 0 0 #F7CE46; }
.lcv-lrow.drop-below { box-shadow: 0 2px 0 0 #F7CE46; }
.lcv-lthumb { flex: none; width: 34px; height: 24px; object-fit: cover; border-radius: 4px; border: 1px solid var(--ink16, rgba(0,0,0,.14)); background: var(--ink08); }
/* A TEXT layer's chip — a T where a capture shows its picture, so the two
   kinds of row read apart at a glance without a second row shape. */
.lcv-tthumb {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--sans); font-size: 11px; font-weight: 600; color: var(--ink50);
}
.lcv-lname.lcv-lempty { color: var(--ink50); font-style: italic; }
.lcv-lname { flex: 1; min-width: 0; font-size: 12px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lcv-lbtn {
  flex: none; width: 22px; height: 22px; border: 0; border-radius: 6px;
  background: transparent; color: var(--ink50); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity .12s ease;
}
.lcv-lrow:hover .lcv-lbtn, .lcv-lrow.is-on .lcv-lbtn, .lcv-lbtn.is-on { opacity: 1; }
.lcv-lbtn:hover { background: var(--ink08); color: var(--ink); }
.lcv-lbtn.is-on { color: var(--ink); }
/* ==== FREE CANVAS END ==== */

/* ── THE END FRAME ─────────────────────────────────────────────────────────
   29 Aug 2026. It had a rule above it and a gap, plus its own weight on the
   name — "separate from other frames" read as a visual instruction. It was
   not: Mark, seeing it, "dont need to separate the end frame card from the
   others so remove the lines. make sure styling is the same."

   Right. The card is separate in the MODEL — it cannot be reordered, nothing
   lands after it, and it is named END rather than numbered. Those facts are
   already visible in the card itself, so drawing a line as well says the same
   thing twice and makes the rail look like it has two sections in it.

   So there are no styles here at all. `.is-endframe` stays on the element as a
   hook — the harness identifies the card by it — but it paints nothing, and the
   End card is a frame card like every other frame card. The absence is the
   decision; that is why this note is here and the rules are not. */

/* ── PAGE DEMO'S ENDING ─────────────────────────────────────────────────────
   The ending is a ROW, not furniture: you right-click a link and choose it,
   exactly like a click or a hover, and it lands in the frame you are on. So
   there is almost nothing to dress — the row wears every rule the action rows
   already have. These two say the only things that are different about it.
   (29 Aug 2026, Mark: "I just thought that it would be an option when you
   right click, the end/last/final click. nothing extra in the rail.") */
#tour-sections .tour-ev-tag.is-end {
  /* Not a button: an ending cannot be flipped to a hover, so it must not wear
     the affordance that says it can. */
  cursor: default;
  color: var(--ink);
}

/* An option that cannot apply right now — the reason is in its summary and its
   tooltip, so the row only has to look unavailable rather than explain itself.
   (29 Aug 2026: Loop, while a Page demo ends on a click.) */
.option-row.is-unavailable { opacity: .45; }
.option-row.is-unavailable .opt-switch { pointer-events: none; }


/* ==== THE RAILS SAY HOW (2 Sep 2026, 14c) ====
   The Viewport/Area rails carry a "how it's taken" card (three steps, the window
   panel's keys) and, for Viewport, Quick capture as its own block. The pill top
   right is the opener. The source bar's Capture on this tab went with the bar
   (5 Sep), and its styles on 23 Sep 2026. */
/* The rail group carrier that says nothing. */
.pc-grp-lbl-silent { display: none !important; }
/* How it's taken. */
.pc-status { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; padding: 9px 12px; border: 1px solid var(--ink08); border-radius: 10px; background: var(--ink04); margin-bottom: 8px; font-size: 12px; }
.pc-status-k { font-weight: 500; color: var(--ink); }
.pc-status-v { font-family: var(--mono); font-size: 10px; letter-spacing: .04em; color: var(--ink50); line-height: 1.5; max-width: 100%; }
.pc-status.is-open .pc-status-v { color: var(--ink); }
.pc-status.is-open .pc-status-v::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent, #F7CE46); margin-right: 6px; vertical-align: 1px; }
.pc-how { border: 1px solid var(--ink08); border-radius: 12px; padding: 0 12px; background: var(--paper); }
.app.dark .pc-how { background: var(--card-fill); }
.pc-how-h { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 500; padding: 11px 0; cursor: pointer; list-style: none; }
.pc-how-h::-webkit-details-marker { display: none; }
.pc-how-h svg { color: var(--ink50); flex: none; }
.pc-how-h .pc-how-cv { margin-left: auto; transition: transform .15s ease; }
.pc-how[open] .pc-how-h .pc-how-cv { transform: rotate(180deg); }
.pc-how[open] .pc-how-h { border-bottom: 1px solid var(--ink08); margin-bottom: 10px; }
.pc-how .pc-steps, .pc-how .pc-keys { margin-bottom: 12px; }
.pc-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.pc-steps li { display: grid; grid-template-columns: 18px 1fr; column-gap: 8px; font-size: 11.5px; line-height: 1.45; color: var(--ink65); }
.pc-steps li span { width: 18px; height: 18px; border-radius: 50%; background: var(--ink08); font-family: var(--mono); font-size: 9px; display: inline-flex; align-items: center; justify-content: center; color: var(--ink); margin-top: 1px; }
.pc-keys { display: flex; align-items: center; gap: 6px; margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--ink08); flex-wrap: wrap; }
.pc-how .pc-keys { margin-top: 0; }
.pc-keys kbd { font-family: var(--mono); font-size: 10px; letter-spacing: .06em; border: 1px solid var(--bmed); border-radius: 7px; padding: 3px 6px; background: var(--paper); color: var(--ink); display: inline-flex; align-items: center; gap: 3px; }
.pc-keys kbd .pc-key-sym { font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif; font-size: 12px; line-height: 1; }
.pc-keys small { font-size: 10.5px; color: var(--ink50); margin-right: 6px; }
/* Quick capture, its own block. */
.pc-quick { margin-top: 12px; border: 1px solid var(--ink08); border-radius: 12px; padding: 12px; background: var(--ink04); }
.pc-quick-h { display: flex; align-items: center; gap: 9px; font-size: 12.5px; font-weight: 500; }
.pc-quick-h .pc-quick-devs { display: inline-flex; align-items: center; gap: 4px; color: var(--ink); }
.pc-quick-h .pc-quick-devs svg:nth-child(1) { width: 18px; height: 18px; }
.pc-quick-h .pc-quick-devs svg:nth-child(2) { width: 15px; height: 15px; }
.pc-quick-h .pc-quick-devs svg:nth-child(3) { width: 12px; height: 12px; }
.pc-quick .pc-quick-sub { margin: 6px 0 10px; font-size: 11.5px; line-height: 1.45; color: var(--ink50); }
.pc-quick-btn {
  appearance: none; width: 100%; border: 1px solid var(--ink); background: transparent; color: var(--ink);
  font: inherit; font-size: 12.5px; font-weight: 500; padding: 8px 14px; border-radius: 100px; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.pc-quick-btn:hover:not(:disabled) { background: var(--ink); color: var(--paper); }
.pc-quick-btn:disabled { opacity: .38; cursor: default; }

/* Diagnostics (14q) */
.diag-row { display: block; }
.diag-log { width: 100%; min-height: 220px; max-height: 420px; resize: vertical; font-family: var(--mono); font-size: 10.5px; line-height: 1.5; color: var(--ink); background: var(--ink04); border: 1px solid var(--ink08); border-radius: 10px; padding: 10px 12px; white-space: pre; overflow: auto; }
.diag-actions { display: flex; gap: 8px; }
/* THE DEVICE BOARD'S RAIL (13 Sep 2026): the Studio's dials fold under their heading; a film's note in the Angle row. */
.ldev-studio { margin-top: 14px; }
/* THE TIMELINE (13 Sep 2026): the device board's film, in the Sequence editor's own rows and classes. */
/* THE FILM'S TIMELINE WEARS THE SEQUENCE'S (13 Sep 2026, Mark: "more like the sequence timeline. styling, width"): no card
   and no frame, the full width of the column under the stage, the transport bar's 34px disc and 10px mono clock */
.ldev-tl { width: 100%; margin: 0; flex: none; padding: 0 2px 4px; background: none; border-radius: 0; }
.ldev-tl-bar { display: flex; align-items: center; gap: 9px; padding: 10px 0 0; }
.ldev-tl-play { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 50%; border: 0; background: var(--ink); color: var(--paper, #fff); cursor: pointer; padding: 0; }
.ldev-tl-play:hover { opacity: .82; }
.ldev-tl-time { font: 400 10px/1 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink-soft); white-space: nowrap; flex: none; }
.ldev-tl-film { margin-left: auto; font: 500 11px/1 var(--mono, monospace); letter-spacing: .06em; text-transform: uppercase; color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-tl .reel-ruler { touch-action: none; user-select: none; }
/* A DRAG DRAGS, IT DOES NOT SELECT (19 Sep 2026, Mark: "draggin the timeline highlights the beat numbers?"): the ruler's lengths,
   the cards' names and the rows' names are words, so a drag across them took them as a text selection. The dock's own fields keep
   their selection. */
#layouts-stage > .ldev-tl, .reel-dock { user-select: none; -webkit-user-select: none; }
#layouts-stage > .ldev-tl input, #layouts-stage > .ldev-tl [contenteditable], .reel-dock input, .reel-dock [contenteditable] { user-select: text; -webkit-user-select: text; }
/* THE STRIP OF SECONDS AND THE CARDS' OWN WORDS (21 Sep 2026, the timeline mockup, Mark: "a bit more unified"): the ruler is ticks
   you scrub along, the card names itself and gives its length at its top, a Move's join its seconds under the mark */
.ldev-tl .reel-ruler { height: 18px; }
.ldev-tl .ldev-ticks { position: absolute; inset: 0; }
/* the chosen shot's poses on the ruler over its card, clear of the card's x (22 Sep 2026, Mark: "will need to not get in the way of the close x") */
.ldev-tl .ldev-posemarks { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
.ldev-posemark { position: absolute; bottom: -5px; width: 10px; height: 10px; margin-left: -5px; color: var(--ink); pointer-events: auto; cursor: pointer; }
.ldev-posemark .ldev-dia path { fill: var(--bg); }
.ldev-posemark.is-on .ldev-dia path { fill: currentColor; }
.ldev-tick { position: absolute; bottom: 0; width: 1px; height: 4px; background: var(--ink32, rgba(14,14,12,.32)); }
.ldev-tick.is-major { height: 7px; }
.ldev-tick b { position: absolute; bottom: 9px; left: 0; transform: translateX(-50%); font: 400 9.5px/1 var(--mono, monospace); color: var(--ink50, rgba(14,14,12,.5)); white-space: nowrap; }
.ldev-tick.is-first b { transform: none; }
.app.dark .ldev-tick b { color: var(--ink65); }   /* dark's half ink read at 2.9 on the rail at 9.5px (21 Sep 2026, the dark pass; probe-dark) */
/* the name and the length under the picture in Expanded, and nothing in Compact (21 Sep 2026, Mark: "having the name on the
   timeline over the thumbnail... too messy. maybe on expanded show the name under the thumbnail. collapsed, no name") */
.ldev-tl .reel-clip-name, .reel-dock .reel-clip-name { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ldev-tl .reel-clip-name > span, .reel-dock .reel-clip-name > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.reel-clip-name .ldev-card-ln { flex: none; font: 400 10px/1 var(--mono, monospace); font-style: normal; color: var(--ink65, rgba(14,14,12,.6)); }
.ldev-tl .reel-clip.t-thin .ldev-card-ln, .reel-dock .reel-clip.t-thin .ldev-card-ln { display: none; }
.ldev-tl .reel-clip.is-playing { box-shadow: inset 0 0 0 1px var(--ink32, rgba(14,14,12,.32)); }
.ldev-tl .reel-strip .ldev-join.is-move { flex-direction: column; gap: 3px; }
.ldev-tl .reel-strip .ldev-join .ldev-join-t { font: 400 9.5px/1 var(--mono, monospace); font-style: normal; color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-tl .reel-strip .ldev-join.t-thin .ldev-join-t { display: none; }
/* a device board's plate has square corners, the picture covering it to the edge (21 Sep 2026, see layoutsFitBoard) */
#layouts-stage .layouts-plate:has(.layouts-slot.is-device) { border-radius: 0; }
/* the stage never lifts a ghost of the board (21 Sep 2026, Mark: "it just moves the whole image. looks a bit broken") */
#layouts-stage .layouts-slot.is-device, #layouts-stage .layouts-slot.is-device * { user-select: none; -webkit-user-drag: none; }
/* the chosen shot's pose above the preview (21 Sep 2026, Mark: "so its obvious which frame you are working on") */
main.main > .ldev-topshot { display: none; position: absolute; z-index: 5; top: 17px; left: 50%; transform: translateX(-50%); align-items: center; gap: 12px; height: 36px; white-space: nowrap; }   /* centred over the preview, level with Focus (Mark: "can the start/end at top be centred over the preview") */
.app.mode-studio:has(#layouts-stage .layouts-plate) main.main > .ldev-topshot { display: inline-flex; }
html.is-focus main.main > .ldev-topshot { display: none; }
.ldev-tl .reel-strip { overflow: visible; padding-bottom: 2px; }
.ldev-tl .reel-clip { height: 84px; }
.ldev-tl .reel-clip-th canvas { display: block; width: 100%; height: 100%; object-fit: contain; }
.ldev-tl .reel-clip.t-thin .reel-clip-th { display: none; }
.ldev-tl .reel-add { width: auto; padding: 0 12px; font: 500 12px/1 var(--sans, sans-serif); color: var(--ink); }
.ldev-join { position: absolute; top: 50%; transform: translate(-50%, -50%); z-index: 4; width: 20px; height: 20px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 50%; background: var(--paper); color: var(--ink65, rgba(14,14,12,.65)); cursor: pointer; }
.ldev-join:hover { border-color: var(--ink); color: var(--ink); }
.ldev-join.is-cont { border-color: var(--ink); color: var(--ink); }   /* Continue: the next shot starts where this one ends (18 Sep 2026) */
.ldev-join-pop { position: fixed; z-index: 60; transform: translate(-50%, -100%); display: flex; flex-direction: column; gap: 10px; padding: 10px 12px 12px; background: var(--paper); border: 1px solid var(--bmed); border-radius: 10px; box-shadow: 0 8px 24px rgba(17,17,17,.14); }
.ldev-join-pop .pill { padding: 5px 11px; font-size: 12px; }
.ldev-join-len { display: flex; align-items: center; justify-content: space-between; gap: 12px; font: 400 12px/1 var(--sans, sans-serif); color: var(--ink); }
.ldev-join-len .opt-slider { width: 110px; }
.ldev-join-len b { font-weight: 400; width: auto; }
/* THE LAYERS (19 Sep 2026): the line a dragged row drops on, and a locked or hidden layer on the stage */
.ldev-ldrop { position: absolute; left: 4px; right: 4px; height: 2px; border-radius: 1px; background: var(--ink); pointer-events: none; }
.layouts-txel.is-locked { pointer-events: none; }
.layouts-txel.is-off { display: none; }
/* the layers under the film's timeline, a row each, on the shots' own time */
.ldev-lbars { height: auto; display: block; margin-top: 0; }
.ldev-lbars.reel-ruler-row { padding-top: 3px; }   /* straight under the shots (19 Sep 2026, Mark: "big gap between them") */
.ldev-lbars[hidden] { display: none; }
.ldev-lbtrack { position: relative; }
.ldev-lbarrow { position: relative; height: 28px; margin-top: 2px; }
/* THE BAR IS THE LANE (21 Sep 2026, the layer bars mockup, Mark: "i dont like the grey background showing slightly around the edge.
   makes it look blurry", then "ok build A"): no fill under it, the bar fills its row in the shot cards' own grey, the chosen one
   ringed in ink inside its edge */
.ldev-lbar { position: absolute; top: 1px; bottom: 1px; box-sizing: border-box; display: flex; align-items: center; gap: 6px; padding: 0 10px; border-radius: 7px; background: var(--acc-band, #e6e6e3); border: 0; font: 400 11px/1 var(--sans, sans-serif); color: var(--ink); overflow: hidden; white-space: nowrap; cursor: pointer; }
.ldev-lbar:hover { border-color: var(--ink32, rgba(14,14,12,.3)); }
.ldev-lbar .g { font: 500 10px/1 var(--mono, monospace); color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-lbar .n { overflow: hidden; text-overflow: ellipsis; }
.ldev-lbar.is-sel { box-shadow: inset 0 0 0 1.5px var(--ink); }
.ldev-lbar.is-hidden { opacity: .45; }
.ldev-lbar { cursor: grab; touch-action: none; }
.ldev-lbar.is-drag { cursor: grabbing; box-shadow: 0 4px 12px rgba(14,14,12,.14); }
.ldev-lbar::before, .ldev-lbar::after { content: ''; position: absolute; top: 0; bottom: 0; width: 8px; cursor: ew-resize; }
.ldev-lbar::before { left: 0; } .ldev-lbar::after { right: 0; }   /* the ends trim (19 Sep 2026) */
/* an image layer's place: pinned to one of nine, or free (19 Sep 2026) */
.ldev-imgrow { border: 0; padding-top: 4px; }
.ldev-pinrow { display: flex; align-items: center; gap: 14px; }
.ldev-pins { display: grid; grid-template-columns: repeat(3, 16px); gap: 5px; padding: 7px; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 8px; }
.ldev-pin { width: 16px; height: 16px; padding: 0; border: 0; border-radius: 4px; background: var(--ink08, rgba(14,14,12,.09)); cursor: pointer; }
.ldev-pin:hover { background: var(--ink18, rgba(14,14,12,.22)); }
.ldev-pin.is-on { background: var(--ink); }
/* LAYERS ON A TIMELINE, AS MOYO'S (19 Sep 2026): the names and their eyes in a column on the left, a line and Behind above the layers
   behind the device or the blocks, a bar per layer that moves and trims to the moment, a readout while it goes */
.ldev-tl.has-layers .reel-ruler-row, .ldev-tl.has-layers .ldev-striprow, .ldev-tl.has-layers .ldev-lbars { margin-left: 88px; }
.ldev-striprow { position: relative; }
.ldev-stripclip { position: relative; }
.ldev-stripclip > .reel-strip { width: max-content; min-width: 100%; }
.ldev-tl.is-zoom .ldev-stripclip { overflow: hidden; }
.ldev-tl .reel-strip { position: relative; }
.ldev-gutlbl { display: none; }
.ldev-tl.has-layers .ldev-gutlbl, .reel-has-layers .ldev-gutlbl { display: block; position: absolute; left: -82px; top: 50%; transform: translateY(-50%); font: 500 11px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-lgut { position: absolute; left: -88px; width: 80px; top: 0; bottom: 0; box-sizing: border-box; padding: 0 4px 0 6px; border-radius: 7px; display: flex; align-items: center; gap: 4px; font: 400 11.5px/1 var(--sans); color: var(--ink); cursor: grab; touch-action: none; user-select: none; }
.ldev-lgut { background: var(--bg, #fafafa); z-index: 3; }   /* the film pans behind the names, never through them (20 Sep 2026, Mark: "zoom breaking timeline") */
.ldev-lbar { z-index: 1; }
.ldev-lgut:hover { background: var(--pill, rgba(14,14,12,.055)); } .ldev-lgut.is-sel { background: var(--ink08, rgba(14,14,12,.08)); }
.ldev-lgut .g { flex: none; width: 18px; display: inline-flex; align-items: center; font: 500 10px/1 var(--mono, monospace); color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-lgut .g svg { width: 12px; height: 12px; }
.ldev-lsp { flex: 1; min-width: 0; }   /* the name has gone to the bar; this holds Lock and Eye at the column's right, and is the row's grip with the mark beside it (20 Sep 2026) */
.ldev-lnm { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ldev-lgut .n { font: 400 12px/1.15 var(--sans); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ldev-lnm .w { display: none; }   /* when it is on is in its bar now, as the ruler's lengths are in theirs */
.ldev-lbar .n { flex: 1; min-width: 0; text-align: left; }
.ldev-lbh { flex: none; font: 400 9.5px/1 var(--mono, monospace); letter-spacing: .02em; color: var(--ink50, rgba(14,14,12,.5)); }   /* a layer behind says so by its name, on the bar (20 Sep 2026) */
.ldev-lbar.is-thin .ldev-lbh { display: none; }
.ldev-lgut.is-hidden .ldev-lnm, .ldev-lgut.is-hidden .g { opacity: .45; }
.ldev-lgut.is-sel .n { font-weight: 500; }
.ldev-leye { flex: none; display: inline-flex; padding: 2px; border: 0; background: transparent; color: var(--ink65, rgba(14,14,12,.65)); cursor: pointer; }
.ldev-leye:hover { color: var(--ink); } .ldev-leye.is-off { opacity: .35; }
.ldev-llk { display: none; } .ldev-lgut:hover .ldev-llk, .ldev-lgut.is-sel .ldev-llk, .ldev-llk.is-on { display: inline-flex; }   /* the lock when pointed at, chosen or on, so the names keep their room */
.ldev-lbtrack > .ldev-ldrop { left: -88px; right: 0; } .ldev-lbarrow.is-ldrag { opacity: .5; }
/* A BLOCK'S OWN TEXT AS ROWS (19 Sep 2026, Mark: "ok do 6"): under the sequence's layers, a line naming the block, then the block's
   own text; the bars are dashed because they belong to the block and travel with it, and they do not drag */
.ldev-lbarrow.is-blkhead { height: 22px; }
.ldev-lbarrow.is-blkhead::before { top: 50%; bottom: auto; height: 0; background: none; border-radius: 0; border-top: 1px dashed var(--ink18, rgba(14,14,12,.2)); }
.ldev-lgut.is-blkhead { cursor: default; width: max-content; max-width: 340px; }   /* it has no bar, so its name runs on past the column (20 Sep 2026) */
.ldev-lgut.is-blkhead:hover { background: none; }
.ldev-lgut.is-blkhead .g, .ldev-lgut.is-blkhead .n { color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-lgut.is-blkhead .n { font-size: 11px; }
.ldev-lgut.is-blk { cursor: pointer; }
.ldev-lbar.is-blk { background: transparent; border: 1px dashed var(--ink32, rgba(14,14,12,.3)); cursor: pointer; }   /* a block's own text: dashed, since it travels with the block */
.ldev-lbar.is-blk.is-sel { border: 1.5px solid var(--ink); box-shadow: none; }
.ldev-lbar.is-blk::before, .ldev-lbar.is-blk::after { display: none; }
/* EXPANDED SHOWS THE MOTION (20 Sep 2026, Mark: "I think the expanded view should make the layers bigger and show any motion detail
   like ramps on them. collapsed is as is now"): a taller lane, and the bar drawing its reveal as a ramp in, and out where the layer
   leaves before its time is up. A line and the lightest fill, not a shade: the ramp is a shape, not a tint. */
html:not(.tl-compact) .ldev-lbarrow { height: 46px; }
html:not(.tl-compact) .ldev-lbar { top: 5px; bottom: 5px; }
.ldev-lramp { position: absolute; top: 0; bottom: 0; width: 0; display: none; pointer-events: none; }
html:not(.tl-compact) .ldev-lramp { display: block; }
.ldev-lramp svg { display: block; width: 100%; height: 100%; }
.ldev-lramp path.f { fill: var(--pill, rgba(14,14,12,.05)); stroke: none; }
.ldev-lramp path.l { fill: none; stroke: var(--ink32, rgba(14,14,12,.3)); stroke-width: 1; vector-effect: non-scaling-stroke; }
.ldev-lramp.in { left: 0; } .ldev-lramp.out { right: 0; transform: scaleX(-1); }
/* the size switch is one button now: an icon, no words */
.ldev-tlsize.is-one { border: 0; background: transparent; }
.ldev-tlsize.is-one button { width: 28px; height: 28px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 8px; background: var(--paper, #fff); color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-tlsize.is-one button:hover { color: var(--ink); background: var(--btn2bg-hover); }
.ldev-tlsize.is-one button.is-on { background: var(--paper, #fff); color: var(--ink65, rgba(14,14,12,.65)); }
/* THE TIMELINE'S ZOOM (20 Sep 2026, Mark: "need a zoom of some kind for the timeline. ?"): Fit, or the shots widened and the dock
   panned under the names' column */
.ldev-tlzoom { flex: none; display: inline-flex; height: 28px; margin-left: 8px; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 8px; overflow: hidden; background: var(--paper, #fff); }
.ldev-tlzoom button { border: 0; background: transparent; padding: 0 9px; font: 500 11.5px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.65)); cursor: pointer; }
.ldev-tlzoom button + button { border-left: 1px solid var(--bmed, rgba(14,14,12,.14)); }
.ldev-tlzoom button[data-tlzoom="fit"] { min-width: 42px; font: 400 11px/1 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink); }
.ldev-tlzoom button:hover:not(:disabled) { background: var(--btn2bg-hover); color: var(--ink); }
.ldev-tlzoom button:disabled { opacity: .35; cursor: default; }
.ldev-tl.is-zoom .reel-ruler-row { overflow: hidden; }
.ldev-tl-bar .layouts-toolbar.in-ldev-tl .ldev-tlzoom { margin-left: 8px; }
/* the playhead runs down through the shots and every lane, a line as in the ruler */
.ldev-tl { position: relative; } .reel-panel { position: relative; }
.ldev-tl-line, .reel-phline { position: absolute; width: 2px; background: var(--ink); pointer-events: none; z-index: 5; }   /* over the joins, which stand at 4 (21 Sep 2026, Mark: "playhead is running under the transition blocks") */
.ldev-tl-line[hidden], .reel-phline[hidden] { display: none; }
/* THE CHOSEN LAYER IS THE RAIL (19 Sep 2026, Mark, of Moyo's: "if you click the layers, the settings for that layer appears in the
   rail"): the way back at its head, the layer's name, what it is and when, a menu; Appearance and Motion underlined, as Moyo's */
#previewPanel.is-layer:has(> .ldev-layerview) > :not(.ldev-layerview) { display: none !important; }   /* only while the layer is drawn there: the Sequence draws its own rail into this column and leaves the class (19 Sep 2026) */
#previewPanel.is-shot:has(> .ldev-shotview) > :not(.ldev-shotview) { display: none !important; }

/* A LAYOUTS BOARD THE SAME WAY (21 Sep 2026, the Layouts rail mockup, Mark: "ok build A and B", then B put back: "no benefit"):
   the groups have no heads and stand apart by space, and the board's name carries its count */
#previewPanel .layouts-grp + .layouts-grp { margin-top: 24px; }   /* 24 between groups, every rail's (21 Sep 2026) */
/* a group's rows 23px in from the rail's edge and their values 22px from the other, the group's own 2px a side (22 Sep 2026): a device
   board's bands had carried it in their bodies, and a Layouts board's groups stood 2px short of every other rail; a list of places
   inside keeps the group's and drops its own */
#previewPanel > .layouts-grp { padding: 0 2px; }
#previewPanel > .layouts-grp .lplace-rows { padding-left: 0; padding-right: 0; }
/* a row's name gives way before its marks do, and ends in a dot rather than under the picture (21 Sep 2026, Mark: "free canvas is
   messed up. desktop word cut off"); the canvas's four marks stand compact */
#previewPanel .lplace-line > .option-label { flex: 1 1 auto; min-width: 0; }
#previewPanel .lplace-line > .option-label:not(.lplace-name) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }   /* a named row wraps whole instead (18 Sep 2026, Mark: "text that is truncated is poor") */
#previewPanel .lplace-do { flex: none; gap: 4px; }
#previewPanel .lplace-do .lplace-ico { width: 26px; height: 26px; }
#previewPanel .lplace-do .lplace-ico svg { width: 14px; height: 14px; }
#previewPanel .layouts-rail-sub { margin: -4px 0 14px; font: 400 12.5px/1.3 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); }
/* THE TEXT ROWS, A SIZE YOU CAN READ (21 Sep 2026, Mark: "make these text layers in layouts rail bigger and sort out the awkward add
   and paste buttons"): the rows at the film's lane height, one + Add text across the row and Paste a quiet pill at its end */
#previewPanel .reel-txrow { height: 38px; padding: 0 10px; gap: 9px; border-radius: 8px; }
#previewPanel .reel-txrow .g { font-size: 10px; }
#previewPanel .reel-txrow .s { font-size: 13.5px; }
#previewPanel .tx-addrow { position: relative; margin-top: 10px; }
#previewPanel .tx-addrow > .ghost-btn.is-add { flex: 1 1 auto; padding: 10px 12px; font-size: 12.5px; }
#previewPanel .tx-addrow > .tx-paste { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); flex: none; height: 24px; padding: 0 10px; font-size: 11.5px; }
#previewPanel .tx-addrow > .tx-paste[hidden] { display: none; }
/* the way back to Style at the head of the editor, the layer view's own (21 Sep 2026, Mark: "we need a way of getting back to main style mode") */
/* the text note, inside the row now, with Apply to all beside it (21 Sep 2026) */
.ss-txall { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 12px 0 2px; font: 400 12px/1.35 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); }
.ss-txall .pill { flex: none; }   /* and the chosen shot, the same way (21 Sep 2026, Mark: "only its settings appear in rail, like what layers does") */
/* THE SHOT'S HEAD IS A LAYER'S (21 Sep 2026, Mark: "we can make this top section alot nicer and cleaner", then the shot head
   mockup: "ok do B"): no tray and no field showing, the name as the title with the frame of its capture beside it, and one line
   under it saying which shot of how many and how long it runs. */
/* the frame stands as tall as the name and its line together, so the three edges meet (21 Sep 2026, Mark: "nothing seems to line up") */
.ldev-shotview .ldev-shothead { margin: 0; padding: 0; min-height: 0; background: none; align-items: flex-start; gap: 12px; }
.ldev-shotview .ldev-shothead .ldev-shotpic { width: 64px; height: 46px; flex: none; border-radius: 8px; margin: 0; }   /* a fixed height: stretched, its picture set the row's height to the capture's own */
.ldev-shotview .ldev-shothead .t { display: flex; flex-direction: column; justify-content: space-between; min-height: 46px; }
.ldev-shotview .ldev-shothead .t span { display: block; margin-top: 4px; font: 400 12.5px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); }
.ldev-shotview .ldev-shothead .ldev-shottitle { display: block; width: 100%; box-sizing: border-box; max-width: none; margin: -3px -7px 0; padding: 1px 7px; border: 1px solid transparent; border-radius: 7px; background: none; font: 500 20px/1.2 var(--sans); color: var(--ink); }
.ldev-shotview .ldev-shothead .ldev-shottitle:hover { background: var(--pill, rgba(14,14,12,.04)); }
.ldev-shotview .ldev-shothead .ldev-shottitle:focus { outline: none; border-color: var(--ink); background: var(--paper, #fff); }
.ldev-shotview .ldev-shotbody { padding-top: 8px; }
/* AIR, NOT HEADINGS (21 Sep 2026, Mark: "needs to be cleaner. maybe more spacing between things. I think the whole app needs more
   spacing and less wording"): the shot's blocks stand apart by space alone */
.ldev-shotview .ldev-shotbody > .ldev-shotgrp > .option-row:first-of-type, .ldev-shotview .ldev-tgwrap, .ldev-shotview .ldev-moverow { margin-top: 24px; }   /* 24 between groups, every rail's (21 Sep 2026) */
.ldev-shotview .ldev-dials { margin-top: 14px; }   /* a touch less between Move and Length (21 Sep 2026, Mark: "slightly smaller gap") */
.ldev-shotview .ldev-dials > .option-row { padding: 0; margin: 0; }   /* 48px rows as every rail's, no air of their own (21 Sep 2026) */
/* THE CAPTURE IS THE PICTURE (21 Sep 2026, Mark: "what about having change and remove icons above the thumbnail and the start/end under
   the thumbnail?"): its tools above it at the right, the pose pills beneath it, and no words naming it twice */
.ldev-caphead { display: flex; justify-content: flex-end; gap: 6px; margin-bottom: 8px; }
.ldev-caphead .ldev-tool.ldev-captool { flex: 0 0 30px; width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }   /* the actions' tools grow to fill their row; these two stay square */
.ldev-shotview .ldev-tgwrap .ldev-poserow { margin-top: 12px; }
/* THE BOXES ARE ALWAYS THERE (21 Sep 2026, Mark: "then you know straight away that you can move/drag them"): a pose with no box of
   its own wears one at the whole capture, dashed, until its corners are pulled in or a box is drawn inside it */
.ldev-target { position: relative; }
/* an empty card on the strip says how to fill it (21 Sep 2026, Mark: "the actual shot layer is quite quiet") */
.ldev-card-add { position: absolute; inset: 0; margin: auto; width: max-content; height: 28px; padding: 0 12px; border: 1px dashed var(--bmed, rgba(14,14,12,.16)); border-radius: 8px; background: var(--paper, #f6f6f4); font: 500 11.5px/1 var(--sans); color: var(--ink); cursor: pointer; z-index: 2; }
.ldev-card-add:hover { border-color: var(--ink); }
html.tl-compact .ldev-tl .reel-clip-th { position: relative; }
/* a long label gives way to its number, not the other way round, on every slider row in the rail (21 Sep 2026, Mark: "up and down
   number is at the edge", then Camera zoom doing the same in the Scene band) */
#previewPanel .opt-toggle-row .option-label { flex: 0 1 auto; min-width: 0; padding-right: 10px; }
#previewPanel .opt-slider-wrap { flex: 1 1 auto; min-width: 0; justify-content: flex-end; }
#previewPanel .opt-slider-wrap .opt-slider { flex: 1 1 auto; width: auto; min-width: 70px; max-width: 150px; }
#previewPanel .opt-slider-val { flex: none; min-width: 42px; text-align: right; }
.ldev-layerview { padding-top: 2px; }
.ldev-lback { display: inline-flex; align-items: center; gap: 7px; margin: 16px 0 22px; padding: 0; border: 0; background: none; font: 400 14px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); cursor: pointer; }   /* a size you can see (21 Sep 2026, Mark: "The link back seems too small") */
.ldev-lback svg { width: 13px; height: 13px; }
.ldev-lback:hover { color: var(--ink); }
.ldev-lhead { display: flex; align-items: flex-start; gap: 10px; }
.ldev-lhead .t { flex: 1; min-width: 0; }
.ldev-lhead .t b { display: block; font: 500 20px/1.2 var(--sans); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ldev-lhead .t span { display: block; margin-top: 6px; font: 400 12.5px/1.3 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); }
.ldev-lmore { flex: none; width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--bmed, rgba(14,14,12,.14)); background: transparent; display: inline-flex; align-items: center; justify-content: center; color: var(--ink65, rgba(14,14,12,.65)); cursor: pointer; }
.ldev-lmore:hover { color: var(--ink); background: var(--btn2bg-hover); }
/* the Sequence's rail: Block settings and Sequence settings as tabs, one section showing, a chosen layer in their place */
.reel-rtabs { display: flex; padding: 4px; border-radius: 12px; background: var(--pill, rgba(14,14,12,.06)); margin: 2px 0 16px; }
.reel-rtabs button { flex: 1; border: 0; background: transparent; padding: 10px 0; border-radius: 9px; font: 500 13px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.6)); cursor: pointer; }
.reel-rtabs button.is-on { background: var(--paper, #fff); color: var(--ink); box-shadow: 0 1px 2px rgba(14,14,12,.08); }
.reel-side.has-rtabs > details.canvas-acc > summary { display: none; }
.reel-side.has-rtabs[data-rtab="block"] > #reel-seqsec, .reel-side.has-rtabs[data-rtab="seq"] > #reel-clipcard,
.reel-side.has-rtabs:not(.is-layer) > #reel-laysec, .reel-side.has-rtabs.is-layer > #reel-seqsec, .reel-side.has-rtabs.is-layer > #reel-clipcard { display: none !important; }
/* THE TEXT RAIL AS ROWS (21 Sep 2026, Mark: "the text rail needs looking at. spacing, lines etc... all rails should have
   consistent spacing, fonts etc"): the two tabs and their line went, every control is a row with its word at the left, and a
   menu standing in a row wears the row's value voice, no box */
.ldev-ltxed { margin-top: 8px; }
.opt-toggle-row > .reel-dd.ldev-rowdd, .reel-side .opt-toggle-row > .reel-dd.ldev-rowdd { flex: 0 1 auto; min-width: 0; align-self: center; display: flex; align-items: center; }   /* flex, not block: a block gave its inline button a line box taller than the button, and the row 52px */
.reel-dd.ldev-rowdd .reel-dd-btn, .reel-side .reel-dd.ldev-rowdd .reel-dd-btn, #previewPanel .layouts-txed .reel-dd.ldev-rowdd .reel-dd-btn, .styleset-overlay .layouts-txed .reel-dd.ldev-rowdd .reel-dd-btn { width: auto; height: auto; padding: 0; border: 0; border-radius: 0; background: none; gap: 10px; justify-content: flex-end; font-family: var(--mono); font-size: 11px; letter-spacing: .04em; color: var(--ink50); }
.reel-dd.ldev-rowdd .reel-dd-btn:hover, .reel-side .reel-dd.ldev-rowdd .reel-dd-btn:hover { color: var(--ink); }
.reel-dd.ldev-rowdd .reel-dd-btn svg { color: var(--ink32); }
.reel-dd.ldev-rowdd .reel-dd-menu, .reel-side .reel-dd.ldev-rowdd .reel-dd-menu, #previewPanel .layouts-txed .reel-dd.ldev-rowdd .reel-dd-menu, .styleset-overlay .layouts-txed .reel-dd.ldev-rowdd .reel-dd-menu { left: auto; right: 0; min-width: 180px; }
#reel-txplacerow[hidden] { display: none; }   /* the row's own flex would otherwise beat hidden */
#reel-layplace { margin-top: 8px; }
.seq-settings .canvas-acc-body .reel-cc-text.is-seq > .option-expand { padding-top: 0; }
.opt-toggle-row > .swatch-grid { flex-wrap: nowrap; }
/* + Add text in the timeline's top row, its arrow for an image or a paste */
.ldev-tladd { flex: none; display: inline-flex; align-items: stretch; height: 28px; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 8px; background: var(--paper, #fff); overflow: hidden; }
.ldev-tladd button { border: 0; background: transparent; padding: 0 11px; display: inline-flex; align-items: center; font: 500 12px/1 var(--sans); color: var(--ink); cursor: pointer; }
.ldev-tladd button:hover { background: var(--btn2bg-hover); }
.ldev-tladd button + button { border-left: 1px solid var(--bmed, rgba(14,14,12,.14)); padding: 0 8px; color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-tladd.is-one button { gap: 6px; }   /* one button with its arrow inside it, not a button with a flap on the side (20 Sep 2026, Mark: "ok mockup A") */
.ldev-tladd.is-one button svg { color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-tl-bar .layouts-toolbar.in-ldev-tl .ldev-tladd { margin-left: 8px; }   /* one gap, at the size switch: a second auto margin here split the row and left the size and the zoom stranded in the middle (20 Sep 2026, Mark: "can we move the collapse/expand and zoom over to the right") */
.ldev-tl-bar .layouts-toolbar.in-ldev-tl .ldev-tladd + .num-stepper.lt-step { margin-left: 8px; }
.ldev-addmenu { min-width: 150px; } .ldev-addmenu button:nth-child(3) { margin-top: 0; border-top: 0; border-radius: 6px; padding-top: 7px; }
/* THE DOCK (19 Sep 2026, Mark: "I think the layers and timeline need to sit in its own section not floating", then "ok build it with
   the dock"): the timeline in a section of its own at the foot of the stage, edge to edge between the left nav and the rail, in the
   rail's colour with a hairline above; the picture keeps its inset above it */
html:not(.is-focus) .app.shell-modetop #layouts-stage:has(> .ldev-tl) { margin: 0 -20px -24px; min-height: calc(100vh - 72px); max-height: calc(100vh - 72px); gap: 0; }
html:not(.is-focus) #layouts-stage:has(> .ldev-tl) > .layouts-scroller { padding: 0 20px; box-sizing: border-box; width: 100%; }
#layouts-stage > .ldev-tl { align-self: stretch; width: auto; box-sizing: border-box; margin-top: 14px; padding: 12px 20px 18px; background: var(--bg, #fafafa); border-top: 1px solid var(--ink08, rgba(14,14,12,.08)); }
.reel-dock { flex: none; margin: 14px -20px -24px; padding: 12px 20px 18px; background: var(--bg, #fafafa); border-top: 1px solid var(--ink08, rgba(14,14,12,.08)); }
.app.seq-door-open .reel-dock { display: none; }   /* nothing to show behind the door; empty, it stood as a strip at the window's foot, plain in dark (21 Sep 2026) */
/* THE DOCK'S HEIGHT (19 Sep 2026, Mark: "ok build the height adjust"): the whole top edge is the grip, a short bar in its middle saying
   so; it goes while there are no lanes to size */
.reel-dock { position: relative; }
.loey-tlgrip { position: absolute; left: 0; right: 0; top: -5px; height: 10px; z-index: 4; cursor: row-resize; touch-action: none; }
.loey-tlgrip::after { content: ''; position: absolute; left: 50%; top: 3px; width: 40px; height: 4px; margin-left: -20px; border-radius: 2px; background: var(--ink18, rgba(14,14,12,.2)); transition: background .15s; }
.loey-tlgrip:hover::after, html.is-tlgrip .loey-tlgrip::after { background: var(--ink50, rgba(14,14,12,.45)); }
html.is-tlgrip, html.is-tlgrip * { cursor: row-resize !important; user-select: none !important; }
.ldev-tl:not(.has-layers) > .loey-tlgrip, .reel-panel:not(.reel-has-layers) .loey-tlgrip { display: none; }
/* THE TIMELINE'S SIZE (19 Sep 2026): Compact, the default, keeps the stage its room; Expanded shows it all */
.ldev-tlsize { flex: none; display: inline-flex; height: 28px; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 8px; overflow: hidden; background: var(--paper, #fff); }
.ldev-tlsize button { border: 0; background: transparent; padding: 0 10px; font: 500 11.5px/1 var(--sans); color: var(--ink65, rgba(14,14,12,.65)); cursor: pointer; }
.ldev-tlsize button + button { border-left: 1px solid var(--bmed, rgba(14,14,12,.14)); }
.ldev-tlsize button.is-on { background: var(--ink); color: var(--paper, #fff); }
.ldev-tl-bar .layouts-toolbar.in-ldev-tl .ldev-tlsize { margin-left: auto; }
.ldev-tl-bar .layouts-toolbar.in-ldev-tl .ldev-tlsize + .ldev-tladd { margin-left: 8px; }
.reel-tbar .ldev-tlsize { margin-left: 12px; } .reel-tbar .ldev-tlsize + .ldev-tladd { margin-left: 8px; }
html.tl-compact .ldev-tl .reel-clip, html.tl-compact .ldev-tl .reel-add, html.tl-compact .reel-panel #reel-strip .reel-clip, html.tl-compact .reel-panel #reel-strip .reel-add { height: 50px; min-height: 0; }   /* the + box too: its own 80px was the gap under the cards */
html.tl-compact .ldev-tl .reel-clip-name, html.tl-compact .reel-panel #reel-strip .reel-clip-name { display: none; }   /* the ruler above names them */
/* and the picture fills the card, no white frame round it (20 Sep 2026, Mark: "when collapsed can the blocks not have the white
   border around the thumbnail?") */
html.tl-compact .ldev-tl .reel-clip, html.tl-compact .reel-panel #reel-strip .reel-clip { padding: 0; }
html.tl-compact .ldev-tl .reel-clip-th, html.tl-compact .reel-panel #reel-strip .reel-clip-th { margin-bottom: 0; border-radius: 7px; }
.ldev-tl.has-layers .ldev-lbars, .reel-has-layers .reel-dock > #reel-lrows { margin-left: 0; padding-left: 90px; max-height: var(--tl-lanes, 108px); overflow-y: auto; overflow-x: hidden; }   /* the names' column and the row's own 2px, inside the scroll; how much shows is the grip's, in both sizes (19 Sep 2026) */
.ldev-lbars.is-more { -webkit-mask-image: linear-gradient(to bottom, #000 78%, transparent); mask-image: linear-gradient(to bottom, #000 78%, transparent); }
.ldev-lhint.is-float { position: fixed; bottom: auto; transform: translateY(-100%); z-index: 80; }
/* FOCUS (19 Sep 2026): the stage alone, a small transport floating over it */
main.main:has(> .loey-corner) { position: relative; }
/* the corner: Focus and the workspace's colour, side by side (21 Sep 2026) */
.loey-corner { display: none; position: absolute; z-index: 5; top: 17px; left: 20px; align-items: center; gap: 8px; }
.loey-focusbtn { display: inline-flex; align-items: center; padding: 0 !important; border: 0 !important; background: transparent !important; box-shadow: none !important; gap: 7px; height: 36px; padding: 0 14px 0 12px; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 10px; background: var(--paper, #fff); font: 500 13px/1 var(--sans); color: var(--ink); cursor: pointer; }
.loey-focusbtn:hover { background: var(--btn2bg-hover); } .loey-focusbtn svg { display: block; }
.loey-focusbtn kbd { font: 400 10.5px/1 var(--mono, monospace); color: var(--ink50, rgba(14,14,12,.5)); margin-left: 3px; }
.app.mode-studio:has(#layouts-stage .layouts-plate) .loey-corner, .app.mode-edits:has(.reel-panel #reel-cv) .loey-corner { display: flex; }
/* the corner on Style's editor and on a Layouts board too, never their doors (21 Sep 2026, Mark: "also need workspace colour picker for
   style and layouts"): each page shows its own word after the circle, Focus in Studio and the Sequence, the way back in Style's editor */
.app.mode-lay:not(.lay-gal-open):has(#layouts-stage .layouts-plate) .loey-corner, .app.mode-style:has(.styleset-overlay.is-inline) .loey-corner { display: flex; }
.app:not(.mode-studio):not(.mode-edits) .loey-corner .loey-focusbtn { display: none; }
.loey-back { display: none; align-items: center; gap: 6px; height: 36px; padding: 0; border: 0; background: transparent; font: 500 13px/1 var(--sans); color: var(--ws-on, var(--ink)); cursor: pointer; }
.loey-back svg { display: block; }
.app.mode-style:has(.styleset-overlay.is-inline) .loey-corner .loey-back { display: inline-flex; }
/* THE WORKSPACE'S COLOUR (21 Sep 2026, Mark: "for moyo we have a colour picker in the left corner next to focus. it is just a coloured
   circle and you click it to change the colour of the workspace"): light, grey and dark, so a pale board reads against grey or dark
   and a dark one against light; the circle wears the colour and a click steps to the next */
.loey-wsbtn { width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--ink32, rgba(14,14,12,.35)); background: var(--work-bg); padding: 0; cursor: pointer; box-sizing: border-box; }   /* one ring in the workspace's own text colour, as Focus beside it, so the circle shows against its own colour: ink on light, paper on grey and dark (21 Sep 2026, Mark: "not have a dark and white ring", then "the middle grey colour needs a ring") */
/* Focus is words on the workspace, not a box (Mark: "dark text on light and light text on dark") */
html.ws-grey .app.shell-sep:not(.lay-gal-open):not(.seq-door-open) .loey-focusbtn, html.ws-dark .app.shell-sep:not(.lay-gal-open):not(.seq-door-open) .loey-focusbtn { color: #F2F2F0 !important; }
html.ws-grey .app.shell-sep:not(.lay-gal-open):not(.seq-door-open) .loey-focusbtn kbd, html.ws-dark .app.shell-sep:not(.lay-gal-open):not(.seq-door-open) .loey-focusbtn kbd { color: rgba(242,242,240,.6); }
html.ws-grey .loey-wsbtn { background: #818181; border-color: rgba(242,242,240,.85); }
html.ws-dark .loey-wsbtn { background: #1c1c1a; border-color: rgba(242,242,240,.7); }
html.ws-grey .app.shell-sep:is(.mode-studio, .mode-edits, .mode-lay, .mode-style:has(.styleset-overlay.is-inline)):not(.lay-gal-open):not(.seq-door-open) :is(.workspace > .main, #layouts-stage, #reel-host, .reel-panel, .reel-stage, .styleset-stage) { background: #818181; }   /* the stage alone, never the doors (Mark: "it is showing in the main screens around the edge") */   /* Mark's own grey (21 Sep 2026: "try this for the middle colour - #818181"); the stage paints its own, so it takes the colour too */
html.ws-dark .app.shell-sep:is(.mode-studio, .mode-edits, .mode-lay, .mode-style:has(.styleset-overlay.is-inline)):not(.lay-gal-open):not(.seq-door-open) :is(.workspace > .main, #layouts-stage, #reel-host, .reel-panel, .reel-stage, .styleset-stage) { background: #1c1c1a; }   /* dark mode's own work area */
/* THE POSE AS WORDS ON THE WORKSPACE (21 Sep 2026, the pose bar mockup, Mark: "ok lets try B"): no box, no name and no count, the
   rail's head saying which shot; Start and End in the workspace's own text colour as Focus is, the chosen one at full strength with
   a line under it, the rest faded, and the + and the - as glyphs */
.app { --ws-on: var(--ink); --ws-on-soft: var(--ink50, rgba(14,14,12,.5)); }
html.ws-grey .app, html.ws-dark .app { --ws-on: #F2F2F0; --ws-on-soft: rgba(242,242,240,.6); }
html.ws-grey .app { --ws-on-soft: rgba(242,242,240,.75); }   /* the faded word held up a little against #818181 */
/* KEYFRAME CHIPS (22 Sep 2026, the Start and End mockup, Mark: "how can we make it obvious that you are on the start and end and these
   are things that you need to click", then "we can try A and see"): the pose you are on a filled chip, the others a hollow diamond
   with their word at full strength, none faded to look switched off; the same row over the preview and in the rail */
.app { --ws-on-inv: var(--paper); }
html.ws-grey .app, html.ws-dark .app { --ws-on-inv: #0E0E0C; }
.ldev-dia { display: block; flex: none; }
.ldev-dia path { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linejoin: round; }
.is-on > .ldev-dia path { fill: currentColor; }
.is-offer > .ldev-dia path { stroke-dasharray: 1.8 1.6; }   /* not its own yet: a held shot's End, a piece's worked out points */
#previewPanel .ldev-posesel.lib-seg, main.main > .ldev-topshot .ldev-posesel.lib-seg { gap: 2px; height: auto; border: 0; border-radius: 0; background: transparent; overflow: visible; }
#previewPanel .ldev-posesel .lib-seg-btn, main.main > .ldev-topshot .ldev-posesel .lib-seg-btn { display: inline-flex; align-items: center; gap: 7px; height: 28px; padding: 0 11px 0 9px; border: 0; border-radius: 8px; background: transparent; font: 500 13px/1 var(--sans, sans-serif); opacity: 1; }
#previewPanel .ldev-posesel .lib-seg-btn { color: var(--ink); }
#previewPanel .ldev-posesel .lib-seg-btn:hover { background: var(--pill); }
#previewPanel .ldev-posesel .lib-seg-btn.is-on { background: var(--ink); color: var(--paper); }
main.main > .ldev-topshot .ldev-posesel .lib-seg-btn { color: var(--ws-on); }
main.main > .ldev-topshot .ldev-posesel .lib-seg-btn:hover { background: color-mix(in srgb, var(--ws-on) 10%, transparent); }
main.main > .ldev-topshot .ldev-posesel .lib-seg-btn.is-on { background: var(--ws-on); color: var(--ws-on-inv); }
main.main > .ldev-topshot .ldev-poserow { display: flex; align-items: center; gap: 0; margin: 0; }
main.main > .ldev-topshot .ldev-ptadd { flex: none; width: 28px; height: 28px; border: 0; border-radius: 8px; background: transparent; color: var(--ws-on); }
main.main > .ldev-topshot .ldev-ptadd:hover { background: color-mix(in srgb, var(--ws-on) 10%, transparent); }
main.main > .ldev-topshot .ldev-posesel + .ldev-ptadd { margin-left: 10px; }
main.main > .ldev-topshot .ldev-ptadd + .ldev-ptadd { margin-left: 4px; }
html.is-tlgrip .layouts-scroller { overflow: hidden; }   /* the plate scaled while the grip moves never scrolls */   /* on paper, as Focus is, so it reads on a grey or dark workspace */
html.is-focus #previewPanel, html.is-focus header.topbar, html.is-focus .ldev-tl,
html.is-focus .reel-dock, html.is-focus #reel-tx { display: none !important; }
html.is-focus main.main { position: fixed; inset: 0 0 0 var(--focus-left, 0px); z-index: 60; padding: 72px 28px 112px !important; background: var(--focus-bg, #e6e6e4); }   /* the picture clear of the way out above it and the transport below, at any shape (19 Sep 2026, Mark: "the focus button overlaps", "in focus mode at narrow ratios there isnt enough gap") */
html.is-focus .main-inner, html.is-focus .main-inner > .panel.is-active, html.is-focus #reel-host, html.is-focus .reel-panel { height: 100% !important; max-height: 100% !important; min-height: 0 !important; }
/* and the stage with them (20 Sep 2026, Mark: "focus still overlaps, this time the transport"): its own height is measured off the
   window (100vh less the app's chrome), which is taller than the room Focus leaves, so the picture was fitted to a box that ran
   under the transport. In focus it takes the room it is given, and the fit follows, since the fit measures the scroller. */
html.is-focus #layouts-stage, html.is-focus #layouts-stage > .layouts-scroller { height: 100% !important; max-height: 100% !important; min-height: 0 !important; }
.loey-ftrans { position: fixed; z-index: 70; bottom: 26px; left: calc(var(--focus-left, 0px) + (100vw - var(--focus-left, 0px)) / 2); transform: translateX(-50%); display: flex; align-items: center; gap: 12px; height: 46px; padding: 0 8px 0 7px; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 999px; background: var(--paper, #fff); box-shadow: 0 10px 30px rgba(14,14,12,.14); }
.loey-ftrans button { border: 0; padding: 0; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.loey-ftrans .p { width: 32px; height: 32px; border-radius: 50%; background: var(--ink); color: var(--paper, #fff); }
.loey-ftrans .p svg { width: 11px; height: 11px; }
.loey-ftrans .r { background: transparent; color: var(--ink65, rgba(14,14,12,.65)); }
.loey-ftrans .t { font: 400 12px/1 var(--mono, monospace); color: var(--ink65, rgba(14,14,12,.65)); white-space: nowrap; min-width: 78px; }
.loey-ftrans .s { position: relative; width: 240px; height: 14px; cursor: pointer; touch-action: none; }
.loey-ftrans .s::before { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 3px; margin-top: -1.5px; border-radius: 2px; background: color-mix(in srgb, var(--ink) 12%, transparent); }
.loey-ftrans .s i { position: absolute; left: 0; top: 50%; height: 3px; margin-top: -1.5px; border-radius: 2px; background: var(--ink); }
.loey-ftrans .s b { position: absolute; top: 50%; width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px; border-radius: 50%; background: var(--ink); }
.loey-ftrans .x { width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--bmed, rgba(14,14,12,.14)); background: transparent; color: var(--ink65, rgba(14,14,12,.65)); }
.loey-ftrans .x:hover, .loey-ftrans .r:hover { color: var(--ink); }
.loey-ftrans .z { display: inline-flex; align-items: center; gap: 2px; }   /* the preview's zoom, in Focus alone (21 Sep 2026) */
.loey-ftrans .z button { width: 26px; height: 26px; border-radius: 6px; background: transparent; color: var(--ink65, rgba(14,14,12,.65)); font: 400 15px/1 var(--sans, sans-serif); }
.loey-ftrans .z button:hover { color: var(--ink); background: var(--btn2bg-hover, rgba(14,14,12,.06)); }
.loey-ftrans .z b { font: 400 12px/1 var(--mono, monospace); color: var(--ink65, rgba(14,14,12,.65)); min-width: 40px; text-align: center; }
/* THE SEQUENCE'S DOCK THE FILM'S WAY (21 Sep 2026, the Sequence timeline mockup, Mark: "the ruler should be beats though?", then
   "ok build it"): a strip of beats you scrub along, the cards named with their beats and their beat lines faint on them, no
   slider in the transport */
.reel-dock .reel-ruler { height: 18px; cursor: pointer; }
.reel-dock .reel-ruler .ldev-tick { position: absolute; bottom: 0; }
.reel-tspace { flex: 1; }
.reel-dock .reel-clip-th { position: relative; overflow: hidden; }
.reel-dock .reel-clip .reel-beat { position: absolute; top: 0; bottom: 0; width: 1px; background: rgba(128,128,128,.4); pointer-events: none; }
.reel-dock #reel-strip { scrollbar-width: none; } .reel-dock #reel-strip::-webkit-scrollbar { display: none; }   /* zoomed, the strip scrolls with a sideways wheel, no bar under the cards (21 Sep 2026) */
/* the Sequence's chosen layer: its text controls line up with the section, and the section's data-tab shows one half of them (19 Sep 2026) */
#reel-layhost .reel-cc-text { padding-left: 0; padding-right: 0; }
.reel-cc-text.is-seq #reel-txrm { display: none !important; }
.reel-tbar .ldev-tladd { margin-left: 12px; }
#reel-layplace, #reel-laywhen { margin-bottom: 12px; }
.ldev-lhint { position: absolute; bottom: calc(100% + 4px); z-index: 3; white-space: nowrap; padding: 4px 7px; border-radius: 5px; background: var(--ink); color: var(--paper, #fff); font: 400 11px/1 var(--sans); pointer-events: none; }
.reel-has-layers .reel-dock > .reel-ruler-row, .reel-has-layers .reel-dock > #reel-strip { margin-left: 88px; }
.reel-has-layers .reel-dock > #reel-strip { position: relative; }
.reel-lrows .ldev-lbtrack { position: relative; }
.ldev-lbars { overflow: visible; }   /* the names stand outside the rows, in the column on the left */
/* THE SEQUENCE'S LAYERS (19 Sep 2026): the block's text controls, brought into Layers while a sequence layer is edited */
.reel-cc-text.is-seq > .option-summary { display: none; }
.reel-cc-text.is-seq > .option-expand { display: block !important; }
.reel-cc-text.is-seq #reel-txlist, .reel-cc-text.is-seq .tx-addrow, .reel-cc-text.is-seq #reel-txcopy, .reel-cc-text.is-seq .opt-toggle-row:has(#reel-txfade), .reel-cc-text.is-seq #reel-txplacerow, .reel-cc-text.is-seq #reel-txlayer, .reel-cc-text.is-seq .tx-iconrow-gap { display: none !important; }
.reel-cc-text.is-img .opt-toggle-row:has(#reel-txfont, #reel-txwt, #reel-txsz, #reel-txlh, #reel-txls, #reel-txsw, #reel-txal, #reel-txlayer) { display: none !important; }   /* the type's rows, since 21 Sep 2026 rows */
.reel-txel.is-locked { pointer-events: none; }
.layouts-toolbar.has-ldev-tl [data-ltplay], .layouts-toolbar.has-ldev-tl [data-ltrestart], .layouts-toolbar.has-ldev-tl [data-ltprog], .layouts-toolbar.has-ldev-tl [data-lttime], .layouts-toolbar.has-ldev-tl [data-ltplay] + .lt-div, .layouts-toolbar.has-ldev-tl .lt-prog + .lt-div { display: none; }
.ldev-shotgrp { margin-top: 14px; }
.ldev-shot-name { flex: 1; min-width: 0; max-width: 160px; margin-left: auto; padding: 4px 8px; border: 1px solid var(--bmed, rgba(14,14,12,.14)); border-radius: 6px; background: var(--paper); font: 400 12px/1.3 var(--sans, sans-serif); color: var(--ink); }
.ldev-done { margin-left: auto; padding: 3px 10px; font-size: 12px; }
.ldev-shotgrp .lplace-head .lplace-fold { cursor: default; }
.ldev-head { margin-top: 14px; }
.ldev-head:first-child { margin-top: 0; }
.ldev-pose { margin-left: auto; font: 400 12px/1.3 var(--mono, monospace); color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-reset { margin-left: 8px; padding: 3px 9px; font-size: 12px; }
.ldev-pose-foot { justify-content: flex-end; padding: 6px 0 2px; }
.ldev-pose-foot .ldev-reset:disabled { opacity: .4; cursor: default; }
.ldev-tl-save { display: inline-flex; align-items: center; }
.ldev-tl-save .ghost-btn, .ldev-tl-bar .ghost-btn { padding: 5px 10px; font-size: 12px; }
.ldev-tl-save input { width: 150px; padding: 5px 8px; border: 1px solid var(--ink); border-radius: 7px; background: var(--paper); font: 400 12px/1.3 var(--sans, sans-serif); color: var(--ink); }
.ldev-tl-bar .lt-anim-wrap { margin-left: 4px; }
.ldev-tl-bar .num-stepper.lt-step { margin-left: auto; }
.ldev-tl-bar .num-stepper.lt-step + .ldev-tl-save { margin-left: 4px; }
/* the toolbar inside the timeline's bar: its chrome off, its menu and zoom as they were */
.ldev-tl-bar .layouts-toolbar.in-ldev-tl { flex: 1; min-width: 0; width: auto; margin: 0; padding: 0; background: none; border: 0; box-shadow: none; border-radius: 0; }
.ldev-tl-bar .layouts-toolbar.in-ldev-tl .num-stepper.lt-step { margin-left: auto; }
.ldev-tl-bar .layouts-toolbar.in-ldev-tl .lt-div:first-child { display: none; }
/* the fold under a device setting: a quiet caption that opens the finer dials (14 Sep 2026) */
.ldev-fold { margin: 6px 0 0; }
.ldev-fold > summary { list-style: none; cursor: pointer; padding: 8px 0; font: 400 12px/1 var(--sans, sans-serif); color: var(--ink50); display: flex; align-items: center; gap: 6px; }
.ldev-fold > summary::-webkit-details-marker { display: none; }
.ldev-fold > summary::before { content: ''; width: 5px; height: 5px; border-right: 1.2px solid currentColor; border-bottom: 1.2px solid currentColor; transform: rotate(-45deg); transition: transform .15s ease; margin-right: 4px; }
.ldev-fold[open] > summary::before { transform: rotate(45deg); }
.ldev-fold > summary:hover { color: var(--ink); }

/* THE SHOT'S SETTINGS, CLEANED UP (14 Sep 2026, Mark: "sort out the rail shot settings. clean it up"): group labels, a
   row of icon tools for the one shot, captures as pictures, an x on the band and on the chosen card, the film's menu */
.ldev-grp { margin: 14px 0 2px; font: 400 9.5px/1 var(--mono, ui-monospace, Menlo, monospace); letter-spacing: .14em; text-transform: uppercase; color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-tools { display: flex; gap: 4px; margin: 14px 0 4px; }
/* THE SHOT RAIL IN ORDER (18 Sep 2026, Mark: "ok lets build this rail then"): the open shot's title line, its actions in words,
   the device as the Library's selector, the finish as swatches, the capture as a row with its name whole beneath */
.ldev-shothead { display: flex; align-items: center; gap: 9px; min-height: 42px; padding: 6px 4px; border-radius: 8px; background: var(--pill, rgba(14, 14, 12, .045)); }
.ldev-shothead .ldev-shot-name { flex: 1; max-width: none; margin-left: 0; }
.ldev-acts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; margin: 10px 0 4px; }
.ldev-acts.ldev-acts2 { grid-template-columns: auto 1fr 1fr; margin-top: 0; }
.ldev-acts .ldev-tool { gap: 5px; font: 400 11.5px/1 var(--sans, sans-serif); white-space: nowrap; padding: 0 4px; }
.ldev-order { height: 32px; }
.ldev-order .lib-seg-btn, .ldev-devseg .lib-seg-btn { display: inline-flex; align-items: center; justify-content: center; }
.ldev-order .lib-seg-btn { padding: 0 9px; }
.ldev-order .lib-seg-btn:disabled { opacity: .35; cursor: not-allowed; }
.ldev-devseg, .ldev-finsw, .ldev-capbtns { margin-left: auto; }
.ldev-devseg .lib-seg-btn { padding: 5px 9px; }
.ldev-capbtns { display: inline-flex; gap: 4px; }
.ldev-capbtns .pill { padding: 4px 10px; }
.lplace-line .option-label.lplace-name { white-space: normal; overflow-wrap: anywhere; line-height: 1.3; }   /* a capture's name on a Studio board, whole (18 Sep 2026) */
.ldev-capname { margin: 4px 0 2px; font: 400 11.5px/1.4 var(--sans, sans-serif); color: var(--ink50, rgba(14, 14, 12, .5)); }
.ldev-tgwrap .ldev-poserow .ldev-posesel + .ldev-ptadd { margin-left: 6px; }   /* the + beside the poses it adds to (22 Sep 2026) */
#previewPanel .ldev-tgwrap .ldev-ptadd { width: 28px; height: 28px; border-radius: 8px; }
.ldev-tool { flex: 1; min-width: 0; display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 0; border: 1px solid var(--bmed); border-radius: 7px; background: transparent; color: var(--ink); cursor: pointer; }
.ldev-tool:hover { background: var(--btn2bg-hover); }
.ldev-tool:disabled { opacity: .35; cursor: not-allowed; }
/* the shots (17 Sep 2026): the rail's first group on a film, a line each (22 Sep 2026) */
.ldev-shotlist { padding-top: 2px; }
.ldev-shotrow { border-bottom: 1px solid var(--ink08); }
.ldev-shotrow:last-of-type { border-bottom: 0; }
.ldev-shotline { display: flex; align-items: center; gap: 9px; width: 100%; min-height: 42px; padding: 6px 4px; border: 0; background: transparent; color: var(--ink); font: inherit; text-align: left; cursor: pointer; border-radius: 8px; }
.ldev-shotline:hover { background: var(--pill, rgba(14, 14, 12, .04)); }
.ldev-shotrow.is-sel .ldev-shotline { background: var(--ink08, rgba(14, 14, 12, .05)); }
.ldev-shotno { flex: none; width: 16px; font: 500 10.5px/1 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink50); text-align: right; }
.ldev-shotpic { flex: none; width: 30px; height: 22px; border-radius: 3px; overflow: hidden; background: #E8E6E0; box-shadow: 0 0 0 1px var(--ink08, rgba(14, 14, 12, .1)); }
.ldev-shotpic img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
.ldev-shotname { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 12.5px; }
.ldev-shotmeta { flex: none; font: 400 10.5px/1 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink50); min-width: 30px; text-align: right; }
.ldev-shotbody { padding: 4px 2px 12px; }
.ldev-shotsub { display: flex; align-items: center; font: 400 10.5px/1 var(--mono, ui-monospace, Menlo, monospace); letter-spacing: .12em; text-transform: uppercase; color: var(--ink50); margin: 6px 0 4px; }
.ldev-shotbody .ldev-shotgrp { margin-top: 6px; }
.ldev-shotlist .lplace-addrow { margin-top: 8px; }
/* the film playing: the row under the playhead lights, the dials read rather than edit (17 Sep 2026) */
.ldev-shotrow.is-now .ldev-shotno { color: var(--honey, #F7CE46); font-weight: 600; }
.ldev-shotrow.is-now .ldev-shotname { font-weight: 500; }
.ldev-shotbody.is-live .opt-slider { opacity: .45; }
.ldev-shotbody.is-live .opt-slider-val { color: var(--ink); }
.ldev-x { width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--bmed); background: transparent; color: var(--ink65, rgba(14,14,12,.65)); font: 400 15px/1 var(--sans, sans-serif); cursor: pointer; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.ldev-x:hover { color: var(--ink); background: var(--btn2bg-hover); }
.ldev-caps { gap: 5px; }
.ldev-cap { width: 34px; height: 26px; padding: 2px; border: 1px solid var(--bmed); border-radius: 5px; background: var(--paper); cursor: pointer; overflow: hidden; }
.ldev-cap img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; border-radius: 3px; }
.ldev-cap.is-selected { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
.ldev-card-x { position: absolute; right: 4px; top: 4px; width: 18px; height: 18px; border-radius: 50%; border: 0; background: var(--ink); color: var(--paper, #fff); font: 400 12px/18px var(--sans, sans-serif); text-align: center; padding: 0; cursor: pointer; z-index: 2; }
.ldev-tl-film i { font-style: normal; text-transform: none; letter-spacing: 0; color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-tl-more { flex: none; width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--bmed); background: transparent; color: var(--ink65, rgba(14,14,12,.65)); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; padding: 0; }
.ldev-tl-more:hover { color: var(--ink); background: var(--btn2bg-hover); }
.ldev-join-pop.is-below { transform: translate(-50%, 0); }
.ldev-menu { display: flex; flex-direction: column; min-width: 190px; }
.ldev-menu button { text-align: left; padding: 7px 10px; border: 0; border-radius: 6px; background: transparent; color: var(--ink); font: 400 12.5px/1.3 var(--sans, sans-serif); cursor: pointer; }
.ldev-menu button:hover { background: var(--btn2bg-hover); }
.ldev-menu button:disabled { opacity: .4; cursor: not-allowed; }
.ldev-menu button:nth-child(3) { margin-top: 5px; border-top: 1px solid var(--bmed); border-radius: 0 0 6px 6px; padding-top: 10px; }
.ldev-pieces { display: grid; grid-template-columns: repeat(4, 96px); gap: 6px; }
.ldev-pieces button { text-align: center; padding: 8px 6px 7px; border: 1px solid var(--bmed); border-radius: 8px; background: transparent; color: var(--ink); cursor: pointer; font-family: var(--sans, sans-serif); }
.ldev-pieces button:hover { background: var(--btn2bg-hover); }
.ldev-pieces b { display: block; font-size: 12.5px; font-weight: 500; margin-bottom: 2px; }
.ldev-pieces span { display: block; font-size: 11px; line-height: 1.35; color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-poserow { display: inline-flex; align-items: center; gap: 6px; }
.ldev-link { width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--bmed); background: transparent; color: var(--ink50, rgba(14,14,12,.5)); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; padding: 0; }
.ldev-link.is-on { background: var(--ink); border-color: var(--ink); color: var(--paper, #fff); }
/* the join marks sit on the ruler, between the blocks (15 Sep 2026, Mark: "the grab and the transition"); the cards' edges are the grips' alone */
/* the film strip lays its own gaps (15 Sep 2026): a cut is a 24px gap with its mark in it, a Move a card of its own */
.ldev-tl .reel-strip { gap: 0; }
.ldev-tl .reel-strip .reel-add { margin-left: 8px; }
/* THE JOINS, ONE FAMILY (18 Sep 2026, Mark: "I think just in the timeline. make the options look the same so option 3"): a Cut, a
   Move and a Continue are the same box between two cards, the mark alone since 21 Sep 2026 (Mark: "just have the icons"), the
   ruler's block above saying which it is and how long; a Move keeps the width of the time it borrows from the shots either side. */
.ldev-tl .reel-strip .ldev-join { position: static; flex: none; transform: none; align-self: stretch; width: auto; height: auto; margin: 0 8px; padding: 0 9px; gap: 0; justify-content: center; border-radius: 8px; white-space: nowrap; overflow: hidden; background: var(--paper, #fff); }   /* a box like the shots, not a pill (20 Sep 2026, Mark: "can the move, cut, continue not be pills and just rounded corner boxes like the actual shots/blocks?") */
.ldev-tl .reel-strip .ldev-join svg { flex: none; }
.ldev-tl .reel-strip .ldev-join.is-cont { border-color: var(--bmed, rgba(14,14,12,.14)); color: var(--ink65, rgba(14,14,12,.65)); }   /* one weight for all three: the mark says which it is */
.ldev-tl .reel-strip .ldev-join.is-cont:hover { border-color: var(--ink); color: var(--ink); }
.ldev-tl .reel-strip .ldev-join.t-mid i { display: none; }
.ldev-tl .reel-strip .ldev-join.t-thin span { display: none; }
.ldev-tl .reel-strip .ldev-join.t-thin { padding: 0 6px; }
.ldev-tl .reel-strip .ldev-loopgap { flex: none; align-self: stretch; }   /* a seamless loop's time, under the ruler's Loop block (19 Sep 2026) */
.opt-toggle-row[data-llooplen][hidden] { display: none; }   /* its Length only while it is on: the row's own display beats the hidden attribute */
.ldev-moveseg { position: absolute; top: 0; bottom: 0; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 0 8px; border: 1px dashed var(--bmed); border-radius: 4px; font: 500 10px/1 var(--sans, sans-serif); color: var(--ink50, rgba(14,14,12,.5)); pointer-events: none; overflow: hidden; white-space: nowrap; }
.ldev-moveseg i { font: 400 10px/1 var(--mono, monospace); font-style: normal; }
/* THE TARGET (15 Sep 2026): the shot's capture in the rail, whole, with the box each end frames drawn on it; drag to draw,
   move or resize one. Start in the marker amber, End in ink; the end being edited solid and named, the other dashed. */
/* THE CAPTURE IS THE PICTURE YOU DRAW ON (18 Sep 2026, Mark: "the capture thumbnail and the change button etc should be the
   thumbnail where you draw on"): Change and Remove ride the picture's top corner rather than a row of their own above it. */
.ldev-tgbox { position: relative; }
.ldev-tgtools { position: absolute; top: 6px; right: 6px; display: flex; gap: 4px; z-index: 3; opacity: .55; transition: opacity .12s ease; }
.ldev-tgbox:hover .ldev-tgtools, .ldev-tgtools:focus-within { opacity: 1; }
.ldev-tgtools .lplace-ico { background: var(--paper); border: 1px solid var(--bmed); box-shadow: 0 1px 3px rgba(17,17,17,.12); }
.ldev-tgadd { width: 100%; }
/* a capture no taller than a phone screen sits whole, centred, no taller than 260px (17 Sep 2026, Mark: "only long ones have this.
   it was fine before for phone and less") */
.ldev-target { max-height: 260px; display: flex; justify-content: center; border: 1px solid var(--bmed); border-radius: 6px; background: var(--paper); margin: 4px 0 2px; overflow: hidden; }
.ldev-target-in { position: relative; display: block; width: 100%; min-width: 0; touch-action: none; cursor: crosshair; user-select: none; -webkit-user-select: none; }   /* its width is set in px from the capture's shape */
.ldev-target-in img { display: block; width: 100%; height: auto; max-height: none; pointer-events: none; }
/* a longer capture at the rail's width in a window that scrolls (17 Sep 2026, Mark: "when captures are really long its too narrow in the rail") */
.ldev-target.is-long { height: 260px; display: block; overflow-x: hidden; overflow-y: auto; scrollbar-width: thin; }
/* the room the picture holds round the chosen box, dashed, on the capture in fixed colours (22 Sep 2026, the frame box mockup, C); the
   picture has room above and below it for the line to reach past a box as tall as the page */
.ldev-troom { position: absolute; box-sizing: border-box; border: 1px dashed rgba(14,14,12,.5); box-shadow: 0 0 0 1px rgba(255,255,255,.5); pointer-events: none; }
.ldev-target:not(.is-long) { padding: 14px 0; max-height: 288px; }
.ldev-target.is-long .ldev-target-in { width: 100% !important; }
.ldev-tsel { position: absolute; box-sizing: border-box; border: 1px dashed #BA7517; pointer-events: none; }
.ldev-tsel.is-end { border-color: #0E0E0C; }   /* drawn on the capture, which is the same in either theme (21 Sep 2026, the dark pass) */
.ldev-tsel { --c: #BA7517; }
.ldev-tsel.is-end { --c: #0E0E0C; }
.ldev-tsel i { display: none; }
/* every box wears its name, Start and the points at its left and End at its right, the chosen one filled; a name is a press that
   chooses its pose (22 Sep 2026, the Start and End mockup) */
.ldev-tsel b { display: block; position: absolute; top: -1.5px; left: 8px; z-index: 1; padding: 2px 5px 3px; border-radius: 0 0 3px 3px; font: 500 10px/1 var(--sans, sans-serif); white-space: nowrap; background: #fff; color: var(--c); box-shadow: inset 0 0 0 1px var(--c); pointer-events: auto; cursor: pointer; }
.ldev-tsel[data-tsel^="m"] b { left: auto; right: 8px; }   /* the points at the top right, */
.ldev-tsel.is-end b { left: auto; right: 8px; top: auto; bottom: -1.5px; border-radius: 3px 3px 0 0; }   /* and End at the foot, so boxes that stand on each other keep every name */
.ldev-tsel.is-now { border-style: solid; border-width: 1.5px; box-shadow: 0 0 0 1px rgba(255, 255, 255, .55), inset 0 0 0 1px rgba(255, 255, 255, .55); }
.ldev-tsel.is-now b { background: var(--c); color: #fff; box-shadow: none; cursor: default; }
.ldev-tsel.is-now i { display: block; position: absolute; width: 7px; height: 7px; background: #fff; border: 1.5px solid #BA7517; border-radius: 1px; }
.ldev-tsel.is-end.is-now i { border-color: #0E0E0C; }
/* a box the pose has not drawn yet: dashed even while chosen, so it reads as an offer (21 Sep 2026) */
.ldev-tsel.is-default, .ldev-tsel.is-now.is-default { border-style: dashed; }
.ldev-tsel i.nw { left: -4px; top: -4px; } .ldev-tsel i.ne { right: -4px; top: -4px; } .ldev-tsel i.sw { left: -4px; bottom: -4px; } .ldev-tsel i.se { right: -4px; bottom: -4px; }
.ldev-ptadd { width: 22px; height: 22px; padding: 0; font: 400 14px/1 var(--sans, sans-serif); flex: 0 0 auto; }
.ldev-ptadd.is-idle { visibility: hidden; pointer-events: none; }   /* the −'s room, kept while it has nothing to take (21 Sep 2026) */
/* the pose and the picture stick to the top of the rail while the dials scroll beneath (15 Sep 2026) */
.ldev-tgwrap { margin: -6px -2px 8px; padding: 8px 2px 8px; }   /* no longer sticky (17 Sep 2026, Mark: "lets remove the sticky for the thumbnail") */
.ldev-tgwrap .ldev-poserow { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ldev-tgwrap .ldev-poserow .pill-select { flex: 1 1 auto; min-width: 0; }
.pill.is-derived { opacity: .6; }
.pill.is-cont { opacity: .6; }   /* a Start that follows the shot before (18 Sep 2026) */
.pill.is-cont::before { content: ''; display: inline-block; width: 7px; height: 1px; background: currentColor; margin-right: 5px; vertical-align: middle; opacity: .8; }
.pill.is-derived::after { content: ''; display: inline-block; width: 4px; height: 4px; border-radius: 50%; background: currentColor; margin-left: 5px; vertical-align: middle; opacity: .7; }
/* Done, not an × (15 Sep 2026, Mark: "we need a done button instead of x on the shot accordion"): closing the shot is finishing with it, not dismissing it */
.ldev-done { height: 30px; padding: 0 13px; border-radius: 8px; border: 0; background: var(--ink); color: var(--paper, #FBFAF8); font: 500 12.5px/1 var(--sans, sans-serif); cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }   /* the way out of a shot, said plainly (20 Sep 2026, Mark: "the 'done' button on a shot in the rail shouldnt be a pill. need to be more obvious") */
.ldev-done:hover { background: #2C2C29; color: var(--paper, #FBFAF8); }
/* THE LAYERS GROUP AS A BAND (15 Sep 2026, Mark: "the layers 1 of ... is out of place now. should this be in its own grey accordion?"):
   the same face as Shot, Scene settings, Style and Export, to the pixel: the same padding, type, chevron and turn (16 Sep 2026,
   Mark: "The layers accordion is not styled correctly") */
.layouts-grp > .layouts-grp-head.lplace-head, .layouts-rail-name + .layouts-grp > .layouts-grp-head.lplace-head { display: flex; align-items: center; padding: 10px 12px; margin: 6px 0 0; background: var(--acc-band); border-radius: 8px; font-family: var(--sans); font-size: 13px; font-weight: 500; line-height: 1.35; letter-spacing: 0; text-transform: none; color: var(--ink); }
.layouts-grp-head.lplace-head:hover { background: var(--acc-band-hover); }
.layouts-grp-head.lplace-head .lplace-fold { font: inherit; line-height: inherit; letter-spacing: inherit; color: inherit; display: flex; align-items: center; gap: 8px; width: 100%; }
.lplace-head .lplace-cnt { font-weight: 400; color: var(--ink50, rgba(14, 14, 12, .5)); }
.lplace-head .option-chevron { display: none; }
.layouts-grp-head.lplace-head .lplace-fold::after { content: ''; width: 6px; height: 6px; margin-left: auto; margin-right: 2px; border-right: 1.2px solid var(--ink50); border-bottom: 1.2px solid var(--ink50); transform: rotate(225deg); transition: transform .15s ease; }
.layouts-grp.is-folded .layouts-grp-head.lplace-head .lplace-fold::after { transform: rotate(45deg); }
.lplace-rows { padding: 0 2px; }
.lplace-rows > .lplace-addrow { margin-bottom: 18px; }   /* room under + Add before the next band */
/* each piece drawn: the frame, where it starts and where it travels (15 Sep 2026) */
.ldev-piece-art { display: block; color: var(--ink); opacity: .8; margin: 0 auto 4px; }
.ldev-pieces button:hover .ldev-piece-art { opacity: 1; }
.ldev-pieces b { margin-bottom: 0; }
/* the Layers band above lost its top padding to the rail's own rule for the first group (`.layouts-rail-name + .layouts-grp > .layouts-grp-head`), which outranked it; the selector above now outranks that (16 Sep 2026, Mark: "layers not same padding") */
/* THE LIGHT BAND (16 Sep 2026): the scenes as pictures of the device on its lit surface with the shadow each throws, and the
   kinds of shadow drawn on their own; the chosen one carries the ink outline the capture thumbnails use */
.ldev-scenes { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin: 6px 0 2px; }   /* four scenes in one row (16 Sep 2026) */
.ldev-scene { padding: 3px 3px 4px; border: 1px solid var(--bmed); border-radius: 7px; background: var(--paper); cursor: pointer; text-align: center; font: 500 10.5px/1.3 var(--sans, sans-serif); color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-scene svg { display: block; width: 100%; height: auto; border-radius: 4px; margin-bottom: 3px; }
.ldev-scene:hover { border-color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-scene.is-selected { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); color: var(--ink); }
.ldev-kinds { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin: 6px 0 4px; }
.ldev-kind { padding: 4px 3px 4px; border: 1px solid var(--bmed); border-radius: 7px; background: var(--paper); cursor: pointer; text-align: center; font: 500 10.5px/1.3 var(--sans, sans-serif); color: var(--ink65, rgba(14,14,12,.65)); }
.ldev-kind svg { display: block; width: 100%; height: auto; margin-bottom: 2px; }
.ldev-kind:hover { border-color: var(--ink50, rgba(14,14,12,.5)); }
.ldev-kind.is-selected { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); color: var(--ink); }
/* STUDIO'S DOOR (17 Sep 2026): the Layouts page with two cards, Showreel and Film, each opening the Layouts detail where the
   device and the motion are chosen. The pictures are drawn here, a phone with its queue and a tilted phone with a target. */
.sgal .lay-gal-bar { padding-bottom: 12px; margin-bottom: 26px; }
.sgal-kinds { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; }
.sgal-kind { background: #fff; border: 1px solid var(--ink08); border-radius: 14px; overflow: hidden; padding: 0; text-align: left; cursor: pointer; color: var(--ink); font: inherit; transition: transform .15s ease, box-shadow .15s ease; }
.sgal-kind:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0, 0, 0, .10); }
.app.dark .sgal-kind { background: rgba(255, 255, 255, .04); }
.sgal-pic { height: 200px; background: #E8E6E0; position: relative; overflow: hidden; }
.app.dark .sgal-pic { background: rgba(255, 255, 255, .06); }
.sgal-pic i { position: absolute; display: block; }
.sgal-floor { left: 0; right: 0; bottom: 0; height: 26%; background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .05)); }
.sgal-ph { left: 50%; bottom: 20%; width: 74px; height: 150px; border-radius: 14px; background: #1a1a1c; transform: translateX(-50%); box-shadow: 0 30px 40px -22px rgba(0, 0, 0, .55); }
.sgal-ph::after { content: ''; position: absolute; left: 5px; right: 5px; top: 5px; bottom: 5px; border-radius: 10px; background: #f4f3f0; }
.sgal-q { left: calc(50% + 56px); top: 34px; width: 120px; height: 10px; border-radius: 3px; background: rgba(14, 14, 12, .28); }
.sgal-q:nth-of-type(4) { top: 56px; width: 96px; background: rgba(14, 14, 12, .2); }
.sgal-q:nth-of-type(5) { top: 78px; width: 108px; background: rgba(14, 14, 12, .13); }
.sgal-q:nth-of-type(6) { top: 100px; width: 80px; background: rgba(14, 14, 12, .08); }
.sgal-phf { left: 38%; top: -30px; width: 190px; height: 390px; border-radius: 30px; background: #1a1a1c; transform: rotate(-16deg); }
.sgal-phf::after { content: ''; position: absolute; left: 11px; right: 11px; top: 11px; bottom: 11px; border-radius: 22px; background: #f4f3f0; }
.sgal-tg { left: 44%; top: 56px; width: 96px; height: 62px; border: 1.5px solid var(--honey, #F7CE46); border-radius: 4px; transform: rotate(-16deg); }
.sgal-tg::before, .sgal-tg::after { content: ''; position: absolute; width: 7px; height: 7px; background: var(--honey, #F7CE46); }
.sgal-tg::before { left: -1.5px; top: -1.5px; } .sgal-tg::after { right: -1.5px; bottom: -1.5px; }
.sgal-txt { padding: 18px 20px 20px; }
.sgal-txt b { display: block; font-size: 17px; font-weight: 500; margin-bottom: 4px; }
.sgal-txt p { margin: 0; color: var(--ink65); font-size: 13px; line-height: 1.5; max-width: 480px; }
.sgal-go { display: inline-flex; align-items: center; gap: 4px; margin-top: 14px; font-size: 13px; font-weight: 500; }
.sgal-go svg { width: 13px; height: 13px; }
@media (max-width: 1100px) { .sgal-kinds { grid-template-columns: 1fr; } }
/* the detail's Device row (Studio): glyph and name on each chip */
.ld-devs .ld-anim { display: inline-flex; align-items: center; gap: 6px; }
.ld-devs .ld-anim svg { width: 14px; height: 14px; }
.ld-devs { margin-bottom: 14px; }
/* the rail's Devices band: the Layers band's dress, one row, the device's menu on the row */
.sdev-line { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 34px; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; padding: 0; }
.sdev-glyph { display: inline-flex; width: 18px; justify-content: center; color: var(--ink); }
.sdev-glyph svg { width: 16px; height: 16px; }
.sdev-dot { width: 9px; height: 9px; border-radius: 50%; border: 1px solid rgba(14, 14, 12, .2); margin-left: auto; flex: none; }
.sdev-meta { font: 400 10.5px/1 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink50); white-space: nowrap; }
.sdev-line .option-chevron { flex: none; color: var(--ink50); }
.sdev-row { display: flex; align-items: center; gap: 6px; }
.sdev-row .sdev-line { flex: 1; min-width: 0; min-height: 44px; }
.sdev-sub { display: block; font: 400 10.5px/1.2 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink50); margin-top: 2px; }
.ldev-shotcap .ldev-cap.is-still { width: 44px; height: 34px; cursor: default; flex: none; }
.ldev-shotcap .ldev-cap.is-still img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
.ldev-shotcap .lplace-do { margin-left: 8px; }
.ldev-shotcap .lplace-addrow.is-inline { max-width: 58%; }
.ldev-fins .pill { display: inline-flex; align-items: center; padding: 6px 8px; }
.ldev-fins .sdev-dot { width: 11px; height: 11px; margin: 0; }
.ldev-cap-add { display: inline-flex; align-items: center; justify-content: center; font-size: 16px; font-weight: 300; line-height: 1; color: var(--ink); border-style: dashed; }
/* five devices on one line (18 Sep 2026, Mark: "this is messy"): the row shares its width out rather than wrapping the last one
   onto a line of its own */
.ldev-devs { display: flex; flex: 1; min-width: 0; }
.ldev-devs .pill { display: inline-flex; align-items: center; justify-content: center; flex: 1; min-width: 0; padding: 4px 2px; }
.ldev-devs .pill svg { width: 15px; height: 15px; }
.sdev-menu { display: flex; flex-direction: column; min-width: 200px; }
.sdev-menu button { display: flex; align-items: center; gap: 9px; text-align: left; padding: 7px 10px; border: 0; border-radius: 6px; background: transparent; color: var(--ink); font: 400 12.5px/1.3 var(--sans, sans-serif); cursor: pointer; }
.sdev-menu button svg { width: 16px; height: 16px; flex: none; }
.sdev-menu button em { margin-left: auto; font: 400 10px/1 var(--mono, ui-monospace, Menlo, monospace); color: var(--ink50); font-style: normal; }
.sdev-menu button:hover { background: var(--btn2bg-hover); }
.sdev-menu button.is-on { font-weight: 500; background: var(--ink08); }
.sdev-menu .sdev-sep { font: 400 9.5px/1 var(--mono, ui-monospace, Menlo, monospace); letter-spacing: .14em; text-transform: uppercase; color: var(--ink50); padding: 10px 10px 4px; margin-top: 4px; border-top: 1px solid var(--ink08); }
.sdev-menu .sdev-dot { margin-left: 1px; margin-right: 6px; width: 10px; height: 10px; }
