/* crumb/crumb.css — the guided-tour popover. Include ONLY when a page runs CRUMB tours; the
   lamp visuals come from grain/ai/ai.css (CRUMB reuses that primitive). Tokens only (grain
   non-negotiable) — the card reads the theme's paper/ink/border/space slots, so it re-skins with
   the rest of the design system and never hardcodes a color. */

/* A NON-MODAL <dialog> (dialog.show()) so the lit surface stays clickable through the passthrough
   lamp — so we own its box (native modal centering/backdrop don't apply). It sits above the lamp
   veil (--z-ai-veil 9000) and the acting label (--z-ai-surface 9002). JS sets left/top. */
.crumb-pop {
  position: fixed; margin: 0; inset: auto; z-index: 9100;
  max-width: min(24rem, calc(100vw - 24px));
  /* The card is BOUNDED and its body scrolls (added 2026-08-11). It had no max-height at all, so a
     card taller than the viewport ran off the bottom taking Back and Finish with it. This is only
     the floor: the real budget depends on where the card's top lands, so placeCard overrides this
     inline with the space actually below it. Measured, not assumed — 100dvh alone did not fix it,
     because a card can start low and still overflow while being shorter than the screen. */
  max-height: calc(100dvh - 24px);
  padding: 0; border: var(--border); border-radius: var(--radius-md);
  background: var(--paper); color: var(--ink);
  box-shadow: 0 8px 30px color-mix(in srgb, var(--ink) 24%, transparent);
  font-family: var(--font-smooth);
}
/* head and foot stay put, the body takes the scroll — the reviewer can always reach Finish. */
.crumb-pop[open] { display: flex; flex-direction: column; }
.crumb-pop__head, .crumb-pop__foot { flex: 0 0 auto; }
.crumb-pop:not([open]) { display: none; }
@starting-style { .crumb-pop[open] { opacity: 0; transform: translateY(4px); } }
.crumb-pop { transition: opacity 0.16s ease, transform 0.16s ease; }

.crumb-pop__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-3) var(--space-2);
  border-bottom: var(--border);
}
/* The title takes the slack and is the only thing allowed to. Everything after it is a fixed badge
   that must never break across lines: "2 / 4" wrapping to "2 /" and "4" reads as a rendering fault
   and costs the reviewer a beat working out it is not one. min-width:0 lets the title shrink instead
   of shoving the badges into a wrap. */
.crumb-pop__title { font-weight: 600; font-size: var(--text-sm); margin-right: auto; min-width: 0; }
.crumb-pop__count {
  font-size: var(--text-xs); color: var(--ink-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap; flex: 0 0 auto;
}
.crumb-pop__status { white-space: nowrap; flex: 0 0 auto; }
.crumb-pop__x {
  appearance: none; border: 0; background: transparent; color: inherit; cursor: pointer;
  font-size: var(--text-lg); line-height: 1; padding: 0 var(--space-1); opacity: 0.6;
}
.crumb-pop__x:hover { opacity: 1; }

/* min-height:0 is the load-bearing half: a flex child's automatic minimum is its content size, so
   without it the body refuses to shrink and max-height above buys nothing. */
.crumb-pop__body {
  padding: var(--space-3); display: grid; gap: var(--space-2);
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
}
.crumb-pop__say { margin: 0; font-size: var(--text-sm); line-height: 1.5; }
.crumb-pop__review {
  margin: 0; font-size: var(--text-sm); line-height: 1.5;
  padding: var(--space-2); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  border-left: 3px solid var(--color-accent);
}
.crumb-pop__verify {
  margin: 0; font-size: var(--text-xs); line-height: 1.45;
  color: var(--ink-muted);
}
/* the prefill honesty marker (stagedNote in crumb-live.js) — italic to read as a system note about
   the field's state, distinct from the tour's own review/verify prose sitting beside it. */
.crumb-pop__staged {
  margin: 0; font-size: var(--text-xs); line-height: 1.45; font-style: italic;
  color: var(--ink-muted);
}

.crumb-pop__foot {
  display: flex; justify-content: flex-end; gap: var(--space-2);
  padding: var(--space-2) var(--space-3) var(--space-3);
}

/* verification-status chip — CRUMB's own review vocab, deliberately NOT grade.
   These two used to read `var(--ok, green)` and `var(--warn, orange)`. Neither token was defined
   anywhere in grain or crumb, so the fallbacks always fired and a literal green and orange were
   the only hues reaching the screen in a palette that has none. Settled 2026-08-11 by the same
   walked review that settled the button (plans/grain-token-debt.md, tour review-grain-status):
   the chips follow the button's answer, so the difference is weight, never hue. verified goes
   denser in the ink ramp because it is settled; known-issue takes the doubled rule, which is the
   exact vocabulary the destructive button now carries — one thing to learn, used twice. */
.crumb-pop__status {
  font-size: var(--text-xs); text-transform: lowercase; letter-spacing: 0.01em;
  padding: 2px var(--space-2); border-radius: var(--radius-pill);
  border: var(--border); color: var(--ink);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}
.crumb-pop__status[data-status="verified"] { background: color-mix(in srgb, var(--ink) 14%, transparent); }
.crumb-pop__status[data-status="known-issue"] { border-width: 2px; }
.crumb-pop__status[data-status="changed"],
.crumb-pop__status[data-status="new"] { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }

@media (prefers-reduced-motion: reduce) {
  .crumb-pop { transition: none; }
}

/* ============================================================================
   THE FRAME (B3) — a routed app-shell VARIANT: a fixed top-bar + a combined
   nav/content sidebar + a bordered viewport. NOT an iframe (PLAN.md); the host
   page is the real page, read as "framed" via a border edge. The host SHRINKS
   into the framed viewport (the body rule below) — the chrome wraps AROUND the
   site, it never covers it. One sidebar component, `data-mode="demo|dev"`
   flipped live. Tokens only.
   Sits above grain's lamp veil (--z-ai-veil 9000) so the chrome is clickable
   while the lit surface stays reachable through the passthrough hole. */

/* the shrink: while the frame is up, the body becomes a fixed box inset by the
   bar + sidebar, so the whole site lives INSIDE the framed viewport instead of
   running underneath the chrome. The chrome vars live HERE (not on .crumb-frame)
   because the body needs them for its own inset and the frame root — a body
   child — inherits them; one definition, no drift. `height/width: auto` beat a
   host body that sizes itself (e.g. grain's .app-window-backdrop height:100dvh);
   a shell that fills the body (height:100%) shrinks with it for free. The frame
   chrome stays position:fixed and the body carries NO transform, so fixed
   descendants (grain's lamp veil, lightboxes) keep real viewport coordinates. */
body[data-crumb-frame] {
  --crumb-bar-h: 3rem;
  --crumb-side-w: min(20rem, 82vw);
  position: fixed;
  inset: var(--crumb-bar-h) var(--crumb-side-w) 0 0;
  height: auto; width: auto;
  overflow: hidden;
}
.crumb-frame {
  font-family: var(--font-smooth);
}

/* the bordered viewport edge — a pure visual frame around the SHRUNK host box
   (the same inset the body rule above uses, so it hugs the site exactly).
   pointer-events:none so it never eats a click meant for a lit surface. */
.crumb-frame__edge {
  position: fixed; z-index: 9110; pointer-events: none;
  inset: var(--crumb-bar-h) var(--crumb-side-w) 0 0;
  border: 2px solid color-mix(in srgb, var(--color-accent) 55%, transparent);
  border-top: 0;
}

/* top bar */
.crumb-frame__bar {
  position: fixed; z-index: 9120; inset: 0 0 auto 0; height: var(--crumb-bar-h);
  display: flex; align-items: center; gap: var(--space-3);
  padding: 0 var(--space-3);
  background: var(--paper); color: var(--ink);
  border-bottom: var(--border);
  box-shadow: 0 2px 12px color-mix(in srgb, var(--ink) 14%, transparent);
}
.crumb-frame__title { font-weight: 600; font-size: var(--text-sm); }
.crumb-frame__count {
  font-size: var(--text-xs); font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}
.crumb-frame__x { margin-left: auto; }

/* the demo|dev toggle — a segmented control; the SAME step re-renders on flip. */
.crumb-frame__modes {
  display: inline-flex; gap: 2px; padding: 2px;
  border: var(--border); border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
}
.crumb-frame__mode {
  appearance: none; border: 0; cursor: pointer;
  font: inherit; font-size: var(--text-xs); color: var(--ink);
  padding: 3px var(--space-3); border-radius: var(--radius-pill);
  background: transparent; opacity: 0.7;
}
.crumb-frame__mode[aria-selected="true"] {
  background: var(--paper); opacity: 1; font-weight: 600;
  box-shadow: 0 1px 4px color-mix(in srgb, var(--ink) 16%, transparent);
}

/* the combined nav/content sidebar */
.crumb-sidebar {
  position: fixed; z-index: 9120; inset: var(--crumb-bar-h) 0 0 auto;
  width: var(--crumb-side-w);
  display: flex; flex-direction: column;
  background: var(--paper); color: var(--ink);
  border-left: var(--border);
  box-shadow: -4px 0 24px color-mix(in srgb, var(--ink) 16%, transparent);
}

/* nav rail — the ordered steps (jump-to) */
.crumb-sidebar__nav { overflow-y: auto; border-bottom: var(--border); flex: 0 1 auto; }
.crumb-sidebar__list { list-style: none; margin: 0; padding: var(--space-2); display: grid; gap: 2px; }
.crumb-sidebar__goto {
  appearance: none; border: 0; background: transparent; color: inherit; cursor: pointer;
  width: 100%; text-align: left; font: inherit; font-size: var(--text-sm);
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2); border-radius: var(--radius-sm);
}
.crumb-sidebar__goto:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); }
.crumb-sidebar__num {
  flex: 0 0 auto; width: 1.5rem; height: 1.5rem; display: grid; place-items: center;
  font-size: var(--text-xs); font-variant-numeric: tabular-nums;
  border: var(--border); border-radius: var(--radius-pill);
  color: var(--ink-muted);
}
.crumb-sidebar__label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crumb-sidebar__step[data-visited] .crumb-sidebar__num {
  background: color-mix(in srgb, var(--ink) 10%, transparent); color: var(--ink);
}
.crumb-sidebar__step[data-current] .crumb-sidebar__goto {
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
}
.crumb-sidebar__step[data-current] .crumb-sidebar__num {
  background: var(--color-accent); color: var(--paper); border-color: transparent;
}
.crumb-sidebar__chip {
  flex: 0 0 auto; font-size: 0.66rem; text-transform: lowercase; letter-spacing: 0.01em;
  padding: 1px var(--space-2); border-radius: var(--radius-pill);
  border: var(--border); background: color-mix(in srgb, var(--ink) 6%, transparent);
}
/* the rail's chip — same vocabulary as .crumb-pop__status above, same reason. */
.crumb-sidebar__chip[data-status="verified"] { background: color-mix(in srgb, var(--ink) 14%, transparent); }
.crumb-sidebar__chip[data-status="known-issue"] { border-width: 2px; }
.crumb-sidebar__chip[data-status="changed"],
.crumb-sidebar__chip[data-status="new"] { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }

/* content pane — the current step's prose (+ dev review/verify) */
.crumb-sidebar__detail {
  flex: 1 1 auto; overflow-y: auto;
  padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2);
}
.crumb-sidebar__say { margin: 0; font-size: var(--text-sm); line-height: 1.55; }
.crumb-sidebar__review {
  margin: 0; font-size: var(--text-sm); line-height: 1.5;
  padding: var(--space-2); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  border-left: 3px solid var(--color-accent);
}
.crumb-sidebar__verify {
  margin: 0; font-size: var(--text-xs); line-height: 1.45;
  color: var(--ink-muted);
}
/* the prefill honesty marker — see .crumb-pop__staged; same treatment, framed presentation. */
.crumb-sidebar__staged {
  margin: 0; font-size: var(--text-xs); line-height: 1.45; font-style: italic;
  color: var(--ink-muted);
}
/* Pinned chrome, no longer a child of the scrolling pane (crumb-live.js renderFrame). It carries
   its own padding now that it does not inherit __detail's, and a rule so it reads as an edge
   rather than as prose that happens to be last. */
.crumb-sidebar__foot {
  flex: 0 0 auto;
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-top: var(--border);
  display: flex; justify-content: flex-end; gap: var(--space-2);
}

/* the prompt card: the one place a tour takes input. One rule set for both presentations, since the
   two prefixes render the same body (crumb-live.js promptBody). Tokenized like everything else, no
   primitives: the fields have to read as the host's fields, not as a widget dropped on top. */
.crumb-pop__ask, .crumb-sidebar__ask { display: grid; gap: var(--space-1); font-size: var(--text-xs); }
.crumb-pop__asklabel, .crumb-sidebar__asklabel {
  color: var(--ink-muted);
}
.crumb-pop__askinput, .crumb-sidebar__askinput,
.crumb-pop__composed, .crumb-sidebar__composed {
  width: 100%; box-sizing: border-box; resize: vertical;
  font: inherit; font-size: var(--text-sm); line-height: 1.45;
  padding: var(--space-2); border: var(--border); border-radius: var(--radius-sm);
  background: var(--paper, transparent); color: var(--ink);
}
/* the paste block, shut by default (crumb-live.js promptBody). It is the fallback for when Finish
   does not land, so it should be reachable and not tall: a summary keeps it one click away and
   keeps Finish in view. The label reuses __asklabel so it sits in the same voice as the asks. */
.crumb-pop__paste, .crumb-sidebar__paste { display: grid; gap: var(--space-1); font-size: var(--text-xs); }
.crumb-pop__paste > summary, .crumb-sidebar__paste > summary { cursor: pointer; }

/* the composed prompt is output, not input: it reads as a quote of what you are about to send */
.crumb-pop__composed, .crumb-sidebar__composed {
  font-family: var(--font-mono, monospace); font-size: var(--text-xs);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
}

/* a decision ask: a closed set as radios, so every option is READ rather than hidden behind a select.
   A fieldset carries its own margin and border in every browser, and this has to sit flush with the
   text asks above and below it, so both are cleared and the legend is re-hung as an ordinary label. */
.crumb-pop__decision, .crumb-sidebar__decision {
  margin: 0; padding: 0; border: 0;
}
.crumb-pop__decision > legend, .crumb-sidebar__decision > legend {
  padding: 0; margin-bottom: var(--space-1);
}
.crumb-pop__option, .crumb-sidebar__option {
  display: flex; align-items: baseline; gap: var(--space-2);
  padding: var(--space-1) 0; font-size: var(--text-sm); cursor: pointer;
}
.crumb-pop__option input, .crumb-sidebar__option input { margin: 0; accent-color: var(--color-accent); }
/* the escape's field only exists once "Something else" is picked; hidden is the client's own toggle */
.crumb-pop__decision > input[hidden], .crumb-sidebar__decision > input[hidden] { display: none; }
.crumb-pop__decision > input, .crumb-sidebar__decision > input { margin-top: var(--space-1); }

/* narrow screens: the sidebar becomes a bottom sheet; the host box + edge inset
   from the bottom instead of the side (same values on both, they stay glued) */
@media (max-width: 720px) {
  body[data-crumb-frame] { --crumb-side-w: 0px; inset: var(--crumb-bar-h) 0 45vh 0; }
  .crumb-frame__edge { inset: var(--crumb-bar-h) 0 45vh 0; }
  .crumb-sidebar {
    inset: auto 0 0 0; width: auto; height: 45vh;
    border-left: 0; border-top: var(--border);
    box-shadow: 0 -4px 24px color-mix(in srgb, var(--ink) 16%, transparent);
  }
}
