/* Hub's own copy of the Lakeside Code design system — tokens, glass material,
   chrome. Forked and pruned 2026-07-30: this file no longer tracks upstream
   app.css (the re-copy ritual is retired); unused upstream components were
   removed. Edit freely — hub owns this file now. */

:root {
  color-scheme: dark;
  --bg: #000;
  --bg-raised: #0a0a0a;
  --surface: rgba(22, 22, 22, .82);
  --surface-solid: #111;
  --text: #f5f5f7;
  --text-secondary: #a1a1a6;
  --text-tertiary: #6e6e73;
  --line: rgba(255, 255, 255, .1);
  --line-strong: rgba(255, 255, 255, .16);
  --control: rgba(255, 255, 255, .08);
  --control-hover: rgba(255, 255, 255, .14);
  --inverted: #fff;
  --inverted-text: #000;
  --accent: #d9ff5b;
  --accent-soft: rgba(217, 255, 91, .12);
  --warning: #ff9f0a;
  --danger: #ff453a;
  --success: #32d74b;
  --focus: #d9ff5b;
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
  --mono: "SFMono-Regular", "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* Elevation. On true black a drop shadow does almost no work — separation
     comes from the specular edge. The drops are kept for the detached layer. */
  --elev-1: 0 1px 2px rgba(0, 0, 0, .5);
  --elev-2: 0 8px 26px rgba(0, 0, 0, .46);
  --elev-3: 0 14px 48px rgba(0, 0, 0, .5);
  --elev-4: 0 24px 80px rgba(0, 0, 0, .5);
  --shadow: var(--elev-4);

  /* Liquid glass.
     tint = the material's own body over the blurred backdrop
     spec = specular top edge, rim = inner bottom shade (thickness)
     solid = the stand-in where backdrop-filter is unavailable. It was fully
     opaque, which is legible and reads as "the glass is broken" — a flat black
     band with content clipped hard against it, which is what a browser with
     backdrop-filter disabled has been showing all along. Now slightly
     translucent: still comfortably legible over any content, but content
     passing under it is visible rather than erased. Chosen to
     match how each tint composites over --bg. */
  --glass-blur-thin: 18px;
  --glass-blur-regular: 36px;
  --glass-blur-thick: 46px;
  --glass-sat: 180%;
  /* Dark glass is the hard case: blurring dark content returns dark content, so
     without a lift the material reads as a slightly-less-black panel. Glass
     GATHERS light — it is brighter than what it covers. */
  --glass-lift: 1.18;
  /* The wash the chrome refracts. Deliberately weak — strong enough for the
     blur to have gradient to work with, weak enough that true black still
     reads as black. */
  --wash-a: rgba(96, 128, 255, .20);
  /* Cool, not lime. The accent colour here was pulled to gold by
     saturate(200%) and turned every warning surface brown — a wash is there to
     give the blur gradient to bend, not to tint what sits above it. */
  --wash-b: rgba(150, 110, 235, .15);

  /* Tints are LIGHTER than the page, not darker.
     A pane of glass over a dark surface scatters light towards the viewer, so
     it sits lighter and hazier than what is behind it. These used to be darker
     than the content they floated over, which reads as a hole rather than a
     material. Alpha is lower too: more of the blurred backdrop shows through,
     which is the part that actually looks like glass. */
  --glass-tint-chrome: rgba(28, 30, 40, .30);
  --glass-tint-regular: rgba(28, 30, 40, .34);
  --glass-tint-thick: rgba(48, 48, 56, .44);
  --glass-solid-chrome: rgba(9, 9, 11, .86);
  --glass-solid-regular: rgba(23, 23, 25, .88);
  --glass-solid-thick: #1c1c1e;
  --glass-edge: rgba(255, 255, 255, .16);
  --glass-spec: inset 0 1px 0 rgba(255, 255, 255, .18);
  --glass-rim: inset 0 -1px 0 rgba(0, 0, 0, .3);
  --glass-sheen: linear-gradient(180deg, rgba(255, 255, 255, .13) 0%, rgba(255, 255, 255, .02) 40%, rgba(255, 255, 255, 0) 70%);

  /* Scrims and semantic fills, replacing the raw rgba that ignored the theme. */
  --scrim: rgba(0, 0, 0, .55);
  --scrim-strong: rgba(0, 0, 0, .72);
  --scrim-ink: #fff;
  --warning-soft: rgba(255, 159, 10, .14);
  --warning-edge: rgba(255, 159, 10, .26);
  --warning-ink: #000;
  --danger-soft: rgba(255, 69, 58, .12);
  --danger-soft-hover: rgba(255, 69, 58, .18);
  --danger-edge: rgba(255, 69, 58, .3);
}

/* ── Scales ───────────────────────────────────────────────────────────
   Theme-invariant: only colour, glass and elevation fork per theme. */
:root {
  /* Space, on a 4pt grid. */
  --sp-1: 2px; --sp-2: 4px; --sp-3: 6px; --sp-4: 8px;
  --sp-5: 12px; --sp-6: 16px; --sp-7: 20px; --sp-8: 24px;
  --sp-9: 32px; --sp-10: 44px; --sp-11: 64px; --sp-12: 88px;

  /* Radius. One ladder for the eighteen literals that were in use. */
  --r-xs: 4px; --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-xl: 20px; --r-2xl: 28px;
  --r-pill: 999px; --r-circle: 50%;

  /* Type. */
  --fs-2xs: 10px; --fs-xs: 11px; --fs-sm: 12px; --fs-base: 13px;
  --fs-md: 15px; --fs-lg: 17px; --fs-xl: 21px; --fs-2xl: 28px;
  /* Fourteen distinct weights were in use across forty declarations. The rungs
     sit at each cluster's modal value, so the migration moves most text not at
     all and the rest by twenty to sixty units — well under a rendered pixel. */
  --fw-regular: 400; --fw-medium: 560; --fw-semibold: 610; --fw-bold: 650; --fw-heavy: 720;
  --tr-display: -.055em; --tr-tight: -.025em; --tr-normal: -.012em;
  --tr-caps: .04em; --tr-eyebrow: .11em;
  --lh-tight: 1.18; --lh-snug: 1.45; --lh-prose: 1.66;

  /* Motion. */
  --dur-1: 120ms; --dur-2: 180ms; --dur-3: 260ms; --dur-4: 380ms;
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-standard: cubic-bezier(.2, 0, 0, 1);
  --ease-spring: cubic-bezier(.2, .8, .2, 1);
  --t-control: color var(--dur-2) var(--ease-standard),
    background-color var(--dur-2) var(--ease-standard),
    border-color var(--dur-2) var(--ease-standard);

  /* Layout constants. --workbar-h in particular replaces a sticky offset that
     was written out four times and had drifted to three different values. */
  --bar-h: 84px;
  --workbar-h: 78px;
  --shell-w: 1040px;
  --reading-w: 820px;
  --dock-clear: 160px;
  --tap-min: 44px;

  --z-banner: 15; --z-bar: 20; --z-dock: 25; --z-jump: 26; --z-fab: 30; --z-toast: 100;
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f5f5f2;
  --bg-raised: #fbfbf9;
  --surface: rgba(255, 255, 255, .8);
  --surface-solid: #fff;
  --text: #161616;
  --text-secondary: #676765;
  --text-tertiary: #989894;
  --line: rgba(20, 20, 20, .1);
  --line-strong: rgba(20, 20, 20, .16);
  --control: rgba(20, 20, 20, .055);
  --control-hover: rgba(20, 20, 20, .1);
  --inverted: #151515;
  --inverted-text: #fff;
  --accent: #476b00;
  --accent-soft: rgba(84, 118, 0, .1);
  --warning: #b45f00;
  --danger: #d70015;
  --success: #138a28;
  --focus: #2b4a00;

  --elev-1: 0 1px 2px rgba(48, 44, 32, .07);
  --elev-2: 0 8px 24px rgba(48, 44, 32, .09);
  --elev-3: 0 14px 44px rgba(48, 44, 32, .12);
  --elev-4: 0 24px 70px rgba(30, 30, 20, .14);
  --shadow: var(--elev-4);

  /* Paper wants more tint and less saturation than black: colour pulled through
     a light material reads as a stain rather than a lens. */
  /* And a warmer, weaker wash — on paper the backdrop already has luminance, so
     this only has to give the blur some variation to bend. */
  --wash-a: rgba(180, 160, 120, .1);
  --wash-b: rgba(120, 150, 190, .07);
  --glass-blur-thin: 16px;
  --glass-blur-regular: 28px;
  --glass-blur-thick: 44px;
  --glass-sat: 155%;
  /* Light theme barely needs it: there is already plenty of light to gather. */
  --glass-lift: 1.04;
  --glass-tint-chrome: rgba(247, 247, 243, .72);
  --glass-tint-regular: rgba(255, 255, 255, .72);
  --glass-tint-thick: rgba(255, 255, 255, .86);
  --glass-solid-chrome: rgba(247, 247, 243, .88);
  --glass-solid-regular: rgba(253, 253, 251, .9);
  --glass-solid-thick: #fefefd;
  --glass-edge: rgba(24, 24, 18, .1);
  --glass-spec: inset 0 1px 0 rgba(255, 255, 255, .85);
  --glass-rim: inset 0 -1px 0 rgba(48, 44, 32, .055);
  --glass-sheen: linear-gradient(180deg, rgba(255, 255, 255, .5) 0%, rgba(255, 255, 255, 0) 52%);

  --scrim: rgba(58, 55, 44, .3);
  --scrim-strong: rgba(0, 0, 0, .72);   /* sits over a photo — stays dark */
  --scrim-ink: #fff;
  --warning-soft: rgba(180, 95, 0, .11);
  --warning-edge: rgba(180, 95, 0, .24);
  /* Black reads 4.58:1 on #b45f00 and white 4.59:1 — a dead heat, so this keeps
     black to match the dark theme. The token exists so the choice can fork per
     theme if either --warning ever moves; the rule no longer hardcodes it. */
  --warning-ink: #000;
  --danger-soft: rgba(215, 0, 21, .09);
  --danger-soft-hover: rgba(215, 0, 21, .14);
  --danger-edge: rgba(215, 0, 21, .26);
}

* { box-sizing: border-box; }
html { background: var(--bg); min-height: 100%; }
/* The page carries a faint luminance wash, and this is what makes the glass
   read as glass at all.

   backdrop-filter samples what is behind it. On a pure #000 page with dark
   content there is nothing to sample: blurring black returns black, so the
   material was doing no visible work and only the 1px specular edge showed.
   Two very soft off-white pools give the chrome something to refract without
   lifting the page off true black anywhere a large flat area is visible. */
body {
  min-height: 100vh;
  margin: 0;
  /* Three pools, not two, and much stronger than before.
     
     This is the fix that matters. A material that samples its backdrop has
     nothing to sample on a flat black page: blurring black returns black, and
     refracting black returns black. The chrome was correct and invisible.
     Apple's glass reads because iOS backgrounds are dense and colourful; ours
     was #000 with sparse text.
     
     Still dark — the darkest point is unchanged — but there is now light and
     colour VARIATION across the viewport for the glass to gather and bend. */
  background:
    radial-gradient(90% 55% at 8% -8%, var(--wash-a) 0%, transparent 60%),
    radial-gradient(80% 50% at 96% 2%, var(--wash-b) 0%, transparent 55%),
    radial-gradient(70% 45% at 50% 108%, rgba(80, 190, 220, .12) 0%, transparent 60%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
button, input, textarea, select { color: inherit; font: inherit; }
button, a, select { -webkit-tap-highlight-color: transparent; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }
:where(svg) { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
[hidden] { display: none !important; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Keyboard focus. Tap highlights are suppressed for touch, so every
   interactive surface needs a deliberate ring for keyboard users. */
:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
kbd {
  padding: 2px 5px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--control); color: var(--text-secondary); font: 10px/1.4 var(--mono);
}

.app-bar, .workspace-bar {
  position: sticky;
  top: 0;
  z-index: var(--z-bar);
  /* A symmetric grid, not space-between.
     
     space-between distributes the LEFTOVER space, so the nav sat wherever the
     wordmark and the buttons happened to leave room — and the button count runs
     from one on the harness to five on Ask. The selector visibly jumped every
     time you changed page, which reads as the page being badly built rather
     than as a layout rule.
     
     Equal 1fr rails put the middle column at the true centre of the bar no
     matter what flanks it. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: 84px;
  padding: max(18px, env(safe-area-inset-top)) clamp(22px, 4vw, 52px) 12px;
}
.app-bar > .wordmark, .workspace-bar > .wordmark,
.app-bar > :first-child, .workspace-bar > :first-child { justify-self: start; min-width: 0; }
.app-bar > .mode-tabs, .workspace-bar > .mode-tabs { justify-self: center; }
.app-bar > .bar-actions, .workspace-bar > .bar-actions { justify-self: end; }

@media (max-width: 720px) {
  /* No room for three rails on a phone: the nav takes the width and the two
     flanks sit either side of it. */
  .app-bar, .workspace-bar { grid-template-columns: auto 1fr auto; }
}
.wordmark { display: inline-flex; align-items: center; gap: 11px; font-weight: var(--fw-bold); letter-spacing: -.02em; }
.mark { position: relative; display: inline-flex; width: 24px; height: 24px; align-items: center; justify-content: center; }
.mark i {
  position: absolute; width: 8px; height: 19px; border-radius: var(--r-sm) var(--r-sm) var(--r-xs) var(--r-xs);
  background: var(--text); transform: rotate(32deg) translateX(-3px);
}
.mark i + i { height: 14px; opacity: .38; transform: rotate(32deg) translate(5px, -2px); }
.large-mark { width: 42px; height: 42px; margin-bottom: 18px; }
.large-mark i { width: 13px; height: 34px; }
.large-mark i + i { height: 25px; }
.mode-tabs:has(.mode-tab:nth-child(1).selected) { --i: 0; }
.mode-tabs:has(.mode-tab:nth-child(2).selected) { --i: 1; }
.mode-tabs:has(.mode-tab:nth-child(3).selected) { --i: 2; }
.mode-tabs:has(.mode-tab:nth-child(4).selected) { --i: 3; }
.mode-tabs:has(.mode-tab:nth-child(5).selected) { --i: 4; }
/* The thumb supplies the fill; the tab only changes ink. Both would double the
   background and make the thumb invisible as it travels. */
.mode-tab.selected { background: transparent; color: var(--inverted-text); }

/* No :has() — every tab keeps its own pill, exactly as before. Degrades to the
   old behaviour rather than to a control with no visible selection. */
@supports not selector(:has(*)) {
  .mode-tab.selected { background: var(--inverted); }
}
.bar-actions { display: flex; align-items: center; gap: 8px; }
.circle-button {
  display: inline-flex; width: 44px; height: 44px; padding: 0; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: var(--r-circle); background: var(--control); color: var(--text);
  transition: transform var(--dur-2) var(--ease-standard), background var(--dur-2) var(--ease-standard), border-color var(--dur-2) var(--ease-standard);
}
.circle-button:active { transform: scale(.94); }
.circle-button svg { width: 21px; height: 21px; }
.circle-button.compact { width: 38px; height: 38px; }
.theme-toggle .sun-icon { display: block; }
.theme-toggle .moon-icon { display: none; }
:root[data-theme="light"] .theme-toggle .sun-icon { display: none; }
:root[data-theme="light"] .theme-toggle .moon-icon { display: block; }
.metric { display: flex; flex-direction: column; gap: 3px; }
.metric strong { font-size: 28px; font-weight: var(--fw-medium); letter-spacing: -.045em; line-height: 1; font-variant-numeric: tabular-nums; }
.metric span { color: var(--text-tertiary); font-size: 12px; }
.empty-list { padding: 28px 6px; color: var(--text-tertiary); }
.skeleton-line.short { width: min(28%, 180px); height: 10px; margin-top: 10px; }
@keyframes skeleton-sweep { from { background-position: 130% 0; } to { background-position: -130% 0; } }
.search-results .agent-row.selected { background: var(--control); box-shadow: inset 2px 0 0 var(--accent); }

.sheet {
  width: 100%; height: 100%; max-width: none; max-height: none; padding: 0; border: 0;
  background: transparent; color: var(--text);
}
.sheet::backdrop { background: var(--scrim); backdrop-filter: blur(var(--glass-blur-thin)); -webkit-backdrop-filter: blur(var(--glass-blur-thin)); }
.sheet[open] .sheet-panel { animation: sheet-in .28s cubic-bezier(.2, .8, .2, 1); }
@keyframes sheet-in { from { opacity: 0; transform: translate(-50%, 18px) scale(.985); } }
.form-error { min-height: 18px; margin: 10px 4px 0; color: var(--danger); font-size: 12px; }
.presence.live i { background: var(--success); }
.turn { margin-bottom: 46px; }
.tool-group.open .tool-summary svg { transform: rotate(90deg); }
.tool-group.open .tool-detail { display: block; }
.change-card.open .change-chevron { transform: rotate(90deg); }
.validation-row.failed .check-mark { background: color-mix(in srgb, var(--danger) 22%, transparent); color: var(--danger); }
.validation-row.running .check-mark { background: var(--control); color: var(--text-tertiary); }
.validation-row.failed .check-name { color: var(--danger); }
/* A check nobody ran is not a check that passed. Shown, dimmed, and counted
   separately — silence about a skipped check reads as approval. */
.validation-row.skipped .check-mark { background: var(--control); color: var(--text-tertiary); }
.validation-row.skipped .check-name { color: var(--text-tertiary); }
.delivery-step .dot { width: 7px; height: 7px; border-radius: var(--r-circle); background: var(--line-strong); }
.delivery-step.reached .dot { background: var(--accent); }
.delivery-step.current { color: var(--text); font-weight: var(--fw-semibold); }
.delivery-step.current .dot { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.delivery-step.retained .dot { background: var(--warning); }
.change-card.open .change-raw { display: block; }
@keyframes loading-ui-classic-fade { 0% { opacity: 1; } 100% { opacity: .14; } }
@keyframes text-shimmer-wave { from { background-position: 120% 0; } to { background-position: -120% 0; } }
/* ── Materials ────────────────────────────────────────────────────────
   Every glass surface is legible with no backdrop-filter at all, and the blur
   is layered on top inside @supports. Progressive enhancement rather than
   @supports not(...): a browser that mis-parses the query then degrades to
   readable instead of transparent-and-unblurred, which is the failure worth
   defending against.

   Each surface declares only its level; the recipe is written once. */
.workspace-bar, .app-bar,
.sheet-panel, .composer, .jump-latest,
.attention-banner, .question-panel {
  background-color: var(--mat-solid, var(--glass-solid-regular));
  background-image: var(--glass-sheen);
  box-shadow: var(--glass-spec), var(--glass-rim);
}

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  /* Force a compositing layer.
     
     backdrop-filter only paints when the element has its own layer. Without a
     promotion hint some engines and some GPU configurations accept the
     declaration, report it in the computed style, and render nothing — which
     looks exactly like "the blur is too weak" and is really "the blur never
     ran". transform: translateZ(0) is the long-standing way to ask for the
     layer; will-change states the intent for engines that read it. */
  .workspace-bar, .app-bar,
  .sheet-panel, .composer, .jump-latest,
  .attention-banner, .question-panel, .prompt-field {
    background-color: var(--mat-tint, var(--glass-tint-regular));
    -webkit-backdrop-filter: blur(var(--mat-blur, var(--glass-blur-regular))) saturate(var(--glass-sat)) brightness(var(--glass-lift));
    backdrop-filter: blur(var(--mat-blur, var(--glass-blur-regular))) saturate(var(--glass-sat)) brightness(var(--glass-lift));
    transform: translateZ(0);
    will-change: backdrop-filter;
  }
}

/* Chrome: pinned to a viewport edge with content scrolling UNDER it. A hairline
   on the content side, no drop — the shadow belongs to things that float. */
.workspace-bar, .app-bar {
  --mat-tint: var(--glass-tint-chrome);
  --mat-solid: var(--glass-solid-chrome);
  border-bottom: 1px solid var(--glass-edge);
}

/* Never nest a blur inside a blur. A backdrop root samples its ancestor's
   ALREADY blurred output, which reads muddy on WebKit and is the most expensive
   thing on the page to composite. These all live inside a bar that carries the
   material, so they keep a plain translucent fill and no filter of their own. */
.circle-button, .mode-tabs, .segmented-control {
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.segmented-control.wrap { grid-auto-flow: row; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: 2px; }
.segmented-control button.selected { background: var(--surface-solid); color: var(--text); box-shadow: var(--elev-1); }
/* #toast is the live region and stays put; the messages are its children, so a
   second one does not overwrite the first before it has been read. */
.toast {
  position: fixed; z-index: var(--z-toast); left: 50%; bottom: max(30px, calc(var(--safe-bottom) + 20px));
  display: flex; width: max-content; max-width: calc(100% - 40px); flex-direction: column; gap: var(--sp-4);
  align-items: center; transform: translateX(-50%); pointer-events: none;
}
.toast:empty { display: none; }
/* On the two surfaces with a fixed composer, clear it — a message landing on
   top of the input the reader is about to use is in the way, not in view. */
.workspace-page .toast, .ask-page .toast { bottom: calc(var(--dock-clear) - var(--sp-7)); }
.toast-item {
  display: flex; max-width: 100%; padding: var(--sp-4) var(--sp-4) var(--sp-4) var(--sp-5);
  align-items: center; gap: var(--sp-5);
  border: 1px solid var(--line-strong); border-left: 3px solid var(--text-tertiary);
  border-radius: var(--r-md); background: var(--surface-solid); box-shadow: var(--elev-3);
  color: var(--text); font-size: var(--fs-sm); cursor: pointer; pointer-events: auto;
  opacity: 0; transform: translateY(var(--sp-4));
  transition: opacity var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out);
}
.toast-item.is-shown { opacity: 1; transform: translateY(0); }
.toast-item.is-leaving { opacity: 0; transform: translateY(calc(var(--sp-2) * -1)); }
/* Tone is a leading bar rather than a wash, so an error is distinguishable at a
   glance without the message becoming hard to read. */
.toast-item.tone-success { border-left-color: var(--success); }
.toast-item.tone-warning { border-left-color: var(--warning); }
.toast-item.tone-error { border-left-color: var(--danger); }
.toast-text { min-width: 0; }

/* Activity.
   The commit strip is a sequential encoding — one hue, light to dark, anchored
   away from the surface in each theme, with the near-zero step allowed to
   recede. Steps were validated for monotone lightness, step separation, and a
   readable gap between "no commits" and "one commit" in both themes; every cell
   also carries its exact count in a label, and the row prints the number, so
   nothing here is readable by colour alone. */
:root {
  --heat-0: rgba(255, 255, 255, .07);
  --heat-1: #3c4719;
  --heat-2: #5b6b26;
  --heat-3: #94ad3e;
  --heat-4: #d9ff5b;
}
:root[data-theme="light"] {
  --heat-0: rgba(20, 20, 20, .06);
  --heat-1: #b6c39b;
  --heat-2: #94a86b;
  --heat-3: #5d8935;
  --heat-4: #476b00;
}
.strip-legend .cell { width: 9px; height: 9px; }
.cell { display: block; border-radius: var(--r-xs); background: var(--heat-0); }
.cell.level-1 { background: var(--heat-1); }
.cell.level-2 { background: var(--heat-2); }
.cell.level-3 { background: var(--heat-3); }
.cell.level-4 { background: var(--heat-4); }
.activity-strip .cell { width: 11px; height: 22px; border-radius: var(--r-xs); }
@keyframes caret-blink { 50% { opacity: 0; } }
/* Undo the icon defaults at the top of this file. Every other svg on the page is
   a 24px line icon, so `svg { width: 24px; fill: none; stroke: currentColor }`
   would otherwise crush a diagram into an icon-sized, unfilled tangle. Sizing
   comes from the width/height attributes the renderer derives from the viewBox;
   colour comes from the diagram's own adopted stylesheet. */
.mermaid-view svg { display: block; width: auto; height: auto; max-width: 100%; margin: 0 auto; }
/* A knowledge doc or a state read is not a path, so it expands in place instead
   of opening the file viewer. */
.source-row.static { margin: 0; white-space: normal; }
.segmented-control.compact { display: inline-grid; grid-auto-flow: column; grid-template-columns: none; padding: 2px; border-radius: var(--r-md); }
.segmented-control.compact button { padding: 5px 11px; border-radius: var(--r-sm); font-size: 11px; }
/* Model and effort lists vary in length by provider, so they wrap instead of
   forcing every option into three fixed columns. */
.segmented-control.wrap { display: flex; flex-wrap: wrap; gap: 4px; grid-template-columns: none; }
.segmented-control.wrap button { flex: 1 1 auto; min-width: 76px; }

@media (max-width: 680px) {
  .app-bar { height: 76px; padding-right: 18px; padding-left: 18px; }
  .activity-strip .cell { width: 100%; max-width: 14px; flex: 1; }
  .metric strong { font-size: 23px; }
  .sheet[open] .sheet-panel { animation-name: mobile-sheet-in; }
  @keyframes mobile-sheet-in { from { opacity: 0; transform: translateY(24px); } }
  .workspace-bar .theme-toggle { display: none; }
  .turn { margin-bottom: 36px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* ── Tap targets ──────────────────────────────────────────────────────
   44px is the iOS minimum and the thumb does not care what the box model
   says. Where the visual size has to stay small, a transparent ::after
   expander grows the hit area without moving a pixel. */
.circle-button.compact { width: 40px; height: 40px; }
/* 44px cannot fit in the Ask composer without eating the input it sits under.
   32px plus the surrounding spacing meets WCAG 2.5.8 AA; 2.5.5 AAA is not
   reachable here and distorting the dock to chase it would cost more than it
   buys. */
.segmented-control.compact button { min-height: 32px; }

.circle-button.compact, .attach-button, .send-button,
button.context-chip, .answer-action, .icon-action { position: relative; }
.circle-button.compact::after, .attach-button::after, .send-button::after,
button.context-chip::after, .answer-action::after, .icon-action::after {
  content: ""; position: absolute; inset: -4px;
}

/* Hover is a pointer capability, not a default. Without this guard every one
   of these fires as sticky hover on a touch screen: the row a thumb last
   touched stays lit as though it were still under a cursor. :active and
   :focus-visible stay outside, because a touch device has both. */
@media (hover: hover) and (pointer: fine) {
  .circle-button:hover { background: var(--control-hover); border-color: var(--line-strong); }
  .source-row.static:hover { background: transparent; color: var(--text-secondary); }
}
.kind-mark.check { border-radius: var(--r-circle); }

/* The delivery rail: branch, dev, production.
   Three segments rather than a tick, because a tick says "finished" for work
   that has sat unmerged on a branch for three weeks. Each segment has a title,
   so the meaning is available without a legend. */
.rail { display: flex; gap: 3px; }
.rail i {
  display: block; width: 16px; height: 4px; border-radius: var(--r-pill);
  background: var(--line-strong); transition: background var(--dur-2) var(--ease-standard);
}
.rail i.on { background: var(--accent); }
/* Production is the only one that means users have it, so it alone gets the
   glow — the rail should reward shipping, not merging. */
.rail i.on.live { box-shadow: 0 0 0 2px var(--accent-soft); }
.rail.none i { background: color-mix(in srgb, var(--line-strong) 55%, transparent); }
.status-chip.active { background: var(--accent-soft); color: var(--accent); }
.status-chip.live { background: color-mix(in srgb, var(--success) 16%, transparent); color: var(--success); }
.run-line .who {
  color: var(--text-tertiary); font-size: 10px; font-weight: var(--fw-semibold);
  letter-spacing: .09em; text-transform: uppercase;
}
.run-line.tool .said { color: var(--text-secondary); font: 12px/1.5 var(--mono); }
.run-line.failed .who, .run-line.failed .said { color: var(--danger); }
.run-line.asked .who { color: var(--warning); }

/* Edge light. Real glass is brightest where it curves away, so the highlight
   belongs at the top and bottom edges rather than spread across the face — a
   uniform sheen is what makes a panel look like paper with opacity. */
.workspace-bar::after, .app-bar::after, .mode-tabs::after,
.composer::after, .sheet-panel::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--glass-edge) 100%, transparent) 0,
      transparent 1.5px),
    linear-gradient(to top,
      color-mix(in srgb, var(--glass-edge) 60%, transparent) 0,
      transparent 1px);
}

/* Chromatic split at the rim: the one cue that reads unmistakably as glass
   rather than as plastic. Kept to a hair — visible as iridescence at an edge,
   never as a coloured outline. */

@media (prefers-reduced-transparency: reduce), (prefers-reduced-motion: reduce) {
  /* Someone who has asked for less transparency has asked for less of exactly
     this. Fall back to the solid stand-in rather than a cheaper effect. */
  .workspace-bar, .app-bar, .mode-tabs, .composer {
    background-color: var(--mat-solid, var(--glass-solid-chrome));
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ------------------------------------------------- glass, turned up

   The previous pass made the material visible. This one makes it read as a
   THICK pane rather than a tinted sheet, which is the difference between
   "translucent" and "glass".

   Three cues, all of which real glass has and a blur alone does not:

   1. A specular sweep. Light does not land evenly on a curved surface — it
      pools where the surface turns. A single diagonal highlight across the top
      third does more for the illusion than any amount of extra blur.
   2. Thickness at the bottom. A pane has depth, so its lower inside edge sits
      in its own shadow. Without this the surface reads as film.
   3. A brighter, warmer rim on the lit edge and a cooler one opposite, which is
      the chromatic split glass produces at a boundary. */
.workspace-bar, .app-bar, .mode-tabs, .composer, .prompt-field, .sheet-panel {
  /* A whisper, not a stripe. The 104deg sweep read as a painted band across
     the face — the single most artificial thing about it. Glass is even across
     its face; the light lives at the edge. */
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, .045) 0%, transparent 38%);
}

.workspace-bar, .app-bar {
  box-shadow: inset 0 1px 0 var(--glass-edge);
}

/* The bar is chrome, and chrome is glass — always, not only once you have
   scrolled far enough.
   
   Every rule above used to be keyed on .is-pinned, a class a script adds past
   scrollY 4. On a page that had not been scrolled, or whose script had not run,
   the bar was a fully transparent strip with no material at all: measured
   backdrop-filter "none" and background rgba(0,0,0,0). Text passing under it
   showed through raw, which is exactly the "it does not refract anything"
   this kept coming back as.
   
   .is-pinned now only adds the separating hairline, which IS about whether
   content has gone under it. */
.app-bar.is-pinned { border-bottom: 1px solid var(--glass-edge); }
.workspace-bar > .bar-actions,
.workspace-bar > .back-button,
.workspace-bar > .presence { flex: 0 0 auto; }

/* ------------------------------------------------------- iOS input zoom

   Safari on iOS zooms the whole page when you focus a field whose font-size is
   under 16px, and it does NOT zoom back out afterwards — which is why the page
   ends up scrollable in both directions and feels like it has come loose.
   Nothing was overflowing: measured scrollWidth equals clientWidth at 390px on
   every page.

   16px on touch inputs is the documented way to stop it. Scoped to coarse
   pointers so the desktop type scale is untouched, and done here rather than by
   disabling user-scalable — taking pinch-zoom away from someone who needs it to
   read is not a fix. */
@media (pointer: coarse) {
  .composer textarea,
  .prompt-field textarea,
  input[type="text"], input[type="search"], input:not([type]), select, textarea {
    font-size: 16px;
  }
}
