/* ==========================================================================
   contextfirst.css

   v2: links benlive.tv's actual shared design system directly from the
   document head rather than maintaining a parallel token set. This reverses the original
   "must render standalone when cloned" rationale — this repo already
   depends on host-only scripts (/js/nav-toggle.js,
   /js/animation-observer.js) with documented graceful degradation when
   opened outside benlive.tv; extending that same acknowledged host
   coupling to CSS is consistent, not a new kind of compromise. See
   docs/architecture.md and docs/decisions.md for the full reasoning.
   ========================================================================== */

:root {
  /* contextfirst-specific aliases onto the shared token system, not new colors.
     Dark is the default everywhere else on the site — contextfirst matches. */
  --bg: var(--bg-dark);
  --bg-raised: var(--bg-card);
  --fg: var(--text-primary);
  --fg-muted: var(--text-secondary);
  --fg-faint: var(--text-muted);
  --border-panel: color-mix(in oklch, var(--border) 60%, var(--text-secondary) 40%);

  /* The interface and diagrams share one optimistic spectrum. The aliases
     come from the host palette so dark/light and HDR tuning stay centralized. */
  --accent: var(--accent-cyan);
  --accent-text: var(--accent-cyan);
  --accent-quiet: color-mix(in oklch, var(--accent-text) 75%, var(--text-secondary) 25%);
  --art-cyan: var(--accent-cyan);
  --art-blue: var(--accent-blue);
  --art-violet: var(--accent-purple);
  --art-pink: color-mix(in oklch, var(--accent-purple) 58%, var(--accent-red) 42%);
  --art-green: var(--accent-green);
  --art-yellow: var(--accent-yellow);

  --font-display: 'Iowan Old Style', 'Georgia', 'Times New Roman', serif;
  --font-ui: -apple-system, 'Inter', 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'SF Mono', 'Menlo', 'Consolas', monospace;

  --measure: 60ch;
  --radius: var(--radius-lg);
  --touch: var(--touch-target-min);

  --dur-fast: var(--duration-fast);
  --dur-med: var(--duration-slow);
  --ease: var(--organic-ease-soft);
  --ease-enter: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-settle: cubic-bezier(0.34, 1.22, 0.64, 1);

  /* JS-overridden by /js/nav-toggle.js once it measures the real fixed-nav
     height (same fallback pattern as port.css / login.css). */
  --nav-shell-height: clamp(4.9rem, calc(4rem + 2.4vh), 5.75rem);
}

html[data-theme='dark'] {
  --accent-text: #62e8f3;
}
html[data-theme='light'] {
  --accent-text: #006d80;
  --border-panel: color-mix(in oklch, var(--border) 60%, var(--text-primary) 20%);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background:
    radial-gradient(circle at 8% 18%, color-mix(in oklch, var(--art-cyan) 10%, transparent) 0, transparent 31rem),
    radial-gradient(circle at 92% 70%, color-mix(in oklch, var(--art-violet) 9%, transparent) 0, transparent 35rem),
    var(--bg);
  color: var(--fg);
  font-family: var(--font-ui);
  line-height: 1.55;
  overflow-x: hidden;
  transition: background-color var(--dur-med) var(--ease), color var(--dur-med) var(--ease);
}
h1, h2, h3 { font-family: var(--font-display); line-height: 1.1; margin: 0 0 var(--space-sm); }
p { margin: 0 0 var(--space-sm); }
a { color: inherit; }
code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--light-subtle);
  padding: 0.1em 0.4em;
  border-radius: var(--radius-sm);
}

.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;
}

.skip-link {
  position: absolute; left: -999px; top: 0; z-index: var(--z-tooltip, 70);
  background: var(--accent); color: #14100c; padding: 0.75rem 1.25rem;
  border-radius: 0 0 8px 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

:focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 2px;
}

.container { max-width: 1180px; margin: 0 auto; padding-inline: var(--space-md); }

/* nav { --nav-extra-offset } isn't declared by _navigation.css itself —
   landing.css sets it per-page; contextfirst needs its own value for the
   mobile flyout to sit at the right offset below the fixed nav. */
nav { --nav-extra-offset: clamp(0.75rem, 2vh, 1.5rem); }

/* ---------- View toolbar (mode toggle + Contents) ----------
   Deliberately normal document flow, not position:fixed or grid-pinned to
   an edge — three earlier consent-UI designs this session each collided
   with some page's own fixed content (see docs/ARCHITECTURE.md's Session C
   section); this sits between <nav> and <main> instead, so it can't. */
.deck-toolbar {
  display: flex; flex-wrap: wrap; gap: var(--space-sm);
  max-width: 1180px; padding-inline: var(--space-md);
  /* nav is position:fixed (see _navigation.css), out of normal flow — .deck
     already compensates with this same margin-top in its base (non-paged)
     rule; without it here, .deck-toolbar (now the first element after nav
     in document flow) would render underneath the fixed nav instead of
     below it. .js-paged overrides this back down since the grid's own
     row 1 already reserves that space. */
  margin: calc(var(--nav-shell-height) + var(--space-sm)) auto 0;
}
/* Reference mode's document can be nine viewports tall — without this, the
   toolbar (the only way back to present mode, or to open Contents) scrolls
   out of reach after the first section, and reaching it again means
   scrolling all the way back to the top first, which silently changes
   "the section being read" before the toggle is even clicked. position:
   sticky (not fixed) keeps it reachable everywhere while still
   contributing to normal document flow — no new class of collision risk,
   since it never leaves the flow the way position:fixed does. */
[data-view-mode='reference'] .deck-toolbar {
  position: sticky; top: var(--nav-shell-height); z-index: 5;
  background: color-mix(in oklch, var(--bg) 92%, transparent);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border-radius: 0 0 var(--radius) var(--radius);
  padding-block: var(--space-xs);
}
/* Author display:flex above would otherwise beat the UA stylesheet's
   [hidden]{display:none} (author origin always wins over user-agent
   origin, regardless of matching specificity) — starts hidden in markup
   (see index.html) until contextfirst.js confirms it can drive the page, so
   this has to be explicit rather than relying on the attribute alone. */
.deck-toolbar[hidden] { display: none; }
.view-toggle {
  display: inline-flex; align-items: center; gap: 0.4rem; min-height: var(--touch);
  padding: 0.4rem 0.9rem; border-radius: 999px; border: 1px solid var(--border);
  background: transparent; color: var(--fg); font-size: 0.85rem; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.view-toggle:hover, .view-toggle[aria-pressed='true'] { border-color: var(--accent); color: var(--accent-text); }

/* Toolbar button icons: same 24x24/stroke-width:2 convention as this
   file's other icons (panel-close, chat send). Sized down to sit
   comfortably next to a text label in a pill button. */
.toolbar-icon { width: 1.05rem; height: 1.05rem; flex-shrink: 0; }

/* ---------- Deck chrome: progress + dots ---------- */
.deck-chrome {
  position: relative; max-width: 1180px; margin: 0 auto var(--space-lg);
  background: color-mix(in oklch, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-xs) var(--space-md);
}
.deck-progress { position: relative; height: 4px; background: var(--border); border-radius: 999px; overflow: hidden; }
.deck-progress-bar {
  position: absolute; inset: 0; height: 100%; width: 100%;
  background: var(--prism-gradient-112); background-size: var(--prism-size);
  transform: scaleX(0.1111); transform-origin: left center;
  transition: transform 420ms var(--ease-enter); will-change: transform;
}

/* Auto-advance countdown: a separate track from .deck-progress on purpose.
   It used to share that same 4px strip as a low-opacity overlay, which
   made it read as noise on the overall progress bar rather than a
   distinct "time until next slide" cue — nothing signaled an advance was
   close. A dedicated track with a solid accent fill fixes the visibility;
   the [data-imminent] pulse in its final stretch (set by
   scheduleAutoAdvance in contextfirst.js) is the actual "about to change" cue. */
.deck-autoadvance-track {
  position: relative; height: 3px; margin-top: 0.4rem; border-radius: 999px;
  background: color-mix(in oklch, var(--border) 65%, transparent);
  overflow: hidden;
}
.deck-autoadvance-timer {
  position: absolute; inset: 0; height: 100%; width: 100%;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left center; transition: transform linear;
  will-change: transform; border-radius: inherit;
}
/* Opacity/box-shadow only — transform is already owned by the linear fill
   transition above, so animating it here would fight that transition
   instead of layering on top of it. */
.deck-autoadvance-timer[data-imminent='true'] {
  animation: deck-autoadvance-pulse 420ms ease-in-out infinite;
}
@keyframes deck-autoadvance-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 color-mix(in oklch, var(--accent) 55%, transparent); }
  50% { opacity: 0.5; box-shadow: 0 0 9px 1px color-mix(in oklch, var(--accent) 55%, transparent); }
}
.deck-dots {
  display: flex; gap: 0.35rem; list-style: none; margin: var(--space-xs) auto 0; padding: 0;
  width: 100%; min-width: 0; max-width: 1180px; overflow-x: auto; scrollbar-width: none;
}
.deck-dots::-webkit-scrollbar { display: none; }
.deck-dot {
  min-width: var(--touch); min-height: 28px; border-radius: 999px; border: 1px solid var(--border);
  background: transparent; color: var(--fg-muted); font-size: 0.75rem; cursor: pointer; flex-shrink: 0;
  padding-inline: 0.6rem; transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.deck-dot[aria-current='true'] {
  background: var(--prism-gradient-112); border-color: var(--art-cyan); color: #10151b; font-weight: 700;
  transform: scale(1.08);
}
.deck-controls {
  display: flex; align-items: center; justify-content: center; gap: var(--space-sm);
  width: fit-content; margin: var(--space-xs) auto 0;
  background: color-mix(in oklch, var(--bg-raised) 88%, transparent);
  border: 1px solid var(--border); border-radius: 999px; padding: 0.25rem 0.5rem;
}

/* Reference mode: progress/position/playback describe paging state that
   doesn't exist once every slide is already visible — hide them. The
   toolbar (mode toggle + Contents) stays visible in both modes. */
[data-view-mode='reference'] .deck-chrome { display: none; }

/* ---------- Slides ---------- */
.deck { display: block; margin-top: var(--nav-shell-height); }
.slide {
  min-height: calc(100dvh - var(--nav-shell-height));
  display: flex; align-items: center; padding-block: var(--space-xl);
  border-bottom: 1px solid var(--border);
  scroll-margin-top: calc(var(--nav-shell-height) + 3rem);
}
/* Reference mode is a normal document, not one-section-per-viewport
   paging — full-viewport min-height (needed so present mode's paging
   looks right) left most sections floating in a mostly-empty box here,
   since actual slide content is rarely a full screen tall. Content sizes
   to itself and top-aligns instead, closing that gap. */
[data-view-mode='reference'] .slide {
  min-height: 0;
  align-items: flex-start;
  padding-block: var(--space-lg);
}
.slide-inner { width: 100%; max-width: 1180px; margin: 0 auto; padding-inline: var(--space-md); }
.slide-inner-split { display: grid; gap: var(--space-lg); align-items: center; }
@media (max-width: 899px) {
  .slide-inner-split { gap: var(--space-sm); }
}
@media (min-width: 900px) {
  .slide-inner-split { grid-template-columns: 1.1fr 1fr; }
  .slide-inner-split-reverse { grid-template-columns: 1fr 1.1fr; }
  .slide-inner-split-reverse .slide-text { order: 2; }
  .slide-inner-split-reverse .slide-figure { order: 1; }
}
/* Landscape phones (e.g. 844x390): width is plentiful but height is not —
   the sub-900px stacked layout wastes the available width and stacks text
   on top of figure into a viewport too short to hold both. Switch to the
   same side-by-side layout as desktop once there's enough width to support
   it, keyed on height rather than width alone. */
@media (min-width: 640px) and (max-height: 500px) {
  .slide-inner-split { grid-template-columns: 1.1fr 1fr; gap: var(--space-md); }
  .slide-inner-split-reverse { grid-template-columns: 1fr 1.1fr; }
  .slide-inner-split-reverse .slide-text { order: 2; }
  .slide-inner-split-reverse .slide-figure { order: 1; }
  /* .illo/.illo-large overrides for this same bracket live further down
     the file (after their own base rules, near .illo-large's comment) —
     equal-specificity rules declared here would be silently overridden by
     those later base rules, so they're kept in the one place that
     actually wins the cascade rather than duplicated here. */
  .js-paged .slide-text h2 { margin-bottom: 0.35rem; }
  .js-paged .slide-text .slide-body { margin-bottom: 0.35rem; }
  .js-paged .slide-text .eyebrow { margin-bottom: 0.35rem; }
  .js-paged .trait-list { margin-top: 0.4rem; gap: 0.35rem; }
}

.eyebrow {
  font-family: var(--font-mono); font-size: 0.78rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent-quiet); margin-bottom: var(--space-sm);
}
.slide-title { font-size: clamp(1.9rem, 4.5vw, 3rem); font-weight: 600; }
.slide-title-hero {
  font-size: clamp(3.5rem, 14vw, 8rem); letter-spacing: -0.02em; font-weight: 800;
  background: var(--prism-gradient-118); background-size: var(--prism-size); background-repeat: var(--prism-repeat);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  animation: hero-breathe 8s var(--ease) infinite alternate; will-change: filter;
}
@keyframes hero-breathe {
  from { filter: saturate(1) brightness(1); }
  to { filter: saturate(1.08) brightness(1.04); }
}
.slide h1[tabindex='-1']:focus, .slide h2[tabindex='-1']:focus { outline: none; }
.slide-lede { font-size: clamp(1.2rem, 2.4vw, 1.6rem); color: var(--fg-muted); max-width: var(--measure); }
.slide-body { max-width: var(--measure); font-size: clamp(1rem, 1.2vw, 1.1rem); }
.slide-body-large { max-width: 72ch; font-size: clamp(1.15rem, 2vw, 1.5rem); }
.slide-body-callout { font-family: var(--font-display); font-style: italic; font-size: 1.2rem; color: var(--accent-quiet); }
.slide-fine { color: var(--fg-faint); font-size: 0.85rem; max-width: var(--measure); }

.trait-list { list-style: none; padding: 0; margin: var(--space-md) 0 0; display: grid; gap: 0.6rem; }
.trait-list li { padding-left: 1.4rem; position: relative; }
.trait-list li::before { content: '—'; position: absolute; left: 0; color: var(--accent-text); }

/* Slide 8 carries more citations than the rest of the deck (accuracy over
   brevity was the explicit call after review), which was enough extra
   content to push it past the paged viewport on ordinary desktop heights,
   not just short/mobile ones the rules above already handle. Tighten its
   own rhythm instead of thinning out the sourcing to make it fit. */
.js-paged #why-now .eyebrow { margin-bottom: 0.3rem; }
.js-paged #why-now .slide-title { margin-bottom: 0.4rem; font-size: clamp(1.6rem, 3.6vw, 2.4rem); }
.js-paged #why-now .slide-body { margin-bottom: 0.5rem; line-height: 1.35; }
.js-paged #why-now .trait-list { margin-top: var(--space-xs); gap: 0.3rem; }
.js-paged #why-now .trait-list li { line-height: 1.3; padding-left: 1.2rem; }

/* ---------- Buttons ---------- */
.slide-actions { margin-top: var(--space-md); display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; }
.btn {
  display: inline-flex; align-items: center; gap: 0.4rem; min-height: var(--touch);
  padding: 0.7rem 1.3rem; border-radius: 999px; font-weight: 600; text-decoration: none;
  cursor: pointer; border: 1.5px solid transparent; font-size: 0.95rem;
  transition: transform var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn-primary { background: var(--accent); color: #14100c; }
.btn-secondary { background: transparent; border-color: var(--border); color: var(--fg); }
.btn-secondary:hover { border-color: var(--accent); }

/* Primary CTA — the layered glass-shimmer treatment from _chat-cta.css,
   scaled down and reserved for the two highest-emphasis CTAs (deck open,
   GitHub source) so it reads as an accent, not wallpaper. */
.btn-glass {
  position: relative; isolation: isolate; contain: paint;
  overflow: hidden; overflow: clip;
  background: linear-gradient(140deg,
    color-mix(in oklch, var(--bg-dark) 88%, var(--art-cyan) 12%) 0%,
    color-mix(in oklch, var(--bg-dark) 76%, var(--art-blue) 24%) 55%,
    color-mix(in oklch, var(--bg-dark) 62%, var(--art-violet) 38%) 100%);
  border: 1.5px solid color-mix(in oklch, var(--art-cyan) 55%, transparent);
  box-shadow:
    0 1px 0 0 rgba(255,255,255,0.1) inset,
    0 -2px 4px 0 rgba(0,0,0,0.35) inset,
    0 0 0 1px rgba(0,0,0,0.1),
    0 8px 20px rgba(0,0,0,0.3);
  color: var(--text-primary);
  transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1), border-color 0.24s ease, box-shadow 0.32s ease;
}
.btn-glass::after {
  content: ''; position: absolute; inset: -16% -50%; border-radius: inherit;
  background: linear-gradient(108deg,
    transparent 12%, rgba(255,255,255,0.005) 24%, rgba(255,255,255,0.06) 38%,
    rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.06) 62%, rgba(255,255,255,0.005) 76%, transparent 88%);
  opacity: 0.25; pointer-events: none; z-index: 0; will-change: transform, opacity;
  animation: ctaGlassSweep 6s ease-in-out infinite alternate;
}
.btn-glass > * { position: relative; z-index: 1; }
.btn-glass:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklch, var(--art-cyan) 78%, transparent);
  box-shadow:
    0 1px 0 0 rgba(255,255,255,0.14) inset,
    0 -2px 4px 0 rgba(0,0,0,0.3) inset,
    0 0 0 1px rgba(0,0,0,0.12),
    0 12px 28px rgba(0,0,0,0.36);
}
.btn-glass:hover::after { opacity: 0.5; animation-duration: 3.6s; }

/* ---------- SVG key art ---------- */
.slide-figure {
  position: relative; display: flex; justify-content: center; min-width: 0;
}
.art-card {
  --art-card-a: var(--art-cyan);
  --art-card-b: var(--art-violet);
  isolation: isolate;
  padding: clamp(0.35rem, 1.7vw, 1.1rem);
  border: 1px solid color-mix(in oklch, var(--art-card-a) 30%, var(--border));
  border-radius: clamp(1.1rem, 2vw, 1.6rem);
  background:
    radial-gradient(circle at 24% 20%, color-mix(in oklch, var(--art-card-a) 18%, transparent), transparent 44%),
    radial-gradient(circle at 80% 78%, color-mix(in oklch, var(--art-card-b) 15%, transparent), transparent 48%),
    linear-gradient(145deg, color-mix(in oklch, var(--bg-raised) 92%, transparent), color-mix(in oklch, var(--bg) 88%, transparent));
  box-shadow:
    inset 0 1px 0 color-mix(in oklch, white 10%, transparent),
    0 22px 55px color-mix(in oklch, var(--art-card-b) 9%, transparent);
}
.art-card-product { --art-card-a: var(--art-cyan); --art-card-b: var(--art-pink); }
.art-card-convergence { --art-card-a: var(--art-green); --art-card-b: var(--art-violet); }
.art-card-loop { --art-card-a: var(--art-cyan); --art-card-b: var(--art-pink); }
.art-card-collaboration { --art-card-a: var(--art-cyan); --art-card-b: var(--art-green); }
/* max-height (not just max-width) is load-bearing: below the 900px
   two-column breakpoint, the figure stacks BELOW the slide's text — the
   two compete for the same vertical space — and without a height cap the
   diagram's natural ~320-380px intrinsic height plus the text above it
   exceeds the paged grid's available middle row at common "half a laptop
   screen" split-view widths (~600-780px). The deck-chrome row then
   visually overlaps the bottom of the diagram instead of the two ever
   being cleanly separated. The cap is deliberately more conservative
   below 900px (stacked, competing with text) than at/above it (side by
   side, text sets its own height independently) — a single cap tuned for
   one case failed the other at real narrow+short combinations. */
.illo { width: 100%; max-width: 420px; max-height: min(220px, 24dvh); height: auto; overflow: visible; color: var(--fg); }
/* .illo-large's one slide (#context) also carries an italic callout +
   button below the body text — more stacked content above the figure
   than any other split slide — so it gets a smaller cap than .illo
   despite starting from a larger source viewBox, not a larger one. */
.illo-large { max-width: 480px; max-height: min(200px, 22dvh); }
@media (min-width: 900px) {
  .illo { max-height: min(340px, 42dvh); }
  .illo-large { max-height: min(380px, 44dvh); }
}
/* Landscape phones (e.g. 844x390): declared after the base rules above so
   it actually wins the cascade (equal specificity, later source order) —
   an earlier attempt at this same bracket further up the file was
   silently dead code for exactly that reason. Tightened after the
   view-mode toolbar's own grid row (see body.js-paged) took a fixed ~44px
   out of this row's available height. */
@media (min-width: 640px) and (max-height: 500px) {
  .illo { max-height: min(95px, 17dvh); }
  .illo-large { max-height: min(75px, 13dvh); }
}
/* Extra-short phones (iPhone SE class): the two-column split still stacks
   here, and content-heavy slides (trait list, callout + button) can outrun
   the viewport even after the 899px-and-below caps above. Shrink the figure
   further and tighten text rhythm — .js-paged .slide already scrolls
   internally as the documented fallback (AGENTS.md invariant 10), so this
   is about reducing how often that fallback is needed, not chasing zero. */
@media (max-width: 420px) and (max-height: 700px) {
  .illo { max-height: min(130px, 18dvh); }
  .illo-large { max-height: min(115px, 16dvh); }
  .js-paged .slide-text h2 { margin-bottom: 0.4rem; }
  .js-paged .slide-text .slide-body { margin-bottom: 0.4rem; }
  .js-paged .slide-text .eyebrow { margin-bottom: 0.4rem; }
  .js-paged .trait-list { margin-top: 0.5rem; gap: 0.4rem; }
}
.illo :is(path, circle, rect, polygon) { vector-effect: non-scaling-stroke; }
.art-tone-cyan { --tone: var(--art-cyan); }
.art-tone-blue { --tone: var(--art-blue); }
.art-tone-violet { --tone: var(--art-violet); }
.art-tone-pink { --tone: var(--art-pink); }
.art-tone-green { --tone: var(--art-green); }
.art-tone-yellow { --tone: var(--art-yellow); }
/* Fill for gradient-painted shapes (nodes, core, bloom) comes from an
   inline fill="url(#...)" attribute in the markup, not from CSS — SVG
   presentation attributes lose to CSS class rules of any specificity, so
   these rules deliberately only set stroke/opacity/etc., never fill.
   Each gradient is a simple 2-stop diagonal sheen (light tint → full-
   chroma tone, one color-mix call, not three) — chosen over the earlier
   3-stop radial "sphere" recipe because a 3-stop gradient doesn't read
   as gradation at small sizes (the 7-9px junction dots rendered as flat
   blobs) and adds no visible benefit at large sizes either. Simpler and
   more robust, per direct feedback: fewer moving parts to get wrong. */
.art-line {
  fill: none; stroke-width: 2.6; stroke-linecap: round;
  opacity: 0.95;
}
.art-bloom { stroke: none; }
/* paint-order stroke trick: a background-colored halo behind each label so
   text stays legible where it crosses connecting lines, without needing
   individually hand-sized background rects per label. */
.node-label {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; fill: var(--fg);
  paint-order: stroke fill; stroke: var(--bg); stroke-width: 4.5px; stroke-linejoin: round;
}
.node-label-core { fill: var(--fg); font-weight: 800; font-size: 10px; }
.node-fill, .node-fill-core {
  stroke: var(--tone, var(--accent)); stroke-width: 2;
}
.node-fill-core {
  stroke-width: 2.4;
  filter: drop-shadow(0 0 14px color-mix(in oklch, var(--tone, var(--accent)) 55%, transparent));
}
.converge-core {
  stroke: var(--tone); stroke-width: 2.5; stroke-linejoin: round;
  filter: drop-shadow(0 0 12px color-mix(in oklch, var(--tone) 50%, transparent));
}
.loop-kicker, .loop-center-label {
  font-family: var(--font-mono); text-anchor: middle; fill: var(--fg-muted);
  letter-spacing: 0.16em; font-size: 9px; font-weight: 700;
}
.loop-center-label { fill: var(--accent-text); font-size: 15px; letter-spacing: 0.2em; }

/* pathLength="100" is set on every animated shape in the markup, so this
   single dasharray/offset works correctly regardless of actual geometry —
   no per-shape magic numbers, no JS getTotalLength() pass. */
[data-animate-draw] path[data-draw], [data-animate-draw] circle[data-draw],
[data-animate-draw] polygon[data-draw], [data-animate-draw] rect[data-draw] {
  stroke-dasharray: 100; stroke-dashoffset: 100; opacity: 0;
  animation: draw-in 700ms var(--ease) forwards;
  animation-delay: var(--draw-delay, 0ms);
}
[data-animate-draw] [data-pop] {
  opacity: 0; transform: scale(0.5); transform-box: fill-box; transform-origin: center;
  animation: pop-in 420ms var(--ease) forwards;
  animation-delay: var(--pop-delay, 500ms);
}
[data-animate-draw] .node-label {
  opacity: 0; animation: fadeIn 400ms ease forwards;
  animation-delay: var(--label-delay, 800ms);
}
[data-animate-draw].is-visible [data-draw],
[data-animate-draw].is-visible [data-pop],
[data-animate-draw].is-visible .node-label { animation-play-state: running; }
[data-animate-draw]:not(.is-visible) [data-draw],
[data-animate-draw]:not(.is-visible) [data-pop],
[data-animate-draw]:not(.is-visible) .node-label { animation-play-state: paused; }
@keyframes draw-in { to { stroke-dashoffset: 0; opacity: 1; } }
@keyframes pop-in { to { opacity: 1; transform: scale(1); } }

.loop-ring {
  fill: none; stroke: url(#work-loop-gradient); stroke-width: 2.2;
  stroke-dasharray: 4 7; opacity: 0.82; transform-box: fill-box; transform-origin: center;
  will-change: transform;
  animation: loop-orbit 24s linear infinite;
}
@keyframes loop-orbit { to { transform: rotate(360deg); } }

/* ---------- Evidence / QA table / inspect grid ---------- */
.evidence-grid { display: grid; gap: var(--space-md); margin-top: var(--space-lg); grid-template-columns: 1fr; }
@media (min-width: 720px) { .evidence-grid { grid-template-columns: repeat(3, 1fr); } }
.evidence-card {
  position: relative;
  background: var(--bg-raised); border: 2px solid var(--border-panel); border-radius: var(--radius);
  padding: var(--space-md); padding-top: calc(var(--space-md) + 0.6rem);
  display: flex; flex-direction: column; gap: 0.4rem;
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.evidence-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.evidence-card::before {
  content: attr(data-tag); position: absolute; top: -1px; left: var(--space-md);
  transform: translateY(-50%); background: var(--bg);
  font-family: var(--font-mono); font-size: 0.65rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--fg-faint); padding-inline: 0.4rem;
}
.evidence-card h3 { margin-bottom: 0; font-size: 1.15rem; }
.evidence-meta { font-family: var(--font-mono); font-size: 0.78rem; color: var(--accent-quiet); margin: 0; }
.evidence-link {
  margin-top: auto; font-weight: 600; text-decoration: none; padding-top: 0.5rem; display: inline-flex;
  align-items: center; gap: 0.3rem; color: var(--accent-text);
  transition: gap var(--dur-fast) var(--ease);
}
.evidence-link:hover { gap: 0.5rem; }

.qa-table { margin-top: var(--space-lg); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.qa-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); padding: 0.85rem var(--space-sm); border-top: 1px solid var(--border); font-size: 0.92rem; }
.qa-row:first-child { border-top: none; }
.qa-row-head { background: var(--light-subtle); font-weight: 700; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; }

/* Slide 6's five-row table plus a closing paragraph was enough content to
   push it past the paged viewport on ordinary desktop heights (same class
   of issue as #why-now above). Tighten this slide's own rhythm rather than
   cutting a row or the closing paragraph's content. */
.js-paged #learning .eyebrow { margin-bottom: 0.3rem; }
.js-paged #learning .slide-title { margin-bottom: 0.3rem; }
.js-paged #learning .qa-table { margin-top: var(--space-xs); }
.js-paged #learning .qa-row { padding: 0.35rem var(--space-sm); font-size: 0.85rem; }
.js-paged #learning .slide-body { margin-bottom: 0.25rem; line-height: 1.28; }
.js-paged #learning .slide-actions { margin-top: 0.4rem; }

.inspect-grid { display: grid; gap: var(--space-sm); margin-top: var(--space-lg); grid-template-columns: 1fr; }
@media (min-width: 640px) { .inspect-grid { grid-template-columns: repeat(2, 1fr); } }
.inspect-card {
  display: flex; flex-direction: column; gap: 0.2rem; text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-sm) var(--space-md);
  min-height: var(--touch); justify-content: center;
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.inspect-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.inspect-label { font-weight: 700; }
.inspect-detail { font-family: var(--font-mono); font-size: 0.8rem; color: var(--fg-muted); }

/* ---------- Deck-attached prev/next controls ---------- */
.deck-nav-btn {
  width: var(--touch); height: var(--touch); border-radius: 50%; border: none;
  background: transparent; color: var(--fg); cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background-color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.deck-nav-btn:hover { background: var(--light-subtle); }
.deck-nav-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.deck-nav-btn:disabled:hover { background: transparent; }
.deck-nav-btn svg { width: 18px; height: 18px; }
.deck-position { font-family: var(--font-mono); font-size: 0.8rem; color: var(--fg-muted); min-width: 3.5ch; text-align: center; }

/* Playback is part of the deck control group, never a floating overlay. */
.deck-playback { display: flex; }
.deck-playback-btn {
  width: calc(var(--touch) * 0.82); height: calc(var(--touch) * 0.82); border-radius: 50%;
  border: 1px solid var(--border); background: transparent; color: var(--fg);
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-md);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.deck-playback-btn:hover { border-color: var(--accent); transform: scale(1.05); }
.deck-playback-btn svg { width: 18px; height: 18px; }
.deck-playback-btn .icon-pause { display: none; }
.deck-playback[data-playing='true'] .icon-play { display: none; }
.deck-playback[data-playing='true'] .icon-pause { display: block; }

/* ---------- Transparency / survey panel (shared dialog chrome) ---------- */
.panel-overlay[hidden] { display: none; }
.panel-overlay { position: fixed; inset: 0; z-index: var(--z-modal, 60); display: flex; align-items: flex-end; }
@media (min-width: 640px) { .panel-overlay { align-items: center; justify-content: center; } }
.panel-backdrop { position: absolute; inset: 0; background: rgba(4,6,12,0.85); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.panel-dialog {
  position: relative; z-index: 1; background: var(--bg-raised); border: 2px solid var(--border-panel);
  border-radius: 16px 16px 0 0; padding: var(--space-lg) var(--space-md) var(--space-md);
  max-width: 560px; width: 100%; max-height: 85vh; overflow-y: auto;
}
@media (min-width: 640px) { .panel-dialog { border-radius: 16px; padding: var(--space-lg); } }
.panel-close {
  position: absolute; top: var(--space-sm); right: var(--space-sm);
  width: var(--touch); height: var(--touch); border-radius: 50%; border: 1px solid var(--border);
  background: transparent; color: var(--fg); cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background-color var(--dur-fast) var(--ease);
}
.panel-close:hover { background: var(--light-subtle); }
.panel-close svg { width: 16px; height: 16px; }
.transparency-list { padding-left: 1.2rem; }
.transparency-list li { margin-bottom: 0.5rem; }
.toc-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.toc-list a {
  display: block; padding: 0.6rem 0.5rem; border-radius: var(--radius-sm);
  color: var(--fg); text-decoration: none; min-height: var(--touch);
  transition: background-color var(--dur-fast) var(--ease);
}
.toc-list a:hover, .toc-list a:focus-visible { background: var(--light-subtle); }
.transparency-excluded { font-size: 0.9rem; color: var(--fg-muted); border-top: 1px solid var(--border); padding-top: var(--space-sm); }

/* Live event log — the recursive, self-referential layer */
.event-log { margin-top: var(--space-md); border-top: 1px solid var(--border); padding-top: var(--space-sm); }
.event-log-title { font-family: var(--font-mono); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-faint); margin-bottom: 0.5rem; }
.event-log-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; max-height: 180px; overflow-y: auto; }
.event-log-item {
  display: flex; justify-content: space-between; gap: 0.5rem; font-family: var(--font-mono); font-size: 0.78rem;
  padding: 0.3rem 0.5rem; border-radius: 6px; background: var(--light-subtle);
  animation: slideIn 240ms var(--ease) both;
}
.event-log-name { color: var(--accent-quiet); }
.event-log-time { color: var(--fg-faint); flex-shrink: 0; }
.event-log-empty { color: var(--fg-faint); font-size: 0.85rem; font-style: italic; }
.session-chip {
  display: inline-flex; align-items: center; gap: 0.4rem; margin-top: var(--space-sm);
  font-family: var(--font-mono); font-size: 0.72rem; color: var(--fg-faint);
  border: 1px solid var(--border); border-radius: 999px; padding: 0.25rem 0.7rem;
}
.session-chip-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

/* Survey */
.survey-question { margin-bottom: var(--space-md); }
.survey-question label { display: block; font-weight: 600; margin-bottom: 0.5rem; }
.survey-scale { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.survey-scale button {
  min-width: var(--touch); min-height: var(--touch); border-radius: 8px; border: 1px solid var(--border);
  background: transparent; color: var(--fg); cursor: pointer; font-family: var(--font-mono);
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.survey-scale button:hover, .survey-scale button[aria-pressed='true'] { border-color: var(--accent); background: color-mix(in oklch, var(--accent) 15%, transparent); }
.survey-textarea {
  width: 100%; min-height: 5rem; border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--bg); color: var(--fg); font-family: var(--font-ui); padding: 0.6rem; resize: vertical;
}
.survey-thanks { text-align: center; padding: var(--space-md) 0; }

/* ---------- Slide transitions (paged mode) ---------- */
.js-paged .slide {
  position: absolute; inset: 0; display: flex; min-height: 0; width: 100%;
  align-items: flex-start; align-items: safe center;
  overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable;
  visibility: hidden; pointer-events: none; opacity: 0;
  transform: translate3d(0, 16px, 0) scale(0.992);
  transition:
    opacity 280ms ease-in,
    transform 460ms var(--ease-settle),
    visibility 0s linear 460ms;
  contain: layout paint style;
}
.js-paged .slide.is-active {
  visibility: visible; pointer-events: auto; opacity: 1; transform: translate3d(0, 0, 0) scale(1);
  transition:
    opacity 360ms var(--ease-enter),
    transform 460ms var(--ease-settle),
    visibility 0s linear 0s;
  z-index: 2;
}
.js-paged[data-slide='intro'] #intro,
.js-paged[data-slide='posthog'] #posthog,
.js-paged[data-slide='context'] #context,
.js-paged[data-slide='how'] #how,
.js-paged[data-slide='evidence'] #evidence,
.js-paged[data-slide='learning'] #learning,
.js-paged[data-slide='humans-agents'] #humans-agents,
.js-paged[data-slide='why-now'] #why-now,
.js-paged[data-slide='inspect'] #inspect {
  visibility: visible; pointer-events: auto; opacity: 1; transform: translate3d(0, 0, 0) scale(1);
}
.js-paged .slide.is-leaving {
  visibility: visible; pointer-events: none; opacity: 0;
  transform: translate3d(0, -10px, 0) scale(0.996);
  transition:
    opacity 260ms ease-in,
    transform 360ms var(--ease-enter),
    visibility 0s linear 360ms;
  z-index: 1;
}
.js-paged[data-direction='backward'] .slide {
  transform: translate3d(0, -16px, 0) scale(0.992);
}
.js-paged[data-direction='backward'] .slide.is-active,
.js-paged[data-direction='backward'][data-slide='intro'] #intro,
.js-paged[data-direction='backward'][data-slide='posthog'] #posthog,
.js-paged[data-direction='backward'][data-slide='context'] #context,
.js-paged[data-direction='backward'][data-slide='how'] #how,
.js-paged[data-direction='backward'][data-slide='evidence'] #evidence,
.js-paged[data-direction='backward'][data-slide='learning'] #learning,
.js-paged[data-direction='backward'][data-slide='humans-agents'] #humans-agents,
.js-paged[data-direction='backward'][data-slide='why-now'] #why-now,
.js-paged[data-direction='backward'][data-slide='inspect'] #inspect {
  transform: translate3d(0, 0, 0) scale(1);
}
.js-paged[data-direction='backward'] .slide.is-leaving {
  transform: translate3d(0, 10px, 0) scale(0.996);
}
.js-paged .slide.is-active, .js-paged .slide.is-leaving {
  will-change: transform, opacity; backface-visibility: hidden;
}
/* Explicit grid-row on all four children (rather than leaving nav/toolbar
   to implicit placement) since two of the four already need explicit rows
   — mixing implicit and explicit placement in one grid is fragile. */
body.js-paged {
  display: grid;
  grid-template-rows: var(--nav-shell-height) auto minmax(0, 1fr) auto;
  height: 100dvh; min-height: 100svh; overflow: hidden;
}
/* The footer is a 5th in-flow body child with no explicit grid-row —
   relying on it auto-placing into an implicit row 5 (and getting
   clipped by the overflow:hidden above) turned out to be unreliable in
   practice, landing it back in row 1 instead. Hide it outright in
   paged mode instead of depending on that; reference mode drops
   .js-paged entirely, so the footer returns to normal flow and shows
   at the true end of the (now scrollable) document — the ai-lab/chat
   pattern this is matching only ever needs it reachable there. */
.js-paged .site-footer { display: none; }
.js-paged nav { grid-row: 1; }
/* Zero margin here — row 2's height is the toolbar's own content box only
   (its buttons already carry the required 44px touch target, see AGENTS.md
   "Accessibility expectations"; that floor can't shrink, so this is the
   only slack available to reclaim in the fixed-height paged grid). */
.js-paged .deck-toolbar { grid-row: 2; margin-block: 0; }
.js-paged .deck {
  position: relative; grid-row: 3; display: block; height: auto;
  margin-top: 0; min-height: 0; overflow: hidden; isolation: isolate; scroll-behavior: auto;
}
.js-paged .deck-chrome {
  grid-row: 4; inline-size: min(100%, 1180px); block-size: auto; margin: 0 auto;
  min-inline-size: 0; padding-block-end: max(var(--space-xs), env(safe-area-inset-bottom));
}
@media (max-height: 720px) {
  /* Tightened from --space-md after the toolbar's new grid row (above)
     took ~44px out of this row's available height — reclaims the rest of
     that budget on the shortest phone viewports (verified against
     "split-layout diagrams never overlap the deck controller"). */
  .js-paged .slide { padding-block: var(--space-xs); }
}

/* ---------- Easter egg ---------- */
.egg-hedgehog {
  position: fixed; bottom: 10%; left: -60px; width: 42px; height: 30px; z-index: var(--z-tooltip, 70);
  pointer-events: none; color: var(--accent-text);
  animation: egg-scurry 3.2s ease-in-out forwards;
}
@keyframes egg-scurry {
  0%   { left: -60px; transform: scaleX(1); }
  48%  { left: calc(100% + 60px); transform: scaleX(1); }
  50%  { left: calc(100% + 60px); transform: scaleX(-1); }
  100% { left: -60px; transform: scaleX(-1); }
}
.egg-badge {
  position: fixed; bottom: var(--space-md); left: 50%; transform: translateX(-50%) translateY(8px);
  background: var(--bg-raised); border: 2px solid var(--accent); border-radius: 999px;
  padding: 0.5rem 1.1rem; font-family: var(--font-mono); font-size: 0.78rem;
  z-index: var(--z-tooltip, 70); opacity: 0; animation: egg-badge-in 400ms var(--ease) 2.6s forwards;
  box-shadow: var(--shadow-lg);
}
@keyframes egg-badge-in { to { opacity: 1; transform: translateX(-50%) translateY(0); } }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
  [data-animate-draw] [data-draw], [data-animate-draw] [data-pop], [data-animate-draw] .node-label {
    stroke-dasharray: none; stroke-dashoffset: 0; opacity: 1; transform: none; animation: none;
  }
  .slide-title-hero { animation: none; background-position: 50% 50%; }
  .js-paged .slide { transform: none; }
  .loop-ring { animation: none; }
  .egg-hedgehog { animation: none; left: 50%; transform: translateX(-50%); }
}
.reduced-motion * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
.reduced-motion [data-animate-draw] [data-draw], .reduced-motion [data-animate-draw] [data-pop],
.reduced-motion [data-animate-draw] .node-label {
  stroke-dasharray: none; stroke-dashoffset: 0; opacity: 1; transform: none;
}
.reduced-motion .slide-title-hero { animation: none; background-position: 50% 50%; }

/* ---------- Standard site footer (same component every other page
   uses — see hire-me.css/blog.css for the pattern this is adapted
   from, using this file's own --fg/--bg/--border aliases instead of
   redeclaring the underlying tokens). In present/paged mode this
   becomes an unplaced grid item under body.js-paged's overflow:hidden
   (see the grid-template-rows rule below) — invisible until reference
   mode's normal scrollable flow reaches it. ---------- */
.site-footer { padding: 2rem 0; border-top: 1px solid var(--border); }
.site-footer .footer-content {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: var(--space-md); margin-bottom: var(--space-sm);
}
.site-footer .footer-links { display: flex; flex-wrap: wrap; gap: var(--space-md); }
.site-footer .footer-links a { color: var(--fg-muted); text-decoration: none; font-size: 0.85rem; }
.site-footer .footer-links a:hover { color: var(--accent-text); }
.site-footer .footer-meta { text-align: center; }
.site-footer .footer-copy { color: var(--fg-faint); font-size: 0.8rem; margin: 0; }

.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  width: 2.625rem; height: 2.625rem; padding: 0; border-radius: 999px;
  border: 1px solid var(--border); background: rgba(15, 23, 42, 0.35);
  color: var(--fg-muted); cursor: pointer;
}
html[data-theme="light"] .theme-toggle { background: rgba(255, 255, 255, 0.8); }
.theme-toggle:hover { color: var(--fg); border-color: var(--accent-text); }
.theme-toggle-icon { display: inline-flex; position: relative; width: 1.125rem; height: 1.125rem; }
.theme-toggle-icon svg {
  position: absolute; inset: 0; width: 1.125rem; height: 1.125rem;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.theme-toggle-icon .icon-sun { opacity: 0; transform: rotate(-90deg); }
html[data-theme="light"] .theme-toggle-icon .icon-sun { opacity: 1; transform: rotate(0deg); }
html[data-theme="light"] .theme-toggle-icon .icon-moon { opacity: 0; transform: rotate(90deg); }

/* ---------- Chat panel ("Ask about PostHog fit") ----------
   Styles the DOM public/js/components/chat-transcript.js renders
   (message bubbles, avatars, content, thinking panel, response badge,
   copy button) using this deck's own tokens, not ai-lab-chat.css's --
   this panel reuses the shared chat-transcript.js module, not its
   sibling stylesheet, so nothing here needs to match ai-lab's visual
   language. See docs/CHAT_ARCHITECTURE.md in the benlive repo: theming
   is CSS against the module's namespaced class hooks, by design. */
.panel-dialog-chat {
  max-width: 640px;
  display: flex;
  flex-direction: column;
  max-height: 88vh;
}
.chat-panel-intro { color: var(--fg-muted); font-size: 0.92rem; }
.chat-panel-privacy-note { color: var(--fg-muted); font-size: 0.78rem; margin-top: -0.35rem; }
.chat-panel-privacy-note a { color: inherit; text-decoration: underline; }
.chat-starters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: var(--space-sm); }
.chat-starter {
  display: inline-flex; align-items: center; min-height: var(--touch);
  border: 1px solid var(--border); border-radius: 999px; background: var(--light-subtle);
  color: var(--fg); font-size: 0.82rem; padding: 0.45rem 0.85rem; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.chat-starter:hover, .chat-starter:focus-visible { border-color: var(--accent); background: var(--light-subtle-hover, var(--light-subtle)); }
.chat-starters[hidden] { display: none; }

.chat-messages {
  flex: 1; min-height: 220px; max-height: 48vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--space-sm);
  padding: var(--space-sm) 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-sm);
}
.chat-messages .message { display: flex; gap: 0.6rem; align-items: flex-start; }
.chat-messages .message.user { flex-direction: row-reverse; }
.chat-messages .avatar {
  flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--light-subtle); border: 1px solid var(--border); position: relative;
}
.chat-messages .avatar-icon { width: 16px; height: 16px; color: var(--fg-muted); }
.chat-messages .avatar-tier-icon { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.chat-messages .content {
  max-width: 78%; background: var(--light-subtle); border: 1px solid var(--border);
  border-radius: 12px; padding: 0.6rem 0.8rem; font-size: 0.92rem; line-height: 1.5;
}
.chat-messages .message.user .content { background: var(--accent-quiet); color: var(--bg); border-color: transparent; }
.chat-messages .message.assistant.error .content { border-color: color-mix(in oklch, var(--error, #e5484d) 60%, var(--border) 40%); }
.chat-messages .content p:last-child { margin-bottom: 0; }
.chat-messages .content code { font-family: var(--font-mono); font-size: 0.88em; }

.chat-messages .thinking-section { margin-top: 0.6rem; border-top: 1px solid var(--border); padding-top: 0.5rem; }
.chat-messages .thinking-toggle {
  display: flex; align-items: center; gap: 0.4rem; color: var(--fg-faint); font-size: 0.8rem;
  cursor: pointer; user-select: none; background: none; border: none; padding: 0; margin: 0; font-family: inherit; text-align: left;
}
.chat-messages .thinking-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.chat-messages .thinking-toggle svg { width: 12px; height: 12px; transition: transform var(--dur-fast) var(--ease); }
.chat-messages .thinking-toggle.expanded svg { transform: rotate(90deg); }
.chat-messages .thinking-content {
  max-height: 0; overflow: hidden; font-size: 0.8rem; color: var(--fg-muted);
  font-family: var(--font-mono); white-space: pre-wrap; transition: max-height var(--dur-normal, 0.3s) ease-out;
}
.chat-messages .thinking-content.expanded { max-height: 320px; overflow-y: auto; margin-top: 0.4rem; }

.chat-messages .response-badge { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-top: 0.5rem; }
.chat-messages .model-badge { display: flex; align-items: center; gap: 0.35rem; font-size: 0.75rem; color: var(--fg-faint); }
.chat-messages .model-icon svg { width: 14px; height: 14px; }
.chat-messages .copy-button {
  width: var(--touch); height: var(--touch); border-radius: 50%; border: 1px solid var(--border); background: transparent;
  color: var(--fg-muted); cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background-color var(--dur-fast) var(--ease);
}
.chat-messages .copy-button:hover { background: var(--light-subtle); }
.chat-messages .copy-button svg { width: 14px; height: 14px; }
.chat-messages .copy-button.is-success { border-color: var(--accent); color: var(--accent); }
.chat-messages .copy-button.is-error { border-color: color-mix(in oklch, var(--error, #e5484d) 60%, var(--border) 40%); }
.chat-messages .typing { display: flex; gap: 0.25rem; padding: 0.3rem 0; }
.chat-messages .typing span {
  width: 6px; height: 6px; border-radius: 50%; background: var(--fg-faint);
  animation: chatTypingPulse 1.1s ease-in-out infinite;
}
.chat-messages .typing span:nth-child(2) { animation-delay: 0.15s; }
.chat-messages .typing span:nth-child(3) { animation-delay: 0.3s; }
@keyframes chatTypingPulse { 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

.chat-form { display: flex; gap: 0.5rem; }
.chat-form input {
  flex: 1; min-height: var(--touch); border: 1px solid var(--border); border-radius: 999px;
  background: var(--bg); color: var(--fg); padding: 0 1rem; font-size: 0.92rem;
}
.chat-form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chat-form input:disabled { opacity: 0.6; }
.chat-form button {
  flex-shrink: 0; width: var(--touch); height: var(--touch); border-radius: 50%; border: none;
  background: var(--accent); color: var(--bg); cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: opacity var(--dur-fast) var(--ease);
}
.chat-form button svg { width: 18px; height: 18px; }
.chat-form button:disabled { opacity: 0.5; cursor: default; }

.chat-panel-footer { margin-top: var(--space-sm); text-align: right; }
.chat-reset {
  display: inline-flex; align-items: center; min-height: var(--touch);
  border: none; background: none; color: var(--fg-faint); font-size: 0.78rem; cursor: pointer;
  padding: 0.3rem 0.6rem; text-decoration: underline; text-underline-offset: 2px;
}
.chat-reset:hover, .chat-reset:focus-visible { color: var(--fg-muted); }

@media (prefers-reduced-motion: reduce) {
  .chat-messages .typing span { animation: none; opacity: 0.6; }
}

