/* The three-pane shell (s34), its chrome and its responsive behaviour.
 * No colour literals — only tokens.
 */

/* The shell reflows on *available space*, not on device width.
 *
 * `rem` and `em` inside a `@media` query are always resolved against the
 * initial font size (16px) — never against the current root font size. So a
 * media-query breakpoint cannot see text zoom: at 1280px with a 32px root,
 * `max-width: 75rem` stayed false, the three-pane grid never collapsed, and
 * `document.scrollWidth` reached 2785 against a 1280 viewport. That fails
 * WCAG 1.4.4 and 1.4.10 and ARCHITECTURE §9.
 *
 * `em` inside a `@container` query *is* resolved against the query
 * container's own font size, so these breakpoints move with text zoom. The
 * numbers are the same ones the media queries used, which is why the 320px
 * layout behaves exactly as before.
 */
#app {
  container-name: leia-app;
  container-type: inline-size;
}

.shell {
  position: relative;
  z-index: var(--z-shell);
  display: grid;
  grid-template-columns: var(--left-w) minmax(0, 1fr) var(--right-w);
  gap: var(--pane-gap);
  height: 100vh;
  height: 100dvh;
  padding: var(--pane-gap);
}

.pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  border: var(--border-width) solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--pane);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
}
.pane:focus { outline: none; }

/* ---- left pane --------------------------------------------------- */

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-bottom: var(--border-width) solid var(--hairline);
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.85rem;
  height: 1.85rem;
  flex: none;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
}

.brand-text { display: grid; flex: 1; min-width: 0; }
.brand-name {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-display);
  line-height: 1.1;
}
.brand-tagline {
  font-size: var(--text-xs);
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.brand-actions { display: flex; gap: var(--space-1); flex: none; }

[data-chrome="minimal"] .brand-tagline { display: none; }

/* Eight sections, two at a time.
 *
 * As one column the nav was 319px of an 800px window — 41% of the left pane
 * spent on a list that never changes — and what it took the space from was the
 * section itself: the sidepane below it got 341px, and the default section's
 * only two buttons sat 160px past the bottom edge. The app's first screen had
 * no visible primary action.
 *
 * `auto-fit` rather than a fixed `1fr 1fr`: the rail drops back to one column
 * on its own when a cell would be too narrow to hold a label, so the layout is
 * decided by the space that exists, not by a breakpoint guess. */
.rail {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.75rem, 1fr));
  gap: 2px;
  padding: var(--space-2);
  border-bottom: var(--border-width) solid var(--hairline);
}

.rail-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-2);
  border: var(--border-width) solid transparent;
  border-radius: var(--control-radius);
  color: var(--muted);
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  text-decoration: none;
}
.rail-item > svg, .rail-item > .icon { flex: none; }
/* Two columns means a cell can be narrower than its longest label. An
   ellipsis is allowed; a label sliced through the middle of a word by the
   panel edge is not. `navigation.js` puts the full name on `title` and on
   `aria-label`, so nothing is lost to anybody. */
.rail-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rail-item:hover { color: var(--fg); background: var(--accent-ghost); }
.rail-item[aria-current="page"] {
  color: var(--accent-ink);
  background: var(--accent-soft);
  border-color: var(--accent-soft);
}

[data-chrome="minimal"] .rail { grid-template-columns: repeat(auto-fit, minmax(2.5rem, 1fr)); }
[data-chrome="minimal"] .rail-label { display: none; }
[data-chrome="minimal"] .rail-item { justify-content: center; padding: 0; }

.rail-horizontal {
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  overflow-x: auto;
  border-bottom: var(--border-width) solid var(--hairline);
}

/* The left column is short — ~340px at 1280x800 — and a section can easily be
   seven screens tall in it. With overlay scrollbars there was no mark on
   screen to say so, so a section's only button could sit half-cut by the panel
   edge and read as the end of the page. Two marks now say otherwise: a
   scrollbar that is always drawn, and a fade over whichever edge has more
   behind it. `navigation.js` sets the data attributes. */
.sidepane-wrap {
  position: relative;
  display: flex;
  flex: 1;
  min-width: 0;
  min-height: 0;
}

.sidepane-wrap::before,
.sidepane-wrap::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  height: var(--space-6);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-standard);
  z-index: 1;
}
.sidepane-wrap::before {
  top: 0;
  background: linear-gradient(to bottom, var(--pane), transparent);
}
.sidepane-wrap::after {
  bottom: 0;
  background: linear-gradient(to top, var(--pane), transparent);
}
.sidepane-wrap[data-overflow-start="true"]::before,
.sidepane-wrap[data-overflow-end="true"]::after { opacity: 1; }

/* `base.css` already zeroes every transition under `[data-animation="off"]`
   and `prefers-reduced-motion`, so the fade appears instantly there rather
   than not at all — the mark itself is information, not decoration. */

.sidepane {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  /* Reserve the scrollbar track so a section does not reflow by 10px the
     first time it is scrolled. Reserving it was never enough on its own —
     the platform's own bar is an overlay and stayed invisible until a scroll
     gesture had already begun — so there are now three marks, and a child has
     to notice only one of them: the bar drawn below, the fade on
     `.sidepane-wrap` above, and the breathing room at the foot of the pane. */
  scrollbar-gutter: stable;
  padding: var(--pane-pad);
  /* The last line of a section should end above the edge, not under it. */
  padding-block-end: var(--space-8);
  scroll-padding-block: var(--space-6);
  /* Section views size themselves against the rail they are in, not against
     the window, so they behave the same in the rail and in a sheet. */
  container-name: sidepane;
  container-type: inline-size;
}

/* Draw the bar ourselves.
 *
 * `scrollbar-gutter` above reserves the track, but on macOS the platform bar
 * is an *overlay*: it stays invisible until a scroll gesture is already under
 * way, `offsetWidth - clientHeight` reports 0, and there was nothing on screen
 * at all to say that most of a section was below the fold. Styling these
 * pseudo-elements opts this one scroller out of overlay behaviour, so the
 * thumb is always drawn and is always sized to how much is left.
 *
 * `scrollbar-color: auto` is the load-bearing line. `base.css` sets
 * `scrollbar-color` on `html`, and it is an *inherited* property — so every
 * scroller in the app was resolving it, and in Chrome any element with a
 * standard `scrollbar-color` / `scrollbar-width` has its `::-webkit-scrollbar`
 * rules ignored and falls straight back to the platform overlay bar. That is
 * why `.sidepane` measured `offsetWidth - clientWidth === 0` with all four
 * pseudo-element rules in force. Resetting the inherited value to `auto` hands
 * this scroller back to the pseudo-elements, and it measures 10px.
 * (`docs/handoffs/P18.md` asks base.css to stop setting it globally.)
 *
 * Engines without the pseudo-elements get the standard properties instead,
 * below. */
.sidepane { scrollbar-color: auto; }
.sidepane::-webkit-scrollbar { width: 10px; }
.sidepane::-webkit-scrollbar-track { background: transparent; }
.sidepane::-webkit-scrollbar-thumb {
  background: var(--border);
  background-clip: padding-box;
  border: 2px solid transparent;
  border-radius: var(--radius-lg);
}
.sidepane::-webkit-scrollbar-thumb:hover { background: var(--muted); background-clip: padding-box; }

@supports not selector(::-webkit-scrollbar) {
  .sidepane { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
}

/* The rail is the narrowest column in the product. Chrome that is generous
   in a card is wasteful here. */
@container sidepane (max-width: 22em) {
  .sidepane .empty-state { padding: var(--space-4) var(--space-3); gap: var(--space-2); }
  .sidepane .ui-head-actions { flex: 1 1 100%; justify-content: flex-start; }
  .sidepane .ui-head-actions > .btn { flex: 1 1 auto; }
  /* The decorative tile is `aria-hidden` and, at this width, 44px of chrome
     standing between the child and the button underneath it. In the `kids`
     theme — the largest control sizes of the four — that tile and the panel's
     restated description were the last things keeping Learn's two buttons off
     the first screen. The icon still identifies the section in the rail. */
  .sidepane .empty-art { display: none; }
  .sidepane .ui-head-desc { font-size: var(--text-sm); }
}

.view { display: flex; flex-direction: column; gap: var(--stack-gap); }

.left-foot {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-top: var(--border-width) solid var(--hairline);
}

.identity {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1;
  min-width: 0;
  min-height: var(--tap-min);
  padding: 0 var(--space-2);
  border: var(--border-width) solid transparent;
  border-radius: var(--control-radius);
  background: transparent;
  color: var(--fg);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  text-align: left;
}
.identity:hover { background: var(--accent-ghost); }
.identity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.guardian-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--tap-min);
  padding: 0 var(--space-2);
  border-radius: var(--control-radius);
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  white-space: nowrap;
}
.guardian-link:hover { color: var(--accent-ink); background: var(--accent-ghost); }

/* ---- centre pane: tab strips ------------------------------------- */

.tabbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  border-bottom: var(--border-width) solid var(--hairline);
  background: var(--pane);
}

.tabbar-strips { display: flex; flex: 1; gap: var(--space-2); min-width: 0; }
.tabbar[data-split="on"] .tabstrip-scroller { flex: 1 1 50%; }
.tabbar-actions { display: flex; gap: var(--space-1); flex: none; }

/* A strip that scrolls has to say so. Overlay scrollbars are invisible until
   you are already scrolling, so an overflowing strip used to look exactly
   like a strip that fitted — with the active tab 327px off-screen and no way
   to reach it. The wrapper carries a fading edge and a real button on each
   side, both driven by `data-overflow-start` / `data-overflow-end`. */
.tabstrip-scroller {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex: 1;
  min-width: 0;
}
.tabstrip-scroller[hidden] { display: none; }

.tabstrip {
  display: flex;
  gap: var(--space-1);
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: thin;
}
.tabstrip[hidden] { display: none; }

.tabstrip-nudge { flex: none; }
.tabstrip-scroller:not([data-overflow-start="true"]) .tabstrip-nudge[data-dir="start"],
.tabstrip-scroller:not([data-overflow-end="true"]) .tabstrip-nudge[data-dir="end"] { display: none; }
/* `i-chev` points right. */
.tabstrip-nudge[data-dir="start"] .icon { transform: rotate(180deg); }

/* The fade sits over the strip, not over the buttons, and never eats clicks. */
.tabstrip-scroller::before,
.tabstrip-scroller::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: var(--space-5);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-standard);
}
.tabstrip-scroller::before {
  left: 0;
  background: linear-gradient(to right, var(--pane), transparent);
}
.tabstrip-scroller::after {
  right: 0;
  background: linear-gradient(to left, var(--pane), transparent);
}
.tabstrip-scroller[data-overflow-start="true"]::before,
.tabstrip-scroller[data-overflow-end="true"]::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .tabstrip { scroll-behavior: auto; }
  .tabstrip-scroller::before, .tabstrip-scroller::after { transition: none; }
}
[data-animation="off"] .tabstrip { scroll-behavior: auto; }
[data-animation="off"] .tabstrip-scroller::before,
[data-animation="off"] .tabstrip-scroller::after { transition: none !important; }

.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 14rem;
  min-height: var(--control-h);
  padding: 0 var(--space-2) 0 var(--space-3);
  border: var(--border-width) solid transparent;
  border-radius: var(--control-radius);
  background: transparent;
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  white-space: nowrap;
}
.tab:hover { background: var(--accent-ghost); color: var(--fg); }
.tab[aria-selected="true"] {
  background: var(--bg-raise);
  border-color: var(--hairline);
  color: var(--fg-strong);
  box-shadow: var(--shadow-soft);
}
.tab[data-dragging="true"] { opacity: 0.5; }
.tab[data-drop-target="true"] { border-color: var(--accent); }
.tab[data-pinned="true"] { border-style: dashed; }
.tab-title { overflow: hidden; text-overflow: ellipsis; }
.tab-dirty { color: var(--warn-ink); font-size: var(--text-xs); }
.tab-close { opacity: 0.55; }
.tab:hover .tab-close, .tab[aria-selected="true"] .tab-close { opacity: 1; }

/* ---- centre pane: lanes ------------------------------------------ */

.lanes {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--pane-gap);
  flex: 1;
  min-height: 0;
  padding: var(--pane-pad);
}
.lanes[data-split="on"] { grid-template-columns: 1fr 1fr; }

.lane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-md);
}
.lane[hidden] { display: none; }
.lanes[data-split="on"] .lane[data-focused="true"] { border-color: var(--accent-soft); }

.lane-body { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }

.surface {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: auto;
  /* A surface is laid out against the lane it is in. The lane is roughly
     half the window when the workbench is split and about 55% of it when it
     is not, so a surface that sizes itself from the viewport gets it wrong
     every time — which is how the Blocks script column ended up 115px wide
     inside a 1440px window. */
  container-name: surface;
  container-type: inline-size;
}
.surface[hidden] { display: none; }

/* Skin only — the layout comes from `.ui-head` in design.css. */
.surface-head { padding-bottom: var(--space-3); }
.surface-title { font-size: var(--text-lg); }
.surface-status { font-size: var(--text-sm); color: var(--muted); }
.surface-placeholder { display: grid; gap: var(--stack-gap); padding: var(--space-2); }
.surface-editor { display: flex; flex-direction: column; gap: var(--space-2); flex: 1; min-height: 0; }

.editor {
  flex: 1;
  min-height: 12rem;
  border-radius: var(--radius-md);
  background: var(--bg-raise);
  font-family: var(--font-mono);
  font-size: var(--text-md);
  resize: none;
}

/* ---- right pane: Leia -------------------------------------------- */

.leia-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-bottom: var(--border-width) solid var(--hairline);
}

.leia-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.85rem;
  height: 1.85rem;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--on-accent);
}

.leia-headings { display: grid; gap: 0.1rem; flex: 1; min-width: 0; }
.leia-name {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-display);
  line-height: 1.1;
}

.leia-history { padding: var(--space-2) var(--space-3) 0; }
.leia-history-toggle { padding-left: var(--space-1); }
.leia-history-list {
  display: grid;
  gap: var(--space-1);
  margin-top: var(--space-2);
  list-style: none;
}
.leia-history-list[hidden] { display: none; }
.leia-history-item { display: block; }

/* A history row is a control, not a caption: it has to look and behave like
 * something you can open, at a size a child can hit. */
.leia-history-open,
.leia-history-static {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: var(--space-1) var(--space-2);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  font: inherit;
  font-size: var(--text-sm);
  text-align: start;
  color: var(--muted);
}
.leia-history-open { cursor: pointer; }
.leia-history-open:hover { color: var(--fg); background: var(--accent-ghost); }
.leia-history-open:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}
.leia-history-open[aria-busy="true"] { opacity: 0.7; }
.leia-history-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.leia-history-when { flex: none; font-variant-numeric: tabular-nums; }

/* The transcript is the only scroller in this pane: the aside is
 * overflow:hidden and the document does not scroll. Every declaration below
 * is load-bearing.
 *
 *   flex/min-height  the pane is a column flexbox; without `min-height: 0` a
 *                    flex item refuses to shrink below its content and the
 *                    scroller never gets a bounded height to scroll inside.
 *   overflow-y       what makes it a scroll container at all.
 *   scroll-behavior  MUST stay `auto`. It was `smooth`, which turns every
 *                    `scrollTop = …` into an animation; restarting that
 *                    animation on each streamed token left the view 11.5px
 *                    into a 1215px range at 1280x800, with the rest of Leia's
 *                    answer — and every control she offered with it — below
 *                    the fold and unreachable.
 *   overflow-anchor  browser scroll anchoring fights the "pin to the newest
 *                    message" logic; the pane does that itself, deliberately.
 */
.leia-transcript {
  flex: 1 1 0%;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  overflow-anchor: none;
  scroll-behavior: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--pane-pad);
}
.leia-transcript:empty { display: none; }
.leia-transcript:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: calc(var(--focus-offset) * -1);
}

/* "Jump to latest" floats over the top edge of the composer, so it is in the
 * same place whatever the transcript is doing. */
.leia-jump {
  position: absolute;
  inset-inline: 0;
  bottom: 100%;
  z-index: var(--z-sticky);
  display: flex;
  justify-content: center;
  padding-block-end: var(--space-2);
  pointer-events: none;
}
.leia-jump[hidden] { display: none; }
.leia-jump .btn {
  pointer-events: auto;
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-pop);
}

.leia-empty {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  align-content: start;
  padding: var(--space-5) var(--pane-pad);
  display: grid;
  gap: var(--space-3);
}
.leia-empty-title {
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-display);
  max-width: 14ch;
}
.leia-empty-body { color: var(--muted); font-size: var(--text-md); max-width: 44ch; }
.leia-suggestions { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.turn { display: grid; gap: var(--space-2); }
.turn-head { display: flex; align-items: center; gap: var(--space-2); }
.turn-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--accent-ink);
}
.turn-avatar[data-who="leia"] { background: var(--accent); color: var(--on-accent); }
.turn-name {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}
.turn-body { font-size: var(--text-md); overflow-wrap: anywhere; }
.turn[aria-busy="true"] .turn-body::after {
  content: "";
  display: inline-block;
  width: 0.5em;
  height: 1em;
  margin-left: 0.15em;
  vertical-align: -0.15em;
  background: var(--accent);
  animation: leia-caret 1s steps(2, end) infinite;
}
.turn[data-who="pupil"] .turn-body {
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--bg-raise);
  border: var(--border-width) solid var(--hairline);
}

@keyframes leia-caret { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

.turn-error {
  display: grid;
  gap: var(--space-2);
  justify-items: start;
  padding: var(--space-3);
  border: var(--border-width) solid var(--danger-soft);
  border-radius: var(--radius-md);
  background: var(--danger-soft);
  color: var(--danger-ink);
  font-size: var(--text-md);
}

.composer {
  /* The anchor for anything that floats above the composer: the `@` mention
   * list and the "Jump to latest" control. */
  position: relative;
  padding: var(--space-3);
  border-top: var(--border-width) solid var(--hairline);
  background: var(--pane);
}
.composer-inner {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-2);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-raise);
}
.composer-inner:focus-within { border-color: var(--accent); box-shadow: var(--shadow-pop); }
.composer .textarea {
  border: 0;
  background: transparent;
  min-height: var(--control-h);
  padding: var(--space-1) var(--space-2);
  resize: none;
}
.composer .textarea:hover { border-color: transparent; }
.composer-row { display: flex; align-items: center; gap: var(--space-2); }
.composer-hint { flex: 1; font-size: var(--text-xs); color: var(--muted); }

/* ---- welcome gate ------------------------------------------------ */

.welcome-card {
  position: relative;
  display: grid;
  gap: var(--space-3);
  justify-items: start;
  width: min(34rem, 100%);
  padding: var(--space-8) var(--space-6);
  border: var(--border-width) solid var(--hairline);
  border-radius: var(--radius-xl);
  background: var(--overlay-bg);
  box-shadow: var(--shadow-lift);
}

.welcome-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--on-accent);
}
.welcome-title { font-size: var(--text-3xl); }
.welcome-lead { color: var(--muted); font-size: var(--text-lg); max-width: 40ch; }
.welcome-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-2); }
.welcome-note { color: var(--muted); font-size: var(--text-sm); }

/* ---- tour -------------------------------------------------------- */

.tour { display: grid; gap: var(--space-3); }
.tour-body { color: var(--muted); line-height: var(--leading-body); }

[data-tour-highlight="true"] {
  position: relative;
  outline: var(--focus-width) solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-lg);
}

/* ---- preferences ------------------------------------------------- */

.prefs { display: grid; gap: var(--stack-gap); }
.prefs-intro { color: var(--muted); font-size: var(--text-md); }
.prefs-grid { display: grid; gap: var(--stack-gap); }
.prefs-footer { display: flex; justify-content: flex-start; padding-top: var(--space-2); border-top: var(--border-width) solid var(--hairline); }

/* ---- narrow-width dock ------------------------------------------- */

.dock {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-dock);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: var(--space-1);
  padding: var(--space-2);
  padding-bottom: max(var(--space-2), env(safe-area-inset-bottom));
  border-top: var(--border-width) solid var(--hairline);
  background: var(--glass-strong);
  backdrop-filter: blur(var(--blur-md));
}
.dock[hidden] { display: none; }

.dock-item {
  display: grid;
  justify-items: center;
  gap: 0.15rem;
  min-height: var(--tap-min);
  padding: var(--space-1);
  border: 0;
  border-radius: var(--control-radius);
  background: transparent;
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  cursor: pointer;
}
.dock-item[aria-pressed="true"] { color: var(--accent-ink); background: var(--accent-soft); }

/* ---- toasts ------------------------------------------------------ *
 *
 * Toasts land at the bottom of the *workbench*, clear of the Leia composer.
 *
 * In the viewport's bottom-right corner (the default in design.css) they sat
 * squarely on top of the composer and its Send button: at 1280x800 the
 * composer is x 901-1267 / y 667-787 and Send is x 1163-1246 / y 731-765,
 * entirely inside a 384px toast in that corner. So in the learner app every
 * "saved" and every "could not do that" covered the one control the child was
 * about to press, for as long as the message was up.
 *
 * Offsetting by the right pane's own width puts them in the workbench's
 * bottom-right instead: still the corner the eye goes to, over a surface with
 * nothing to press at its foot. `--right-w` is the widest the Leia pane is
 * ever laid out at — the shell narrows it at 75em without changing the token
 * — so the offset is a floor and never an underestimate, whatever the text
 * zoom. Once the panes stack there is no workbench corner to aim at, and the
 * rule immediately below moves toasts to the top of the screen instead.
 *
 * This lives here rather than in design.css because the grown-up desk
 * (parent.html) has no Leia pane and loads design.css without this file.
 *
 * The `clamp` is the guard rail: when the window is too narrow for the offset
 * and the toast both, the upper bound collapses below the lower one and
 * `clamp` returns the lower — the ordinary bottom-right corner — rather than
 * pushing the message off the left edge of the screen. */
.toast-host {
  --toast-w: min(24rem, calc(100vw - var(--space-8)));
  right: clamp(
    var(--space-4),
    calc(var(--right-w) + var(--pane-gap) + var(--space-4)),
    calc(100vw - var(--space-4) - var(--toast-w))
  );
  width: var(--toast-w);
}

/* Above the fold, not over the composer, once the panes stack.
 *
 * The narrow-layout container rule further down cannot do this job and never
 * has: the toast host is appended to `<body>`, outside `#app`, and a container
 * query only matches descendants of its container. In the narrow layout the
 * Leia pane is the whole window, so a bottom-anchored toast lands on the
 * composer again — including at 200% text zoom in a 1280px window, which is
 * narrow by available space while every media query still reads 1280px.
 *
 * A rem media query is not an option here: it is exactly the thing that is
 * blind to text zoom, and §2 of test/layout-overflow.test.js forbids one in
 * the live path for that reason. `responsive.js` already mirrors the
 * container's em arithmetic onto `#app[data-breakpoint]`, so `:has()` reads
 * the same decision the grid did, zoom included, from outside the container.
 * See docs/handoffs/P18.md §2 for the structural fix. */
body:has(#app[data-breakpoint="narrow"]) .toast-host,
body:has(.shell[data-breakpoint="narrow"]) .toast-host {
  right: var(--space-2);
  left: var(--space-2);
  top: var(--space-2);
  bottom: auto;
  width: auto;
}

/* ---- responsive -------------------------------------------------- */

/* The dock is CSS-driven, not JS-driven: it exists in the DOM at every size
   and `display: none` keeps it out of the accessibility tree until the
   layout is actually narrow. That way a text-zoom change reflows the shell
   and the dock together, with no script having to notice. */
.dock { display: none; }

@container leia-app (max-width: 75em) {
  .shell { grid-template-columns: 13.5rem minmax(0, 1fr) 19rem; }
  .brand-tagline { display: none; }
}

/* The one rule the container query cannot carry: a query container may not
   style itself, only its descendants. `shell/responsive.js` mirrors the same
   em arithmetic onto `#app` so this still keys off available space rather
   than device width. */
#app[data-breakpoint="narrow"] {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
}

@container leia-app (max-width: 55em) {
  /* The dock joins the flow instead of floating, so it cannot cover the
     shell at large text sizes. */
  .dock { display: grid; position: static; flex: none; }
  .shell {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr);
    height: auto;
    flex: 1;
    min-height: 0;
    padding: var(--space-2);
  }
  .shell > .pane { display: none; }
  .shell[data-mobile-pane="learn"] > .pane-left,
  .shell[data-mobile-pane="work"] > .pane-center,
  .shell[data-mobile-pane="leia"] > .pane-right { display: flex; }
  .lanes[data-split="on"] { grid-template-columns: 1fr; }
  .tabbar[data-split="on"] .tabstrip { flex: 1 1 100%; }
  .welcome-card { padding: var(--space-6) var(--space-4); }
  .welcome-title { font-size: var(--text-2xl); }
  /* "Above the fold rather than behind the dock" used to be a `.toast-host`
     rule here. It never applied: the toast host is a child of `<body>`, not of
     `#app`, and a container query only matches its container's descendants.
     The rule that does the job is the `:has()` one further up. */
}

/* The two-lane split stops being useful long before the shell does. */
@container leia-app (max-width: 40em) {
  .lanes[data-split="on"] { grid-template-columns: 1fr; }
  .lanes[data-split="on"] .lane[data-focused="false"] { display: none; }
}

/* Container queries need a container; if the shell is ever mounted outside
   `#app` these keep the narrow layout reachable from device width alone. */
@supports not (container-type: inline-size) {
  @media (max-width: 75rem) {
    .shell { grid-template-columns: 13.5rem minmax(0, 1fr) 19rem; }
    .brand-tagline { display: none; }
  }
  @media (max-width: 55rem) {
    .dock { display: grid; position: static; flex: none; }
    .shell {
      grid-template-columns: 1fr;
      grid-template-rows: minmax(0, 1fr);
      height: auto; flex: 1; min-height: 0; padding: var(--space-2);
    }
    .shell > .pane { display: none; }
    .shell[data-mobile-pane="learn"] > .pane-left,
    .shell[data-mobile-pane="work"] > .pane-center,
    .shell[data-mobile-pane="leia"] > .pane-right { display: flex; }
  }
}

/* `.leia-transcript` is `scroll-behavior: auto` for everyone now — see the
 * comment on the rule — so there is nothing left here to turn off under
 * reduced motion. */
