/* =============================================================
   Agent Cohort - Landing site
   Hand-written, framework-free. Mirrors the app's Cool Slate palette
   so the marketing surface and the product feel like one product.
   ============================================================= */

:root {
  /* Surfaces */
  --bg:           #0a0a0f;
  --bg-2:         #0e0e15;
  --surface:      #14141c;
  --surface-2:    #1a1a23;
  --surface-3:    #21212c;
  --border:       #24242f;
  --border-2:     #2e2e3a;
  --border-soft:  rgba(255, 255, 255, 0.06);

  /* Text */
  --text:         #ecedf2;
  --text-2:       #b6b8c4;
  --text-3:       #888a98;
  --text-muted:   #5d5f6c;

  /* Accent - Cool Slate (the app's default palette) */
  --accent:       #5cc1d4;
  --accent-2:     #7adff2;
  --accent-soft:  rgba(92, 193, 212, 0.12);
  --accent-glow:  rgba(92, 193, 212, 0.35);

  /* Secondary accents for chips / status */
  --orange:       #e89968;
  --green:        #7ab36a;
  --amber:        #d9a050;
  --red:          #d77780;
  --blue:         #82a8d8;

  /* Type */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI Variable', 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', Consolas, ui-monospace, monospace;
  /* Brand wordmark - the same Raffishly OTF the dock app and the login
     SPA ship (`shared/src/renderer/src/assets/fonts/Raffishly.otf`,
     `login/src/assets/fonts/Raffishly.otf`). Commercial license per
     `shared/src/renderer/src/assets/fonts/LICENSE-fonts.md` (purchased
     2026-05-21) explicitly covers web distribution as part of the
     same paid product, so embedding it on the marketing surface is
     within scope. Fallback stack mirrors the dock's so a mid-load
     swap doesn't reflow dramatically. */
  --font-brand: 'Raffishly', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* Layout */
  --container: 1200px;
  --radius: 12px;
  --radius-lg: 16px;
  --radius-sm: 8px;

  /* Easing */
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Brand wordmark @font-face. `font-display: swap` so the brand text
   renders in the fallback stack first and re-paints when Raffishly
   arrives - tiny FOUT on the wordmark, but the rest of the page paints
   on time even on a slow connection. Same recipe the login SPA uses. */
@font-face {
  font-family: 'Raffishly';
  src: url('fonts/Raffishly.otf') format('opentype');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }

/* Restore the HTML `hidden` attribute's effect across the site.
   Without this rule, any element with an explicit `display:` (flex,
   grid, inline-block, etc.) overrides the user-agent default
   `display: none` that `hidden` sets — which made e.g. plan cards
   marked `hidden` still render, and discount badges with `hidden`
   still show as empty pills. */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px;
  -webkit-text-size-adjust: 100%;
  text-rendering: optimizeLegibility;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, svg { display: block; max-width: 100%; }

a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.15s var(--ease);
}
a:hover { color: var(--accent-2); }

code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: rgba(255, 255, 255, 0.045);
  padding: 0.12em 0.42em;
  border-radius: 4px;
  color: #d8e7eb;
  border: 1px solid var(--border-soft);
}

pre {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.65;
  overflow-x: auto;
}

pre code {
  background: none;
  border: none;
  padding: 0;
  border-radius: 0;
  color: inherit;
  font-size: inherit;
}

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--text);
  line-height: 1.18;
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

h1 { font-size: clamp(2.2rem, 5vw, 3.6rem); letter-spacing: -0.025em; }
h2 { font-size: clamp(1.65rem, 3.2vw, 2.2rem); }
h3 { font-size: 1.05rem; }
h4 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-3); }

p { margin: 0; color: var(--text-2); }

.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  /* Longhand padding so this rule never wipes vertical padding on
     elements that use both `class="container X"` (shorthand on either
     side resets all four edges per spec). */
  padding-left: 24px;
  padding-right: 24px;
}

/* ====== Skip link ====== */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  padding: 8px 12px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  z-index: 100;
}
.skip-link:focus { left: 8px; }

/* ====== Header ====== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  background: rgba(10, 10, 15, 0.72);
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.site-header.is-scrolled {
  border-bottom-color: var(--border);
  background: rgba(10, 10, 15, 0.92);
}

.nav-row {
  display: flex;
  align-items: center;
  gap: 24px;
  height: 64px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.brand:hover { color: var(--text); }

/* Logo placeholder - a 2x2 dot grid that nods at the dock layout */
.brand-mark {
  position: relative;
  width: 28px;
  height: 28px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 3px;
  padding: 4px;
  background: linear-gradient(135deg, var(--surface-2), var(--surface));
  border: 1px solid var(--border);
  border-radius: 7px;
}
.brand-mark-dot {
  background: var(--accent);
  border-radius: 2px;
  opacity: 0.85;
}
.brand-mark-dot:nth-child(1) { opacity: 1; box-shadow: 0 0 8px var(--accent-glow); }
.brand-mark-dot:nth-child(2) { opacity: 0.65; }
.brand-mark-dot:nth-child(3) { opacity: 0.5; }
.brand-mark-dot:nth-child(4) { opacity: 0.8; }

.brand-name { font-size: 15px; }

.primary-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.primary-nav a {
  color: var(--text-2);
  font-size: 14px;
  font-weight: 500;
  padding: 8px 12px;
  border-radius: 6px;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.primary-nav a:hover { color: var(--text); background: var(--surface-2); }
.primary-nav a[aria-current='page'] { color: var(--accent); }

.nav-github {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 4px;
  border: 1px solid var(--border) !important;
  background: var(--surface);
}
.nav-github:hover { background: var(--surface-2) !important; border-color: var(--border-2) !important; }

/* Sign-in is a utility-tier nav link — quieter than the primary nav
   items so it reads as "secondary action" without becoming a button.
   Lives at the rightmost of the nav next to the GitHub Releases icon.
   Hover lifts it to the same color the other primary-nav links use,
   so it doesn't feel disabled. */
.primary-nav a.nav-signin,
.mobile-nav a.nav-signin {
  color: var(--text-3);
}
.primary-nav a.nav-signin:hover {
  color: var(--text);
}

.nav-toggle {
  display: none;
  margin-left: auto;
  width: 38px;
  height: 38px;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: 8px;
  cursor: pointer;
  padding: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.nav-toggle span {
  display: block;
  width: 16px;
  height: 1.5px;
  background: var(--text-2);
  border-radius: 1px;
  transition: transform 0.2s var(--ease);
}

/* Mobile nav drawer
   - Always rendered so we can transition max-height + opacity.
   - Hidden on desktop via display:none in the media block at the bottom. */
.mobile-nav {
  display: flex;
  flex-direction: column;
  padding: 0 24px;
  background: var(--bg);
  border-top: 1px solid transparent;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 0.3s var(--ease), opacity 0.18s var(--ease), padding 0.3s var(--ease), border-color 0.18s var(--ease);
}
.mobile-nav a {
  color: var(--text-2);
  padding: 14px 4px;
  font-weight: 500;
  font-size: 15px;
  border-bottom: 1px solid var(--border-soft);
}
.mobile-nav a:last-child { border-bottom: none; }
.mobile-nav.is-open {
  max-height: 70vh;
  opacity: 1;
  padding: 8px 24px 18px;
  border-top-color: var(--border);
}

/* Hamburger animates into an X when open */
.nav-toggle.is-open span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-toggle.is-open span:nth-child(2) { opacity: 0; }
.nav-toggle.is-open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
.nav-toggle span { transition: transform 0.22s var(--ease), opacity 0.18s var(--ease); }

/* Lock body scroll while the drawer is open */
body.nav-open { overflow: hidden; }

/* Hide the drawer entirely on desktop */
@media (min-width: 721px) {
  .mobile-nav { display: none; }
}

/* ====== Buttons ====== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 20px;
  border-radius: 8px;
  font-weight: 500;
  font-size: 14px;
  font-family: var(--font-sans);
  cursor: pointer;
  border: 1px solid transparent;
  transition: all 0.15s var(--ease);
  white-space: nowrap;
}
.btn-primary {
  background: linear-gradient(180deg, var(--accent) 0%, #4ab3c8 100%);
  color: #0a1518;
  border-color: rgba(255, 255, 255, 0.12);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.15) inset,
    0 8px 22px -10px var(--accent-glow);
}
.btn-primary:hover {
  background: linear-gradient(180deg, var(--accent-2) 0%, var(--accent) 100%);
  transform: translateY(-1px);
  color: #0a1518;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.2) inset,
    0 12px 30px -10px var(--accent-glow);
}
.btn-ghost {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}
.btn-ghost:hover {
  background: var(--surface-2);
  color: var(--text);
  border-color: var(--border-2);
}

/* ====== Eyebrow + section heads ====== */
.eyebrow {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid rgba(92, 193, 212, 0.18);
}

.section-head {
  max-width: 720px;
  margin: 0 auto 48px;
  text-align: center;
}
.section-head .eyebrow { margin-bottom: 14px; }
.section-head h2 { margin-bottom: 14px; }
.section-head p { color: var(--text-2); }

.section-head-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  text-align: left;
  max-width: none;
  gap: 24px;
  margin-bottom: 36px;
}
.section-head-row .eyebrow { margin-bottom: 12px; }

.section-head-center { text-align: center; }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  font-weight: 500;
  color: var(--accent);
  white-space: nowrap;
}
.link-arrow:hover svg { transform: translateX(3px); }
.link-arrow svg { transition: transform 0.18s var(--ease); }

.grad {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 50%, #b1eaf2 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* ====== Hero ====== */
.hero {
  position: relative;
  padding: clamp(28px, 5vw, 60px) 0 clamp(48px, 7vw, 80px);
  overflow: hidden;
}
.hero-bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(900px 500px at 80% -10%, rgba(92, 193, 212, 0.10), transparent 70%),
    radial-gradient(700px 400px at 0% 30%, rgba(122, 223, 242, 0.06), transparent 70%);
  z-index: 0;
}
.hero-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(92, 193, 212, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(92, 193, 212, 0.04) 1px, transparent 1px);
  background-size: 60px 60px;
  mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black, transparent 75%);
  opacity: 0.5;
}

/* Hero is a vertical stack: copy on top (centered, constrained width),
   the app-mock visual sits below at full container width so it can
   show the product in real detail without compressing the headline. */
.hero-stack {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(40px, 6vw, 72px);
}

.hero-copy {
  max-width: 760px;
  text-align: center;
  min-width: 0;
}
.hero-copy .hero-sub {
  margin-left: auto;
  margin-right: auto;
}
.hero-copy .cta-row { justify-content: center; }
.hero-copy .hero-meta { justify-content: center; }
/* Keep .hero-title .grad as block so "Stay in flow." sits on its own line. */

.hero-copy .eyebrow { margin-bottom: 22px; }

.hero-title { margin-bottom: 22px; }
.hero-title .grad { display: block; }

.hero-sub {
  font-size: clamp(15px, 1.4vw, 17px);
  color: var(--text-2);
  margin-bottom: 28px;
  max-width: 540px;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 28px;
}

.hero-meta {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
}
.hero-meta li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-3);
  font-size: 13px;
}
.dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
  flex-shrink: 0;
}

/* ====== Window mock (hero visual) ====== */
.hero-visual {
  position: relative;
  perspective: 2000px;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
}
/* =============================================================
   App mock (hero visual) - mirrors the real Agent Cohort UI
   so the marketing surface looks like the product. Structure:
   .am-header (project chip + agent picker + plugin toolbar +
   search + window controls), .am-grid (1 large focused term +
   2 stacked terms), .am-statusbar.
   ============================================================= */

.app-mock {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  background: var(--bg-2);
  border: 1px solid var(--border);
  box-shadow:
    0 30px 80px -20px rgba(0, 0, 0, 0.7),
    0 0 0 1px rgba(255, 255, 255, 0.03),
    0 18px 60px -28px var(--accent-glow);
  transform: rotate3d(0, 1, 0, -1.5deg) rotate3d(1, 0, 0, 0.5deg);
  transform-origin: center;
  transition: transform 0.4s var(--ease);
  font-family: var(--font-sans);
}
.app-mock:hover { transform: rotate3d(0, 1, 0, 0deg); }

/* ---- Header ---- */
.am-header {
  position: relative;
  height: 40px;
  padding: 0 0 0 10px;
  display: flex;
  align-items: center;
  gap: 7px;
  background: var(--surface);
  flex-shrink: 0;
}
.am-header::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent 0%, var(--accent) 18%, var(--accent) 82%, transparent 100%);
  opacity: 0.55;
}
.am-icon {
  width: 22px; height: 22px;
  border-radius: 6px;
  flex-shrink: 0;
  object-fit: cover;
  /* Source PNG already paints its own dark backdrop + accent peaks; the
     subtle outer ring + glow keep parity with the production AppLogo
     (.dock-header-app-icon-glow) without competing with the artwork. */
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.08),
    0 1px 2px rgba(0, 0, 0, 0.45),
    0 0 14px -2px var(--accent-glow);
  user-select: none;
  -webkit-user-drag: none;
}
.am-divider {
  width: 1px; height: 20px;
  background: var(--border);
  margin: 0 1px;
  flex-shrink: 0;
}

/* New-terminal "+" button. Mirrors `.dock-header-btn-primary` in the
   production DockShell.css: accent-tinted background + inset accent
   border, ~28px-tall pill. The terminal-spawn demo (assets/script.js)
   animates a pulse ring around this button when the cursor clicks it. */
.am-add-term {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 22px;
  padding: 0;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  border: none;
  border-radius: 6px;
  cursor: default;
  flex-shrink: 0;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
  /* Block native focus rings - this button is visual-only in the
     marketing surface; it never receives keyboard focus. */
  outline: none;
}
.am-add-term:focus-visible {
  /* Decorative; should never actually focus, but be safe. */
  outline: none;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 60%, transparent),
              0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent);
}

.am-project {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 26px;
  padding: 0 10px 0 9px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 12px;
  color: var(--text);
  flex-shrink: 0;
}
.am-accent-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25) inset, 0 0 8px var(--accent-glow);
}
.am-project-name { font-weight: 500; }
.am-project-sub { color: var(--text-3); font-size: 11px; }
.am-chev { width: 10px; height: 10px; color: var(--text-3); margin-left: 1px; }

/* Agent picker */
.am-agents {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  flex-shrink: 0;
}
.am-agent {
  height: 20px;
  padding: 0 9px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-2);
  border-radius: 4px;
}
.am-agent.active {
  background: var(--bg-2);
  color: var(--text);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25), inset 0 0 0 1px rgba(255, 255, 255, 0.03);
}
.am-agent-icon {
  width: 10px; height: 10px;
  border-radius: 2px;
  flex-shrink: 0;
}
.am-agent-icon.claude { background: linear-gradient(135deg, #e89968, #c97144); }
.am-agent-icon.codex  { background: linear-gradient(135deg, #25d491, #128a5b); }
.am-agent-icon.shell  { background: linear-gradient(135deg, #888, #555); }

/* Plugin toolbar */
.am-plugins {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  height: 28px;
  padding: 1px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  flex-shrink: 0;
}
.am-plugin {
  position: relative;
  width: 24px; height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-3);
  border-radius: 5px;
}
.am-plugin svg {
  width: 13px; height: 13px;
  stroke-width: 1.5;
}
.am-plugin.active {
  background: var(--accent-soft);
  color: var(--accent);
}
.am-plugin-count {
  position: absolute;
  top: -2px; right: -2px;
  min-width: 13px; height: 12px;
  padding: 0 3px;
  background: var(--accent);
  color: #0a1518;
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 1.5px var(--bg-2);
}
.am-plugin-dot {
  position: absolute;
  top: 4px; right: 4px;
  width: 5px; height: 5px;
  border-radius: 50%;
  box-shadow: 0 0 0 1.5px var(--bg-2);
}
.am-plugin-dot.ok { background: var(--green); }

.am-spacer { flex: 1; min-width: 6px; }

/* Terminal Spaces strip - mirrors src/renderer/.../SpacesStrip.css.
   Recessed dark "track" with pill-style tabs. */
.am-spaces {
  display: inline-flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
  height: 28px;
  margin: 0 4px;
  padding: 2px 4px;
  gap: 2px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25);
  overflow: hidden;
}
.am-space {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 20px;
  padding: 0 8px 0 10px;
  border-radius: 4px;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--text-3);
  white-space: nowrap;
  flex-shrink: 0;
}
.am-space.active {
  background: var(--surface-2);
  color: var(--text);
  box-shadow: inset 2px 0 0 0 var(--accent);
}
.am-space-default { padding-right: 14px; }
.am-space-dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 4px rgba(122, 179, 106, 0.55);
}
.am-space-icon { font-size: 11px; line-height: 1; }
.am-space-name { font-size: 10.5px; }
.am-space-add {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  color: var(--text-3);
  font-size: 14px;
  font-weight: 400;
  flex-shrink: 0;
}

.am-search {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 26px;
  padding: 0 9px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-3);
  font-size: 11.5px;
  flex-shrink: 0;
}
.am-search svg { width: 11px; height: 11px; }
.am-search-text { display: inline-block; }
.am-kbd {
  margin-left: 2px;
  font-size: 10px;
  font-family: var(--font-mono);
  color: var(--text-3);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 1px 5px;
}

.am-win-controls { display: flex; align-self: stretch; margin-left: 4px; flex-shrink: 0; }
.am-win-btn {
  width: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-3);
}
.am-win-btn svg { width: 9px; height: 9px; }

/* ---- Terminal grid ---- */
.am-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 6px;
  padding: 6px;
  background: var(--bg-2);
  height: 360px;
}
.am-term-large { grid-row: 1 / 3; }

.am-term {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  overflow: hidden;
  position: relative;
}
.am-term.focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 8px 24px rgba(0, 0, 0, 0.35);
}

/* Small mode - matches the app's compact terminal header tier */
.am-term-header {
  height: 22px;
  padding: 0 8px;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: 10.5px;
  flex-shrink: 0;
}
.am-agent-badge {
  width: 12px; height: 12px;
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 8px;
  font-weight: 700;
  color: white;
  flex-shrink: 0;
  letter-spacing: -0.02em;
  font-family: var(--font-sans);
}
.am-agent-badge.claude { background: linear-gradient(135deg, #e89968, #c97144); }
.am-agent-badge.codex  { background: linear-gradient(135deg, #25d491, #128a5b); }
.am-agent-badge.shell  { background: linear-gradient(135deg, #777, #444); }

.am-term-title { color: var(--text); font-weight: 500; }
.am-term-meta { color: var(--text-3); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.am-term-spacer { flex: 1; }
.am-term-status {
  width: 7px; height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}
.am-term-status.idle { background: var(--text-muted); }
.am-term-status.active {
  background: var(--green);
  box-shadow: 0 0 0 3px rgba(122, 179, 106, 0.18);
  animation: am-pulse 1.6s ease-in-out infinite;
}
.am-term-status.attention { background: var(--amber); box-shadow: 0 0 0 3px rgba(217, 160, 80, 0.18); }

@keyframes am-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

.am-term-body {
  flex: 1;
  padding: 10px 12px;
  background: #0a0c0f;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.6;
  color: #cfd6dc;
  overflow: hidden;
  white-space: pre-wrap;
  word-break: break-word;
}
.am-term-body .am-accent { color: var(--accent); }
.am-term-body .am-path   { color: #82a8d8; }
.am-term-body .am-ok     { color: var(--green); }
.am-term-body .am-err    { color: var(--red); }
.am-term-body .am-warn   { color: var(--amber); }
.am-term-body .am-dim    { color: #6b6b6b; }
.am-term-body .am-hl     { color: #f0e0c2; }
.am-term-body .am-key    { color: #c49bd6; }
.am-term-body .am-str    { color: #98c379; }
.am-cursor {
  display: inline-block;
  width: 6px; height: 13px;
  background: var(--accent);
  vertical-align: -2px;
  animation: am-blink 1.1s steps(2, start) infinite;
}
@keyframes am-blink { 50% { opacity: 0; } }

/* ---- Status bar ---- */
.am-statusbar {
  height: 24px;
  padding: 0 12px;
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--surface);
  border-top: 1px solid var(--border);
  font-size: 10.5px;
  color: var(--text-3);
  flex-shrink: 0;
}
.am-status-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.am-status-item svg { width: 10px; height: 10px; }
.am-status-ok svg { color: var(--green); }
.am-status-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 6px var(--accent-glow);
}
.am-status-spacer { flex: 1; }

/* Randomize toggle. Sits at the right edge of the statusbar; flips
   the orchestrator's order between "sequence" and "random" via the
   public CohortHero.configure() API. JS sets aria-pressed + a state
   class to reveal the correct icon. */
.am-shuffle-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 18px;
  padding: 0;
  margin-left: 2px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--text-3);
  cursor: pointer;
  outline: none;
  transition: color 100ms ease, background-color 100ms ease, border-color 100ms ease;
  position: relative;
}
.am-shuffle-toggle:hover {
  color: var(--text-2);
  background: color-mix(in srgb, var(--text-3) 12%, transparent);
}
.am-shuffle-toggle:focus-visible {
  border-color: var(--accent);
  color: var(--text);
}
/* When randomize is ON, light the toggle accent + show the shuffle
   icon (and hide the sequence one). Default (off) shows sequence. */
.am-shuffle-toggle[aria-pressed="true"] {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}
.am-shuffle-toggle .am-shuffle-icon { display: none; }
.am-shuffle-toggle .am-sequence-icon { display: inline-block; }
.am-shuffle-toggle[aria-pressed="true"] .am-shuffle-icon { display: inline-block; }
.am-shuffle-toggle[aria-pressed="true"] .am-sequence-icon { display: none; }

/* =============================================================
   App-mock animation system - "hero demos"
   -----------------------------------------------------------------
   The hero mock plays a registry of demos one at a time. Each demo
   is a self-contained overlay markup + CSS scope + JS hook:

     - HTML: <div class="am-demo am-demo-<id>" data-demo-id="<id>" ...>
             inside .app-mock. Multiple demos can coexist as siblings.

     - CSS: animations gated on `.am-demo-<id>.is-playing X` for
             elements INSIDE the demo wrapper, and on
             `.app-mock[data-active-demo="<id>"].is-anim-playing X`
             for effects that fire on elements OUTSIDE the wrapper
             (e.g. the focused terminal flash for the browser-plugin
             demo). Per-demo timing tokens live on the wrapper rule
             so designers can retune one demo without touching the
             others.

     - JS: register with `CohortHero.register('<id>', { duration,
             onMeasure, onPlay })`. The orchestrator handles initial
             delay, ordering (sequence / random / shuffle), pausing
             on visibility / IO / breakpoint / motion changes, and
             cycle teardown.

   The first demo is "browser-plugin" - it mirrors
   shared/plugins/browser/renderer/BrowserPanel.tsx so the marketing
   surface and the product read as one product.

   Accessibility:
   - aria-hidden="true" on every demo wrapper (decorative).
   - prefers-reduced-motion: reduce hides all demos.
   ============================================================= */

/* ─── Demo base (applies to every registered demo) ─────────────── */

.am-demo {
  position: absolute;
  pointer-events: none;
  font-family: var(--font-sans);
  opacity: 0;
  visibility: hidden;
  /* `transition` smooths the orchestrator's activate/deactivate
     state switches. CSS keyframes inside the demo handle the
     in-cycle animation; this transition is only for the wrapper. */
  transition: opacity 200ms var(--ease);
  /* Above the static terminals but below any popovers / tooltips. */
  z-index: 4;
}

/* Orchestrator toggles `.is-active` to bring the demo on-screen
   for its turn. Sibling demos stay hidden + measurable. */
.am-demo.is-active {
  opacity: 1;
  visibility: visible;
}

/* ─── Browser-plugin demo wrapper ──────────────────────────────── */

.am-demo-browser-plugin {
  /* Docks to the right half of the mock, between the header bottom
     and the statusbar top. The runtime header/statusbar heights
     are 40px and 24px (see .am-header / .am-statusbar). */
  top: 40px;
  right: 0;
  bottom: 24px;
  width: 58%;
  min-width: 320px;
  max-width: 640px;

  /* ─ Timing tokens (this demo only) ─
     Cycle = full play including panel-in, inspector flow, modal,
     terminal inject, panel-out, and rest. JS reads `--anim-cycle`
     to know how long to hold before the next demo. */
  --anim-cycle: 17s;

  --anim-panel-in-delay: 0.0s;
  --anim-panel-in-dur: 0.55s;

  --anim-url-type-delay: 0.85s;
  --anim-url-type-dur: 1.4s;

  --anim-load-delay: 2.30s;
  --anim-load-dur: 0.85s;

  --anim-page-delay: 2.65s;
  --anim-page-dur: 0.45s;

  --anim-cursor-to-inspect-delay: 3.05s;
  --anim-inspect-pulse-delay: 3.45s;
  --anim-inspect-active-delay: 3.80s;

  --anim-cursor-to-page-delay: 4.00s;
  --anim-hover-delay: 4.55s;
  --anim-hover-dur: 0.55s;

  --anim-select-delay: 5.10s;

  --anim-modal-delay: 5.45s;
  --anim-modal-dur: 0.42s;

  --anim-modal-type-delay: 5.95s;
  --anim-modal-type-dur: 2.3s;

  --anim-send-pulse-delay: 8.35s;
  --anim-send-click-delay: 8.85s;
  --anim-modal-out-delay: 9.05s;
  --anim-modal-out-dur: 0.45s;

  /* Panel slides back out 500ms after the inspector modal finishes
     closing so the eye gets a brief pause on the dock chrome and
     the receiving terminal before the overlay retracts. */
  --anim-panel-out-delay: 10.0s;
  --anim-panel-out-dur: 0.55s;

  --anim-inject-delay: 9.45s;
  --anim-inject-dur: 2.0s;

  --anim-rest-delay: 11.6s;
  --anim-rest-dur: 5.4s;

  /* Colour tokens borrowed from the product's inspector overlay so
     the hover indicator + selection box read identically to the
     real browser plugin (shared/plugins/browser/electron/inspector-script.ts). */
  --anim-hover-color: #da7756;
  --anim-hover-bg: rgba(218, 119, 86, 0.10);
  --anim-select-color: #2f6df0;
  --anim-select-bg: rgba(47, 109, 240, 0.10);

  /* CTA rect (in wrapper-relative percentages) - written by the JS
     controller after measuring .am-anim-page-cta.primary. Defaults
     here are conservative starting values so the boxes render
     somewhere reasonable even before the first measurement lands. */
  --anim-cta-left: 5%;
  --anim-cta-top: 50%;
  --anim-cta-width: 18%;
  --anim-cta-height: 9%;
  /* Cursor target inside the CTA - biased to the upper-left so the
     cursor's tip (top-left of the SVG) visibly lands inside the
     button. Re-derived from the CTA rect on each measurement. */
  --anim-cta-cursor-x: 8%;
  --anim-cta-cursor-y: 53%;

  /* Cursor target on the modal's "Send to Agent" button. Written
     by JS AFTER modal-in completes (the modal has a scale +
     translate animation that shifts the button's measured rect
     until it settles, so an early measure would land low). The
     defaults here are tuned to land near the button at a typical
     panel size; JS overrides them on every cycle. */
  --anim-send-cursor-x: 80%;
  --anim-send-cursor-y: 75%;
}

/* ─── Cursor pointer ───────────────────────────────────────────── */

.am-anim-cursor {
  position: absolute;
  /* Panel-relative anchor at top-left. The SVG arrow's tip is at
     its own (0, 0), so `top` / `left` directly correspond to where
     the visible cursor tip lands on the panel. */
  top: 2%;
  left: 102%;
  width: 14px;
  height: 16px;
  display: inline-flex;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
  opacity: 0;
  z-index: 8;
}
.am-demo-browser-plugin.is-playing .am-anim-cursor {
  animation: am-anim-cursor-path var(--anim-cycle) linear forwards;
}

/* ─── Panel (slides in from right) ─────────────────────────────── */

.am-anim-panel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--border);
  box-shadow:
    -8px 0 22px -10px rgba(0, 0, 0, 0.55),
    -2px 0 0 0 color-mix(in srgb, var(--accent) 14%, transparent);
  overflow: hidden;
  pointer-events: none;
  transform: translateX(100%);
  opacity: 0.6;
}
/* Two-stage animation: panel-in (delay 0) docks the panel; panel-out
   (delay ~10s) slides it back out 500ms after the modal closes.
   Both use animation-fill-mode: forwards so the panel holds the
   docked state in between. CSS cascades the later animation in the
   list over the earlier one once both are at their "to" frame -
   panel-out therefore wins from 10s onward. */
.am-demo-browser-plugin.is-playing .am-anim-panel {
  animation:
    am-anim-panel-in  var(--anim-panel-in-dur)  var(--ease) var(--anim-panel-in-delay)  forwards,
    am-anim-panel-out var(--anim-panel-out-dur) var(--ease) var(--anim-panel-out-delay) forwards;
}

@keyframes am-anim-panel-in {
  0%   { transform: translateX(100%); opacity: 0.5; }
  60%  { opacity: 1; }
  100% { transform: translateX(0); opacity: 1; }
}

@keyframes am-anim-panel-out {
  0%   { transform: translateX(0);    opacity: 1; }
  35%  { opacity: 1; }
  100% { transform: translateX(105%); opacity: 0.5; }
}

/* ─── Toolbar ──────────────────────────────────────────────────── */

.am-anim-toolbar {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 5px 7px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  min-height: 30px;
  box-sizing: border-box;
  flex-shrink: 0;
}

.am-anim-nav-group,
.am-anim-action-group {
  display: inline-flex;
  align-items: center;
  gap: 1px;
}

.am-anim-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--text-3);
  cursor: default;
  position: relative;
  flex-shrink: 0;
}

.am-anim-inspect-btn { position: relative; }

.am-anim-inspect-pulse {
  position: absolute;
  inset: -3px;
  border-radius: 6px;
  border: 2px solid var(--accent);
  opacity: 0;
  pointer-events: none;
}
.am-demo-browser-plugin.is-playing .am-anim-inspect-pulse {
  animation: am-anim-pulse-out 0.95s var(--ease) var(--anim-inspect-pulse-delay) 2 forwards;
}

/* Inspect button switches to "is-active" tint after the click. */
.am-demo-browser-plugin.is-playing .am-anim-inspect-btn {
  animation: am-anim-inspect-activate 0.25s var(--ease) var(--anim-inspect-active-delay) forwards;
}

@keyframes am-anim-pulse-out {
  0%   { opacity: 0.85; transform: scale(1); }
  100% { opacity: 0; transform: scale(2.1); }
}

@keyframes am-anim-inspect-activate {
  to {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    color: var(--accent);
    border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  }
}

/* ─── URL bar ──────────────────────────────────────────────────── */

.am-anim-urlbar {
  flex: 1;
  height: 22px;
  padding: 0 9px;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 11px;
  display: flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 10.5px;
  position: relative;
  min-width: 0;
  overflow: hidden;
}

.am-demo-browser-plugin.is-playing .am-anim-urlbar {
  animation: am-anim-urlbar-focus 0.3s var(--ease) calc(var(--anim-url-type-delay) - 0.15s) forwards;
}

@keyframes am-anim-urlbar-focus {
  to {
    border-color: var(--accent);
    background: var(--surface);
  }
}

.am-anim-url-text {
  white-space: pre;
  color: var(--text);
}

.am-anim-url-placeholder {
  position: absolute;
  left: 9px;
  color: var(--text-muted);
  font-style: normal;
  pointer-events: none;
}
.am-demo-browser-plugin.is-playing .am-anim-url-placeholder {
  /* Hide the placeholder as soon as URL typing begins. */
  animation: am-anim-fade-out 0.1s linear calc(var(--anim-url-type-delay) - 0.05s) forwards;
}

.am-anim-url-caret {
  display: inline-block;
  width: 1px;
  height: 12px;
  margin-left: 1px;
  background: var(--accent);
  opacity: 0;
}
.am-demo-browser-plugin.is-playing .am-anim-url-caret {
  animation:
    am-anim-fade-in 0.1s linear calc(var(--anim-url-type-delay) - 0.1s) forwards,
    am-anim-caret-blink 1.05s steps(2, start) calc(var(--anim-url-type-delay) - 0.1s) infinite,
    am-anim-fade-out 0.15s linear calc(var(--anim-load-delay) - 0.05s) forwards;
}

@keyframes am-anim-caret-blink {
  50% { opacity: 0; }
}

/* ─── Tab strip ────────────────────────────────────────────────── */

.am-anim-tabstrip {
  display: flex;
  align-items: center;
  gap: 1px;
  height: 18px;
  padding: 0 6px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.am-anim-tab {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 100%;
  padding: 0 8px;
  font-size: 10px;
  color: var(--text-2);
  border-radius: 0;
  position: relative;
}
.am-anim-tab.is-active { color: var(--text); background: var(--bg-2); }
.am-anim-tab.is-active::after {
  content: '';
  position: absolute;
  left: 6px; right: 6px; bottom: 0;
  height: 1.5px;
  background: var(--accent);
  border-radius: 1px;
}
.am-anim-favicon {
  width: 9px; height: 9px;
  border-radius: 2px;
  background: linear-gradient(135deg, var(--text-3), var(--accent));
  opacity: 0.5;
  flex-shrink: 0;
}
.am-anim-tab-title { font-size: 10px; white-space: nowrap; }

.am-anim-tab-new {
  width: 14px; height: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 3px;
  color: var(--text-3);
  font-size: 11px;
  line-height: 1;
  margin-left: 2px;
}

/* ─── Loading bar ──────────────────────────────────────────────── */

.am-anim-loadbar {
  position: absolute;
  left: 0;
  top: 48px;  /* below toolbar(30) + tabstrip(18) */
  height: 1.5px;
  width: 0;
  background: linear-gradient(90deg, transparent, var(--accent) 30%, var(--accent-2) 70%, transparent);
  box-shadow: 0 0 6px var(--accent-glow);
  opacity: 0;
  z-index: 6;
}
.am-demo-browser-plugin.is-playing .am-anim-loadbar {
  animation: am-anim-loadbar var(--anim-load-dur) var(--ease) var(--anim-load-delay) forwards;
}

@keyframes am-anim-loadbar {
  0%   { width: 0;   opacity: 0; }
  10%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { width: 100%; opacity: 0; }
}

/* ─── Host (fake page area) ────────────────────────────────────── */

.am-anim-host {
  flex: 1;
  position: relative;
  background: var(--bg);
  overflow: hidden;
}

/* Empty-state placeholder (before navigation completes). */
.am-anim-host-blank {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: var(--text-3);
}
.am-anim-host-pulse {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 35%, transparent);
  animation: am-anim-blank-pulse 2.4s ease-in-out infinite;
}
.am-anim-host-blank-title { font-size: 12px; font-weight: 600; color: var(--text-2); }
.am-anim-host-blank-sub { font-size: 10.5px; color: var(--text-3); }

.am-demo-browser-plugin.is-playing .am-anim-host-blank {
  animation: am-anim-fade-out 0.25s var(--ease) calc(var(--anim-page-delay) - 0.15s) forwards;
}

@keyframes am-anim-blank-pulse {
  0%, 100% { transform: scale(0.95); opacity: 0.85; }
  50%      { transform: scale(1.05); opacity: 1; }
}

/* ─── Fake page ────────────────────────────────────────────────── */

.am-anim-page {
  position: absolute;
  inset: 0;
  padding: 16px 18px;
  background:
    radial-gradient(circle at 18% 0%, color-mix(in srgb, var(--accent-2) 9%, transparent), transparent 38%),
    radial-gradient(circle at 88% 110%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 42%),
    var(--bg);
  opacity: 0;
  /* Opacity-only fade-in - no translate. JS measures .am-anim-page-cta.primary
     to anchor the hover/select boxes; a transform here would shift the measured
     rect during the fade and the highlight would land off-target. */
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.am-demo-browser-plugin.is-playing .am-anim-page {
  animation: am-anim-page-in var(--anim-page-dur) var(--ease) var(--anim-page-delay) forwards;
}

@keyframes am-anim-page-in {
  to { opacity: 1; }
}

.am-anim-page-eyebrow {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}

.am-anim-page-title {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: var(--text);
}

.am-anim-page-title-grad {
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}

.am-anim-page-sub {
  margin: 0;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--text-2);
}

.am-anim-page-ctas {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 2px;
}

.am-anim-page-cta {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 12px;
  border-radius: 4px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.am-anim-page-cta.primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a1518;
  box-shadow: 0 4px 14px -6px var(--accent-glow);
}
.am-anim-page-cta.ghost {
  border: 1px solid var(--border-2);
  color: var(--text-2);
}

.am-anim-page-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: auto;
}

.am-anim-page-card {
  height: 38px;
  background: linear-gradient(180deg, var(--surface) 0%, var(--bg-2) 100%);
  border: 1px solid var(--border);
  border-radius: 6px;
  position: relative;
  overflow: hidden;
}
.am-anim-page-card::before {
  content: '';
  position: absolute;
  top: 6px; left: 8px;
  width: 28%;
  height: 4px;
  background: var(--accent-soft);
  border-radius: 3px;
}
.am-anim-page-card::after {
  content: '';
  position: absolute;
  top: 16px; left: 8px;
  right: 8px;
  height: 3px;
  background: var(--border-2);
  border-radius: 2px;
}

/* ─── Inspector hover + selection boxes (over the fake page) ─────
 *
 * Both boxes are panel-relative children (NOT host-relative) so they
 * share the cursor's coordinate system. Their rect is pinned to the
 * actual .am-anim-page-cta.primary element by the JS controller via
 * the --anim-cta-* custom properties - the highlight therefore always
 * lands on the CTA even when font loading / viewport changes shift
 * the page layout.
 *
 * Only opacity is animated by keyframes. The visual reads as "the
 * cursor enters the page area, lands on the CTA, sees a hover (orange)
 * indicator, then the user clicks and the selection (blue) commits".
 * Since they live at the same panel-relative position, this reads as
 * "the hover commits to a selection" rather than two separate boxes.
 */

.am-anim-hover-box,
.am-anim-select-box {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  border-radius: 3px;
  left: var(--anim-cta-left);
  top: var(--anim-cta-top);
  width: var(--anim-cta-width);
  height: var(--anim-cta-height);
  /* Above the page content but below the modal (z 5) so the
     selection stays visible after submit fires. */
  z-index: 3;
}

.am-anim-hover-box {
  border: 1.8px solid var(--anim-hover-color);
  background: var(--anim-hover-bg);
}
.am-demo-browser-plugin.is-playing .am-anim-hover-box {
  animation: am-anim-hover-fade var(--anim-hover-dur) var(--ease) var(--anim-hover-delay) forwards;
}

@keyframes am-anim-hover-fade {
  0%   { opacity: 0; }
  25%  { opacity: 0.95; }
  75%  { opacity: 0.95; }
  100% { opacity: 0; }
}

.am-anim-select-box {
  border: 1.8px solid var(--anim-select-color);
  background: var(--anim-select-bg);
  /* `transform` is animated for the commit pop; keep the position
     untouched so the CTA pin stays accurate. */
  transform: scale(0.85);
}
.am-anim-select-badge {
  position: absolute;
  top: -8px;
  left: -8px;
  min-width: 16px;
  height: 14px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--anim-select-color);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  border-radius: 7px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
.am-demo-browser-plugin.is-playing .am-anim-select-box {
  animation: am-anim-select 0.4s var(--ease) var(--anim-select-delay) forwards;
}

@keyframes am-anim-select {
  0%   { opacity: 0; transform: scale(0.85); }
  60%  { opacity: 1; transform: scale(1.04); }
  100% { opacity: 1; transform: scale(1); }
}

/* ─── Inspector submit modal ───────────────────────────────────── */

.am-anim-modal {
  position: absolute;
  /* Anchored top-center within the host so it covers most of the
     panel without escaping the dock chrome. */
  left: 50%;
  top: 52%;
  width: 86%;
  max-width: 460px;
  transform: translate(-50%, calc(-50% + 12px)) scale(0.92);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-2);
  border-radius: 8px;
  box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.7);
  opacity: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: var(--font-sans);
  z-index: 5;
}

.am-demo-browser-plugin.is-playing .am-anim-modal {
  animation:
    am-anim-modal-in var(--anim-modal-dur) var(--ease) var(--anim-modal-delay) forwards,
    am-anim-modal-out 0.45s var(--ease) var(--anim-modal-out-delay) forwards;
}

@keyframes am-anim-modal-in {
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

@keyframes am-anim-modal-out {
  to { opacity: 0; transform: translate(-50%, calc(-50% - 8px)) scale(0.96); }
}

.am-anim-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 7px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  font-weight: 600;
  color: var(--text);
}
.am-anim-modal-hint { font-size: 9.5px; font-weight: 400; color: var(--text-3); }

.am-anim-modal-body {
  padding: 9px 10px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.am-anim-modal-shot {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 16px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 5px;
}

.am-anim-modal-shot-btn {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 10px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a1518;
  border-radius: 4px;
  font-size: 9.5px;
  font-weight: 600;
}

.am-anim-modal-chips {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.am-anim-chip {
  font-family: var(--font-mono);
  font-size: 9.5px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
}

.am-anim-modal-meta {
  font-size: 9.5px;
  color: var(--text-3);
}

.am-anim-modal-textarea {
  position: relative;
  min-height: 38px;
  padding: 6px 8px;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 5px;
  font-family: var(--font-sans);
  font-size: 10px;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
}

.am-anim-modal-text { white-space: pre-wrap; }

.am-anim-modal-caret {
  display: inline-block;
  width: 1px;
  height: 11px;
  margin-left: 1px;
  background: var(--accent);
  vertical-align: -2px;
  opacity: 0;
}
.am-demo-browser-plugin.is-playing .am-anim-modal-caret {
  animation:
    am-anim-fade-in 0.1s linear calc(var(--anim-modal-type-delay) - 0.05s) forwards,
    am-anim-caret-blink 1.05s steps(2, start) var(--anim-modal-type-delay) infinite,
    am-anim-fade-out 0.15s linear calc(var(--anim-send-pulse-delay) - 0.05s) forwards;
}

.am-anim-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  padding: 7px 10px;
  border-top: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg-2) 60%, transparent);
}

.am-anim-modal-btn {
  padding: 4px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 4px;
  font-size: 10px;
  font-weight: 500;
  position: relative;
}

.am-anim-modal-btn.primary {
  background: var(--accent);
  color: #0a1518;
  border-color: var(--accent);
}

/* Send button gets a soft pulse before the click, then a brief flash on click. */
.am-demo-browser-plugin.is-playing .am-anim-modal-btn.primary {
  animation:
    am-anim-send-pulse 0.4s var(--ease) var(--anim-send-pulse-delay) 2 alternate,
    am-anim-send-flash 0.18s var(--ease) var(--anim-send-click-delay) forwards;
}

@keyframes am-anim-send-pulse {
  to { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 30%, transparent); }
}

@keyframes am-anim-send-flash {
  to { filter: brightness(1.4); }
}

/* ─── Cursor path (one keyframes chain covering the whole demo) ─
 *
 * All values are PANEL-RELATIVE percentages so the cursor scales
 * with the panel and aligns with every other panel-positioned
 * element (URL bar, inspect button, hover/select boxes, modal).
 *
 * The CTA stop uses --anim-cta-cursor-{x,y} which the JS controller
 * derives from the actual rendered position of
 * .am-anim-page-cta.primary - that way the cursor lands inside the
 * CTA even if font loading or viewport changes shift the layout.
 * Modern browsers smoothly interpolate between var-resolved
 * percentages because the vars are registered as @property below;
 * older browsers fall back to discrete interpolation (the cursor
 * snaps between stops, which is still acceptable for a marketing
 * vignette).
 */

@keyframes am-anim-cursor-path {
  /* 0-3% : panel slides in; cursor parked off the right edge. */
  0%   { opacity: 0; top: 2%; left: 102%; }
  3%   { opacity: 0; top: 2%; left: 102%; }

  /* Cursor enters and lands on the URL bar (toolbar middle band, ~5% of panel height). */
  5%   { opacity: 1; top: 5%; left: 55%; }
  14%  { opacity: 1; top: 5%; left: 60%; }

  /* Cursor moves to the inspect button (right side of the toolbar). */
  17%  { top: 5%; left: 90%; }
  21%  { top: 5%; left: 90%; }
  22%  { top: 6.5%; left: 90%; } /* press wiggle - cursor sinks slightly on click */
  23%  { top: 5%; left: 90%; }

  /* Cursor enters the page area, landing on the primary CTA.
     The (x, y) target comes from JS-measured CTA rect. */
  27%  { top: var(--anim-cta-cursor-y); left: var(--anim-cta-cursor-x); }
  30%  { top: var(--anim-cta-cursor-y); left: var(--anim-cta-cursor-x); }
  31%  { top: calc(var(--anim-cta-cursor-y) + 1.5%); left: var(--anim-cta-cursor-x); } /* click press */
  32%  { top: var(--anim-cta-cursor-y); left: var(--anim-cta-cursor-x); }

  /* Modal opens. Cursor moves to the textarea (middle of the modal body). */
  35%  { top: 68%; left: 50%; }

  /* Cursor hangs near the textarea during the modal-typing window. */
  50%  { top: 68%; left: 50%; }

  /* Cursor moves to the Send button (lower-right of the modal footer).
     Coordinates come from JS measurement (--anim-send-cursor-{x,y})
     so the cursor lands ON the actual rendered button instead of a
     hardcoded percentage that drifts with modal/panel sizing. */
  53%  { top: var(--anim-send-cursor-y); left: var(--anim-send-cursor-x); }
  54%  { top: calc(var(--anim-send-cursor-y) + 1.5%); left: var(--anim-send-cursor-x); } /* send click press */
  55%  { top: var(--anim-send-cursor-y); left: var(--anim-send-cursor-x); }

  /* Cursor drifts down and fades BEFORE the panel slides out (~58.8%
     of the 17s cycle = 10s). Leaving the cursor visible past that
     point would strand it floating over the revealed terminals once
     the overlay retracts. */
  57%   { opacity: 1; top: 90%; left: 82%; }
  58.5% { opacity: 0; top: 95%; left: 90%; }
  100%  { opacity: 0; top: 95%; left: 90%; }
}

/* Register the CTA cursor vars so cross-keyframe interpolation
   produces a smooth move instead of a discrete snap. Supported
   in Chromium 85+, Safari 16.4+, Firefox 128+ (mid-2024). Older
   browsers ignore @property and fall back to discrete behaviour
   without erroring. */
@property --anim-cta-cursor-x {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 8%;
}
@property --anim-cta-cursor-y {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 53%;
}
@property --anim-send-cursor-x {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 80%;
}
@property --anim-send-cursor-y {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 75%;
}

/* ─── Terminal injection (left focused term picks up the prompt) ─ */

.am-term-inject {
  display: inline-block;
  opacity: 0;
  /* Pre-allocates layout space so the existing prompt above doesn't
     reflow when the inject content fades in. */
  max-height: 0;
  overflow: hidden;
  transition: opacity 0s, max-height 0s;
  will-change: opacity, max-height;
}

/* Demo-specific effect on an element OUTSIDE .am-demo-browser-plugin
   (the inject content lives inside the focused Claude terminal's body).
   Gated on the orchestrator's `data-active-demo` attribute + the global
   `is-anim-playing` class so only the browser-plugin demo lights it up.

   Note: timing values are hardcoded here (not `var(--anim-inject-*)`)
   because those custom properties are scoped to the demo wrapper, which
   is a SIBLING of .am-grid (where this element lives) - so the var
   would resolve to its unset initial value and the animation would run
   with duration 0. Keep these numbers in sync with the matching
   `--anim-inject-*` tokens on `.am-demo-browser-plugin`. */
.app-mock[data-active-demo="browser-plugin"].is-anim-playing .am-term-inject {
  animation: am-anim-term-inject 2s var(--ease) 9.45s forwards;
}

@keyframes am-anim-term-inject {
  0%   { opacity: 0; max-height: 0; }
  40%  { opacity: 1; max-height: 14em; }
  100% { opacity: 1; max-height: 14em; }
}

/* Hide the original prompt-cursor while the inject is streaming, so
   the visual reads as "prompt was submitted, now agent is working".
   Timing hardcoded - see the .am-term-inject rule above for why. */
.app-mock[data-active-demo="browser-plugin"].is-anim-playing .am-cursor-base {
  animation: am-anim-fade-out 0.2s var(--ease) 9.35s forwards;
}

.am-anim-inject-chip {
  display: inline-block;
  padding: 0 5px;
  margin-right: 4px;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  border-radius: 3px;
  vertical-align: 1px;
}

/* ─── Shared helpers ───────────────────────────────────────────── */

@keyframes am-anim-fade-in {
  to { opacity: 1; }
}
@keyframes am-anim-fade-out {
  to { opacity: 0; }
}

/* When the focused term receives the inject, paint a soft accent
   pulse on its border so the eye is drawn to where the agent is
   working. */
.app-mock[data-active-demo="browser-plugin"].is-anim-playing .am-term-large.focused {
  animation: am-anim-term-receive 1.2s var(--ease) 9.35s forwards;
}

@keyframes am-anim-term-receive {
  0%   { box-shadow: 0 0 0 1px var(--accent), 0 8px 24px rgba(0, 0, 0, 0.35); }
  35%  { box-shadow: 0 0 0 2px var(--accent), 0 8px 36px var(--accent-glow), 0 0 24px var(--accent-glow); }
  100% { box-shadow: 0 0 0 1px var(--accent), 0 8px 24px rgba(0, 0, 0, 0.35); }
}

/* =============================================================
   Demo: terminal-spawn -> command -> close-shell
   -----------------------------------------------------------------
   Walks the new-terminal flow: cursor lands on the static `+`
   button in the header, pulse + modal in, agent picked, "Start
   Claude" click, modal out, the 4th terminal spawns into the grid,
   command typed + submitted, then the shell terminal closes and
   the grid returns to 2 right-column rows.

   Wrapper covers the full .app-mock minus the statusbar so the
   cursor can reach both the header (the `+`) and the grid (the
   terminals). Effects on elements outside the wrapper (the +
   pulse, the spawned terminal reveal, the shell close fade, the
   grid reflow) are gated on
   `.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="..."]`
   so the orchestrator drives multi-stage state via a single attr.
   ============================================================= */

.am-demo-terminal-spawn {
  /* Covers everything except the statusbar so the cursor can reach
     the header's + button AND the grid's terminals. */
  top: 0;
  left: 0;
  right: 0;
  bottom: 24px;  /* leave the statusbar visible */
  font-family: var(--font-sans);

  /* ─ Master cycle + per-stage timing tokens ─ */
  --anim-cycle: 14s;

  /* Stage 1: cursor enters and lands on the + button. */
  --anim-cursor-to-add-delay: 0.0s;

  /* Stage 2: + button click pulse. */
  --anim-add-pulse-delay: 0.55s;
  --anim-add-pulse-dur: 0.85s;

  /* Stage 3: new-terminal modal slides in (centered). */
  --anim-modal-in-delay: 0.75s;
  --anim-modal-in-dur: 0.40s;

  /* Stage 4: cursor moves to the Claude agent option, then Start. */
  --anim-cursor-to-claude-delay: 1.40s;
  --anim-cursor-to-start-delay: 2.40s;

  /* Stage 5: Start click pulse. */
  --anim-start-pulse-delay: 2.65s;
  --anim-start-pulse-dur: 0.45s;

  /* Stage 6: modal slides back out. */
  --anim-modal-out-delay: 3.10s;
  --anim-modal-out-dur: 0.40s;

  /* Stage 7: 4th terminal slides into the grid (grid reflows
     to 3 right-column rows). */
  --anim-spawn-delay: 3.30s;
  --anim-spawn-dur: 0.55s;

  /* Stage 8: cursor moves to the new terminal, types a command. */
  --anim-cursor-to-new-delay: 4.10s;
  --anim-cmd-type-delay: 4.60s;
  --anim-cmd-type-dur: 2.10s;

  /* Stage 9: command output streams into the new terminal. */
  --anim-cmd-output-delay: 6.80s;
  --anim-cmd-output-dur: 1.20s;

  /* Stage 10: cursor moves to the shell terminal's close button. */
  --anim-cursor-to-close-delay: 8.20s;

  /* Stage 11: close click pulse. */
  --anim-close-pulse-delay: 8.65s;
  --anim-close-pulse-dur: 0.55s;

  /* Stage 12: shell terminal fades out, grid reflows to 2 rows.
     The fade starts immediately on the cursor click (no perceptible
     gap between the click press and the terminal beginning to
     disappear) - production terminals close synchronously on the
     close-button click, so any delay reads as broken to viewers. */
  --anim-shell-fade-delay: 8.70s;
  --anim-shell-fade-dur: 0.50s;
  --anim-grid-reflow-delay: 9.20s;
  --anim-grid-reflow-dur: 0.45s;

  /* Stage 13: cursor drifts off, then the cycle holds on the final
     state until the orchestrator hands off to the next demo. */
  --anim-cursor-exit-delay: 9.80s;

  /* Position tokens written by JS (so the cursor lands on the
     actual rendered buttons, not hardcoded percentages). The
     fallback values here position the cursor reasonably even
     before the first measurement. */
  --anim-add-cursor-x: 32%;
  --anim-add-cursor-y: 4%;
  --anim-claude-cursor-x: 38%;
  --anim-claude-cursor-y: 42%;
  --anim-start-cursor-x: 56%;
  --anim-start-cursor-y: 65%;
  --anim-new-cursor-x: 78%;
  --anim-new-cursor-y: 82%;
  --anim-close-cursor-x: 88%;
  --anim-close-cursor-y: 60%;

  /* Position tokens for pulse rings - centered on their targets. */
  --anim-add-pulse-x: 33%;
  --anim-add-pulse-y: 5%;
  --anim-close-pulse-x: 89%;
  --anim-close-pulse-y: 61%;

  /* Popover anchor (top-left, where the new-agent picker drops from
     the + button). JS writes these from the rendered + button rect
     - bottom-start alignment with a 4px gap to match production. */
  --anim-spawn-popover-x: 32%;
  --anim-spawn-popover-y: 11%;
}

@property --anim-add-cursor-x      { syntax: '<percentage>'; inherits: true; initial-value: 32%; }
@property --anim-add-cursor-y      { syntax: '<percentage>'; inherits: true; initial-value: 4%; }
@property --anim-claude-cursor-x   { syntax: '<percentage>'; inherits: true; initial-value: 38%; }
@property --anim-claude-cursor-y   { syntax: '<percentage>'; inherits: true; initial-value: 42%; }
@property --anim-start-cursor-x    { syntax: '<percentage>'; inherits: true; initial-value: 56%; }
@property --anim-start-cursor-y    { syntax: '<percentage>'; inherits: true; initial-value: 65%; }
@property --anim-new-cursor-x      { syntax: '<percentage>'; inherits: true; initial-value: 78%; }
@property --anim-new-cursor-y      { syntax: '<percentage>'; inherits: true; initial-value: 82%; }
@property --anim-close-cursor-x    { syntax: '<percentage>'; inherits: true; initial-value: 88%; }
@property --anim-close-cursor-y    { syntax: '<percentage>'; inherits: true; initial-value: 60%; }

/* ─── Cursor (terminal-spawn) ──────────────────────────────────── */

.am-demo-terminal-spawn .am-anim-cursor {
  position: absolute;
  top: 5%;
  left: 110%;
  width: 14px;
  height: 16px;
  display: inline-flex;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
  opacity: 0;
  z-index: 8;
}

.am-demo-terminal-spawn.is-playing .am-anim-cursor {
  animation: am-spawn-cursor-path var(--anim-cycle) linear forwards;
}

@keyframes am-spawn-cursor-path {
  /* Cursor offscreen-right; wait briefly so the user notices the
     demo has begun (the + button is highlighted to draw the eye). */
  0%   { opacity: 0; top: 5%; left: 110%; }
  2%   { opacity: 0; top: 5%; left: 105%; }

  /* Enter and land on the + button (data-driven cursor target). */
  3.5% { opacity: 1; top: var(--anim-add-cursor-y); left: var(--anim-add-cursor-x); }
  5%   { top: var(--anim-add-cursor-y); left: var(--anim-add-cursor-x); }
  5.5% { top: calc(var(--anim-add-cursor-y) + 1.5%); left: var(--anim-add-cursor-x); } /* click press */
  6%   { top: var(--anim-add-cursor-y); left: var(--anim-add-cursor-x); }

  /* Modal opens. Cursor moves to the Claude agent option. */
  10%  { top: var(--anim-claude-cursor-y); left: var(--anim-claude-cursor-x); }
  12%  { top: var(--anim-claude-cursor-y); left: var(--anim-claude-cursor-x); }
  12.5%{ top: calc(var(--anim-claude-cursor-y) + 1.5%); left: var(--anim-claude-cursor-x); } /* click */
  13%  { top: var(--anim-claude-cursor-y); left: var(--anim-claude-cursor-x); }

  /* Move to "Start Claude" button. */
  18%  { top: var(--anim-start-cursor-y); left: var(--anim-start-cursor-x); }
  19.5%{ top: calc(var(--anim-start-cursor-y) + 1.5%); left: var(--anim-start-cursor-x); } /* click */
  20%  { top: var(--anim-start-cursor-y); left: var(--anim-start-cursor-x); }

  /* Modal exits. Cursor moves to the new terminal that spawns in. */
  29%  { top: var(--anim-new-cursor-y); left: var(--anim-new-cursor-x); }

  /* Cursor parks near the terminal during command typing. */
  50%  { top: var(--anim-new-cursor-y); left: var(--anim-new-cursor-x); }

  /* Cursor moves to the shell terminal's close button. */
  60%  { top: var(--anim-close-cursor-y); left: var(--anim-close-cursor-x); }
  62%  { top: var(--anim-close-cursor-y); left: var(--anim-close-cursor-x); }
  62.5%{ top: calc(var(--anim-close-cursor-y) + 1.5%); left: var(--anim-close-cursor-x); } /* click */
  63%  { top: var(--anim-close-cursor-y); left: var(--anim-close-cursor-x); }

  /* Cursor drifts down and fades. */
  73%  { opacity: 1; top: calc(var(--anim-close-cursor-y) + 6%); left: calc(var(--anim-close-cursor-x) + 4%); }
  77%  { opacity: 0; top: calc(var(--anim-close-cursor-y) + 9%); left: calc(var(--anim-close-cursor-x) + 6%); }
  100% { opacity: 0; top: calc(var(--anim-close-cursor-y) + 9%); left: calc(var(--anim-close-cursor-x) + 6%); }
}

/* ─── + button pulse (the static .am-add-term in the header) ───── */

.am-spawn-add-pulse {
  position: absolute;
  left: var(--anim-add-pulse-x);
  top: var(--anim-add-pulse-y);
  width: 26px;
  height: 22px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--accent);
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  z-index: 7;
}

.am-demo-terminal-spawn.is-playing .am-spawn-add-pulse {
  animation: am-spawn-pulse-out var(--anim-add-pulse-dur) var(--ease) var(--anim-add-pulse-delay) 2 forwards;
}

@keyframes am-spawn-pulse-out {
  0%   { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(1.8); }
}

/* The static `+` button itself flashes when clicked so the eye
   reads "the orchestrator pressed it". */
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="add-click"] .am-add-term,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="modal-open"] .am-add-term,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="agent-select"] .am-add-term,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="start-click"] .am-add-term {
  background: color-mix(in srgb, var(--accent) 28%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 60%, transparent);
}

/* ─── New-terminal modal ───────────────────────────────────────── */

/* New-agent picker: a compact popover anchored under the + button,
   mirroring the production AgentPickerPopover.tsx (280px wide,
   bottom-start anchor, 4px gap below the button). The marketing
   version drops the keyboard shortcut column and "Default" badge -
   the demo runs without input. JS writes the anchor position into
   --anim-spawn-popover-{x,y} based on the rendered + button rect,
   so the popover always tracks the button across viewport sizes. */
.am-spawn-modal {
  position: absolute;
  left: var(--anim-spawn-popover-x, 32%);
  top:  var(--anim-spawn-popover-y, 11%);
  width: 240px;
  /* Pixel-cap to keep proportions readable even on a narrow .app-mock. */
  max-width: 70%;
  transform-origin: top left;
  transform: translate(0, -4px) scale(0.96);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-2);
  border-radius: 7px;
  box-shadow: 0 14px 32px -10px rgba(0, 0, 0, 0.65);
  opacity: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  z-index: 5;
}

.am-demo-terminal-spawn.is-playing .am-spawn-modal {
  animation:
    am-spawn-modal-in  var(--anim-modal-in-dur)  var(--ease) var(--anim-modal-in-delay)  forwards,
    am-spawn-modal-out var(--anim-modal-out-dur) var(--ease) var(--anim-modal-out-delay) forwards;
}

@keyframes am-spawn-modal-in {
  to { opacity: 1; transform: translate(0, 0) scale(1); }
}

@keyframes am-spawn-modal-out {
  to { opacity: 0; transform: translate(0, -4px) scale(0.97); }
}

.am-spawn-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  font-weight: 600;
}

.am-spawn-modal-title { color: var(--text); }

.am-spawn-provider-chip {
  display: inline-flex;
  align-items: center;
  height: 15px;
  padding: 0 7px;
  border-radius: 8px;
  font-size: 9px;
  font-weight: 600;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
}

.am-spawn-modal-body {
  padding: 6px 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.am-spawn-modal-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
  font-weight: 600;
  padding: 0 2px;
}

.am-spawn-agents {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.am-spawn-agent {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 5px;
  cursor: default;
  transition: background-color 120ms ease, border-color 120ms ease;
}

.am-spawn-agent .am-agent-badge {
  width: 16px;
  height: 16px;
  font-size: 9px;
  flex-shrink: 0;
}

.am-spawn-agent-info {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
  flex: 1;
}

.am-spawn-agent-name {
  font-size: 10.5px;
  font-weight: 500;
  color: var(--text);
}

.am-spawn-agent-meta {
  font-size: 9px;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.am-spawn-agent-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--accent);
  color: #0a1518;
  opacity: 0;
  flex-shrink: 0;
}
.am-spawn-agent-check svg { width: 9px; height: 9px; }

/* Default-selected Claude option has the check visible from the
   start (so the popover reads as "Claude is the current default"). */
.am-spawn-agent.is-selected .am-spawn-agent-check { opacity: 1; }

.am-spawn-agent.is-selected {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-color: color-mix(in srgb, var(--accent) 28%, transparent);
}

.am-spawn-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 5px;
  padding: 6px 8px;
  border-top: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg-2) 60%, transparent);
}

.am-spawn-modal-btn {
  padding: 3px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 4px;
  font-size: 10px;
  font-weight: 500;
  position: relative;
}

.am-spawn-modal-btn.primary {
  background: var(--accent);
  color: #0a1518;
  border-color: var(--accent);
}

/* Start-Claude pulse + flash, mirroring the browser-plugin Send btn. */
.am-demo-terminal-spawn.is-playing .am-spawn-modal-btn.primary {
  animation:
    am-spawn-start-pulse var(--anim-start-pulse-dur) var(--ease) var(--anim-start-pulse-delay) 2 alternate;
}

@keyframes am-spawn-start-pulse {
  to { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 30%, transparent); }
}

/* ─── 4th terminal: spawn in / out ─────────────────────────────── */

/* Hidden by default. The terminal-spawn demo reveals it; other
   demos keep it out of layout entirely. */
.am-term-new { display: none; }

/* Phase-driven grid layout. The 4-terminal "spawning" phase puts
   the new terminal UNDERNEATH claude in the left column (claude
   shrinks to a single row); codex stays top-right, shell stays
   bottom-right. After "closed" (shell removed), codex stretches
   to span both right-column rows so the layout reads as a clean
   3-terminal grid with claude+new on the left and codex on the
   right - new terminal stays anchored under claude throughout. */

.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="spawning"]        .am-term-large,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="settled"]         .am-term-large,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-term-large,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"]     .am-term-large,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"]         .am-term-large,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"]          .am-term-large {
  grid-row: 1;
  grid-column: 1;
}

.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="spawning"]        .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="settled"]         .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"]     .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"]         .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"]          .am-term-new {
  display: flex;
  grid-row: 2;
  grid-column: 1;
  animation: am-spawn-term-in var(--anim-spawn-dur) var(--ease) both;
}

.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="spawning"]        .am-term[data-am-term="codex"],
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="settled"]         .am-term[data-am-term="codex"],
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-term[data-am-term="codex"],
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"]     .am-term[data-am-term="codex"],
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"]         .am-term[data-am-term="codex"] {
  grid-row: 1;
  grid-column: 2;
}

.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="spawning"]        .am-term-shell,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="settled"]         .am-term-shell,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-term-shell,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"]     .am-term-shell,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"]         .am-term-shell {
  grid-row: 2;
  grid-column: 2;
}

/* After close, codex stretches to span both right-column rows. */
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"] .am-term[data-am-term="codex"] {
  grid-row: 1 / -1;
  grid-column: 2;
}

@keyframes am-spawn-term-in {
  0%   { opacity: 0; transform: translateY(6px) scale(0.95); }
  100% { opacity: 1; transform: translateY(0)    scale(1); }
}

/* Smooth the grid-row template change. CSS Grid template
   interpolation lands in Chrome 117+, Firefox 119+, Safari 17.4+;
   older browsers snap rather than animating, still functional. */
.am-grid {
  transition: grid-template-rows var(--anim-grid-reflow-dur, 0.55s) var(--ease);
}

/* Shell terminal fade-out during phase "closing". */
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"] .am-term-shell {
  animation: am-spawn-shell-fade var(--anim-shell-fade-dur) var(--ease) forwards;
}

@keyframes am-spawn-shell-fade {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.95); }
}

/* Phase "closed": shell hidden entirely (removed from grid flow).
   Grid stays at 1fr 1fr / 1.4fr 1fr; claude + new occupy the left
   column rows; codex stretches across both right-column rows
   (rule above), so the layout reads as a tidy 3-terminal grid
   with new still anchored under claude. */
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"] .am-term-shell {
  display: none;
}
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"] .am-grid {
  grid-template-rows: 1fr 1fr;
}

/* ─── Close button on shell terminal (decorative until demo plays) ─ */

.am-term-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  padding: 0;
  background: transparent;
  border: none;
  color: var(--text-3);
  border-radius: 3px;
  cursor: default;
  opacity: 0;
  transition: opacity 120ms ease, background-color 120ms ease;
  margin-right: 2px;
}

/* Show the close button when the terminal-spawn demo is in a phase
   where the user is about to click it. Keeps the static state
   uncluttered (matches the production UI's hover-reveal behaviour). */
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-term-shell .am-term-close,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"]     .am-term-shell .am-term-close,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"]         .am-term-shell .am-term-close {
  opacity: 0.9;
  background: color-mix(in srgb, var(--text-3) 18%, transparent);
}

/* ─── Pulse ring on the shell close button ─────────────────────── */

.am-spawn-close-pulse {
  position: absolute;
  left: var(--anim-close-pulse-x);
  top: var(--anim-close-pulse-y);
  width: 18px;
  height: 18px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--red);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  z-index: 7;
}

.am-demo-terminal-spawn.is-playing .am-spawn-close-pulse {
  animation: am-spawn-close-pulse var(--anim-close-pulse-dur) var(--ease) var(--anim-close-pulse-delay) 2 forwards;
}

@keyframes am-spawn-close-pulse {
  0%   { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(2); }
}

/* ─── New terminal: command typing + output streaming ──────────── */

.am-anim-spawn-cmd { white-space: pre; }

.am-anim-spawn-output {
  display: inline-block;
  white-space: pre-wrap;
  opacity: 0;
  max-height: 0;
  overflow: hidden;
}

/* Timing hardcoded - --anim-cmd-* are scoped to .am-demo-terminal-spawn
   which is a sibling of .am-grid. See .am-term-inject rule above. */
.app-mock[data-active-demo="terminal-spawn"].is-anim-playing .am-anim-spawn-output {
  animation: am-spawn-output-reveal 1.2s var(--ease) 6.8s forwards;
}

@keyframes am-spawn-output-reveal {
  0%   { opacity: 0; max-height: 0; }
  50%  { opacity: 1; max-height: 10em; }
  100% { opacity: 1; max-height: 10em; }
}

/* Hide the in-terminal blinking cursor inside the new terminal
   until typing starts; reveal it during the typing window so the
   text reads as actively being entered. */
.am-cursor-new {
  opacity: 0;
}
.app-mock[data-active-demo="terminal-spawn"].is-anim-playing .am-cursor-new {
  /* Hardcoded delays - --anim-cmd-* vars don't cascade to this element. */
  animation:
    am-spawn-cursor-show 0.1s linear 4.6s forwards,
    am-spawn-cursor-hide 0.1s linear 6.8s forwards;
}
@keyframes am-spawn-cursor-show { to { opacity: 1; } }
@keyframes am-spawn-cursor-hide { to { opacity: 0; } }

/* The new terminal gets the focused-ring treatment after spawn so
   it visually reads as "newly active". */
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="settled"] .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"] .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"] .am-term-new,
.app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"] .am-term-new {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 8px 24px rgba(0, 0, 0, 0.35);
}

/* =============================================================
   Demo: git-manager quick view
   -----------------------------------------------------------------
   Hover-driven popover from the Git plugin pin in the header.
   Mirrors shared/plugins/git-manager/renderer/GitManagerQuickView.tsx:
   320px-wide popover with branch summary, unstaged + staged file
   lists, commit message textarea, and Pull/Push/Commit action row.

   Flow walks through staging two files, typing a commit message,
   committing (staged section clears, ahead becomes 1, Push button
   appears), then pushing (ahead returns to 0).

   Phase progression on .app-mock[data-gm-phase=...]:
       hover -> qv-open -> stage1 -> stage2 -> message-typed
       -> committed -> pushed -> rest
   ============================================================= */

.am-demo-git-manager {
  /* Covers everything except the statusbar so the cursor can reach
     both the Git pin (in header) and the popover content below it. */
  top: 0;
  left: 0;
  right: 0;
  bottom: 24px;
  font-family: var(--font-sans);

  /* ─ Master cycle + per-stage timing tokens ─ */
  --anim-cycle: 13s;

  --anim-gm-hover-delay: 0.40s;
  --anim-gm-qv-in-delay: 0.65s;
  --anim-gm-qv-in-dur: 0.20s;

  --anim-gm-stage1-cursor-delay: 1.60s;
  --anim-gm-stage1-click-delay: 1.85s;
  --anim-gm-stage1-phase: 2.00s;

  --anim-gm-stage2-cursor-delay: 2.45s;
  --anim-gm-stage2-click-delay: 2.70s;
  --anim-gm-stage2-phase: 2.85s;

  --anim-gm-msg-cursor-delay: 3.25s;
  --anim-gm-msg-type-delay: 3.55s;
  --anim-gm-msg-type-dur: 2.10s;
  --anim-gm-msg-phase: 5.70s;

  --anim-gm-commit-cursor-delay: 5.95s;
  --anim-gm-commit-click-delay: 6.20s;
  --anim-gm-commit-phase: 6.30s;

  --anim-gm-push-cursor-delay: 6.75s;
  --anim-gm-push-click-delay: 7.05s;
  --anim-gm-push-phase: 7.20s;

  --anim-gm-rest-delay: 7.60s;
  --anim-gm-qv-out-delay: 11.30s;
  --anim-gm-qv-out-dur: 0.20s;

  /* Position tokens written by JS each cycle. Defaults are
     conservative starting values used only on the very first paint. */
  --anim-gm-popover-x: 60%;
  --anim-gm-popover-y: 11%;
  --anim-gm-cursor-x: 70%;
  --anim-gm-cursor-y: 5%;
  --anim-gm-stage-cursor-x: 90%;
  --anim-gm-stage-cursor-y: 30%;
  --anim-gm-msg-cursor-x: 75%;
  --anim-gm-msg-cursor-y: 60%;
  --anim-gm-commit-cursor-x: 92%;
  --anim-gm-commit-cursor-y: 75%;
  --anim-gm-push-cursor-x: 78%;
  --anim-gm-push-cursor-y: 75%;
}

@property --anim-gm-cursor-x        { syntax: '<percentage>'; inherits: true; initial-value: 70%; }
@property --anim-gm-cursor-y        { syntax: '<percentage>'; inherits: true; initial-value: 5%; }
@property --anim-gm-stage-cursor-x  { syntax: '<percentage>'; inherits: true; initial-value: 90%; }
@property --anim-gm-stage-cursor-y  { syntax: '<percentage>'; inherits: true; initial-value: 30%; }
@property --anim-gm-msg-cursor-x    { syntax: '<percentage>'; inherits: true; initial-value: 75%; }
@property --anim-gm-msg-cursor-y    { syntax: '<percentage>'; inherits: true; initial-value: 60%; }
@property --anim-gm-commit-cursor-x { syntax: '<percentage>'; inherits: true; initial-value: 92%; }
@property --anim-gm-commit-cursor-y { syntax: '<percentage>'; inherits: true; initial-value: 75%; }
@property --anim-gm-push-cursor-x   { syntax: '<percentage>'; inherits: true; initial-value: 78%; }
@property --anim-gm-push-cursor-y   { syntax: '<percentage>'; inherits: true; initial-value: 75%; }

/* ─── Cursor (git-manager) ─────────────────────────────────────── */

.am-demo-git-manager .am-anim-cursor {
  position: absolute;
  top: 5%;
  left: 110%;
  width: 14px;
  height: 16px;
  display: inline-flex;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
  opacity: 0;
  z-index: 8;
}

.am-demo-git-manager.is-playing .am-anim-cursor {
  animation: am-gm-cursor-path var(--anim-cycle) linear forwards;
}

@keyframes am-gm-cursor-path {
  /* 0-3% : cursor offscreen-right; user notices the static state. */
  0%   { opacity: 0; top: 5%; left: 110%; }
  2%   { opacity: 0; top: 5%; left: 105%; }

  /* Cursor enters and lands on the Git plugin pin. */
  4%   { opacity: 1; top: var(--anim-gm-cursor-y); left: var(--anim-gm-cursor-x); }
  6%   { top: var(--anim-gm-cursor-y); left: var(--anim-gm-cursor-x); }
  /* Hover hold while the 150ms hover-intent runs in production. */
  12%  { top: var(--anim-gm-cursor-y); left: var(--anim-gm-cursor-x); }

  /* Cursor drops to the first file's stage button. */
  14%  { top: var(--anim-gm-stage-cursor-y); left: var(--anim-gm-stage-cursor-x); }
  15.5%{ top: calc(var(--anim-gm-stage-cursor-y) + 1.2%); left: var(--anim-gm-stage-cursor-x); } /* click press */
  16.5%{ top: var(--anim-gm-stage-cursor-y); left: var(--anim-gm-stage-cursor-x); }
  /* Stage1 phase applies at ~15.5% so the file slides up. Cursor
     stays parked because file 2 shifts up to the same row. */
  19%  { top: var(--anim-gm-stage-cursor-y); left: var(--anim-gm-stage-cursor-x); }
  20.5%{ top: calc(var(--anim-gm-stage-cursor-y) + 1.2%); left: var(--anim-gm-stage-cursor-x); } /* second click */
  21.5%{ top: var(--anim-gm-stage-cursor-y); left: var(--anim-gm-stage-cursor-x); }

  /* Cursor drops to the commit message textarea. */
  25%  { top: var(--anim-gm-msg-cursor-y); left: var(--anim-gm-msg-cursor-x); }
  /* Hold during typing. */
  44%  { top: var(--anim-gm-msg-cursor-y); left: var(--anim-gm-msg-cursor-x); }

  /* Move to Commit button. */
  46%  { top: var(--anim-gm-commit-cursor-y); left: var(--anim-gm-commit-cursor-x); }
  47.5%{ top: calc(var(--anim-gm-commit-cursor-y) + 1.2%); left: var(--anim-gm-commit-cursor-x); } /* click */
  48.5%{ top: var(--anim-gm-commit-cursor-y); left: var(--anim-gm-commit-cursor-x); }

  /* Move to Push button (which has just appeared because ahead > 0). */
  52%  { top: var(--anim-gm-push-cursor-y); left: var(--anim-gm-push-cursor-x); }
  54%  { top: calc(var(--anim-gm-push-cursor-y) + 1.2%); left: var(--anim-gm-push-cursor-x); } /* click */
  55%  { top: var(--anim-gm-push-cursor-y); left: var(--anim-gm-push-cursor-x); }

  /* Hold then drift away. */
  85%  { opacity: 1; top: calc(var(--anim-gm-push-cursor-y) + 5%); left: calc(var(--anim-gm-push-cursor-x) + 3%); }
  90%  { opacity: 0; top: calc(var(--anim-gm-push-cursor-y) + 8%); left: calc(var(--anim-gm-push-cursor-x) + 5%); }
  100% { opacity: 0; top: calc(var(--anim-gm-push-cursor-y) + 8%); left: calc(var(--anim-gm-push-cursor-x) + 5%); }
}

/* Pulse on the Git plugin pin while the cursor is hovering it. */
.app-mock[data-active-demo="git-manager"][data-gm-phase="hover"] .am-plugin[data-am-plugin="git"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="qv-open"] .am-plugin[data-am-plugin="git"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-plugin[data-am-plugin="git"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-plugin[data-am-plugin="git"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-plugin[data-am-plugin="git"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-plugin[data-am-plugin="git"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="pushed"] .am-plugin[data-am-plugin="git"] {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
}

/* ─── Quick-view popover ───────────────────────────────────────── */

.am-gm-qv {
  position: absolute;
  /* LEFT-edge anchored (no horizontal translate). The JS controller
     computes the left edge as `pin_center - half_popover_width` so
     the popover is visually centered on the pin, but the layout box
     matches the visual position - critical because the cursor
     measurement uses offsetLeft (transform-independent) and would
     otherwise be off by half_popover_width with translate(-50%, ...). */
  left: var(--anim-gm-popover-x);
  top:  var(--anim-gm-popover-y);
  width: 320px;
  max-width: 78%;
  transform-origin: top center;
  transform: translate(0, -4px) scale(0.96);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-2);
  border-radius: 8px;
  box-shadow: 0 16px 36px -10px rgba(0, 0, 0, 0.7);
  opacity: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  font-family: var(--font-sans);
  font-size: 10.5px;
  overflow: hidden;
  z-index: 5;
}

.am-demo-git-manager.is-playing .am-gm-qv {
  animation:
    am-gm-qv-in  var(--anim-gm-qv-in-dur)  var(--ease) var(--anim-gm-qv-in-delay)  forwards,
    am-gm-qv-out var(--anim-gm-qv-out-dur) var(--ease) var(--anim-gm-qv-out-delay) forwards;
}

@keyframes am-gm-qv-in  { to { opacity: 1; transform: translate(0, 0) scale(1); } }
@keyframes am-gm-qv-out { to { opacity: 0; transform: translate(0, -4px) scale(0.96); } }

/* Summary row */
.am-gm-qv-summary {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 11px;
}
.am-gm-qv-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--amber);
  flex-shrink: 0;
}
/* Branch turns "clean" (green) after the commit + push sequence. */
.app-mock[data-active-demo="git-manager"][data-gm-phase="pushed"] .am-gm-qv-dot,
.app-mock[data-active-demo="git-manager"][data-gm-phase="rest"]   .am-gm-qv-dot {
  background: var(--green);
}
.am-gm-qv-branch {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--text);
  font-weight: 500;
}
.am-gm-qv-branch-name { font-weight: 600; }
.am-gm-qv-meta {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-3);
  font-size: 10px;
}

/* Ahead indicator: hidden by default, visible after commit. */
.am-gm-qv-ahead {
  display: none;
  align-items: center;
  gap: 2px;
  color: var(--accent);
  font-weight: 600;
}
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-gm-qv-ahead {
  display: inline-flex;
}

/* "N changed" indicator. Updates copy by phase. */
.am-gm-qv-changed { color: var(--text-3); }
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"]    .am-gm-qv-changed::after,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"]    .am-gm-qv-changed::after,
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"]    .am-gm-qv-changed::after,
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-gm-qv-changed::after,
.app-mock[data-active-demo="git-manager"][data-gm-phase="pushed"]    .am-gm-qv-changed::after,
.app-mock[data-active-demo="git-manager"][data-gm-phase="rest"]      .am-gm-qv-changed::after {
  /* Reset the original "3 changed" text via phase-specific overrides below. */
  content: '';
}
/* The base text "3 changed" is replaced by phase-aware copy via JS, see qvChanged binding. */

/* Section labels */
.am-gm-qv-section-label {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
  font-weight: 600;
  padding: 0 2px;
}

/* Staged section is hidden by default (no staged files initially). */
.am-gm-qv-section-staged,
.am-gm-qv-files-staged { display: none; }
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-gm-qv-section-staged,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-gm-qv-files-staged,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-section-staged,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-files-staged,
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-gm-qv-section-staged,
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-gm-qv-files-staged {
  display: block;
}
.am-gm-qv-files-staged { display: none; }
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-gm-qv-files-staged,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-files-staged,
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-gm-qv-files-staged {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* File rows */
.am-gm-qv-files {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.am-gm-qv-file {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  background: var(--bg-2);
  border: 1px solid transparent;
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  min-width: 0;
}
.am-gm-qv-file:hover {
  background: color-mix(in srgb, var(--bg-2) 60%, var(--surface-2) 40%);
}

.am-gm-qv-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 700;
  border-radius: 3px;
  flex-shrink: 0;
}
.am-gm-qv-badge-M { color: var(--amber);  background: color-mix(in srgb, var(--amber) 22%, transparent); }
.am-gm-qv-badge-A { color: var(--green);  background: color-mix(in srgb, var(--green) 22%, transparent); }
.am-gm-qv-badge-D { color: var(--red);    background: color-mix(in srgb, var(--red) 22%, transparent); }

.am-gm-qv-path {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-2);
}

.am-gm-qv-stats {
  font-size: 9.5px;
  color: var(--text-3);
  font-family: var(--font-mono);
  flex-shrink: 0;
}
.am-gm-qv-add { color: var(--green); }
.am-gm-qv-del { color: var(--red); }

.am-gm-qv-row-btn {
  width: 16px;
  height: 16px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border-2);
  border-radius: 3px;
  color: var(--text-2);
  font-family: var(--font-sans);
  font-size: 11px;
  line-height: 1;
  cursor: default;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  outline: none;
}

/* Phase-driven file visibility. Each file has TWO DOM entries (one
   in unstaged, one in staged); we toggle which is rendered. */
.am-gm-qv-files-unstaged [data-am-gm-file="1"] { display: flex; }
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"]    .am-gm-qv-files-unstaged [data-am-gm-file="1"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"]    .am-gm-qv-files-unstaged [data-am-gm-file="1"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"]    .am-gm-qv-files-unstaged [data-am-gm-file="1"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-gm-qv-files-unstaged [data-am-gm-file="1"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="pushed"]    .am-gm-qv-files-unstaged [data-am-gm-file="1"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="rest"]      .am-gm-qv-files-unstaged [data-am-gm-file="1"] {
  display: none;
}

.am-gm-qv-files-unstaged [data-am-gm-file="2"] { display: flex; }
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"]    .am-gm-qv-files-unstaged [data-am-gm-file="2"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"]    .am-gm-qv-files-unstaged [data-am-gm-file="2"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-gm-qv-files-unstaged [data-am-gm-file="2"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="pushed"]    .am-gm-qv-files-unstaged [data-am-gm-file="2"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="rest"]      .am-gm-qv-files-unstaged [data-am-gm-file="2"] {
  display: none;
}

/* Staged section copies: visible only between stage and commit. */
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-gm-qv-files-staged [data-am-gm-file="1"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-files-staged [data-am-gm-file="1"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-gm-qv-files-staged [data-am-gm-file="1"] {
  display: flex;
}
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-files-staged [data-am-gm-file="2"],
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-gm-qv-files-staged [data-am-gm-file="2"] {
  display: flex;
}
/* Hide the staged children by default (no phase). */
.am-gm-qv-files-staged [data-am-gm-file] { display: none; }

/* Commit message input */
.am-gm-qv-message {
  position: relative;
  width: 100%;
  min-height: 36px;
  padding: 6px 8px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  font-family: var(--font-sans);
  font-size: 11px;
  line-height: 1.4;
  color: var(--text);
  white-space: pre-wrap;
  opacity: 0.6;
  transition: opacity 120ms ease, border-color 120ms ease;
  box-sizing: border-box;
}
.am-gm-qv-message-placeholder {
  position: absolute;
  left: 8px;
  top: 6px;
  color: var(--text-muted);
  font-size: 11px;
  pointer-events: none;
}
.am-gm-qv-message-text { white-space: pre-wrap; }
.am-gm-qv-message-caret {
  display: inline-block;
  width: 1px;
  height: 11px;
  margin-left: 1px;
  background: var(--accent);
  vertical-align: -1px;
  opacity: 0;
}
/* Once a file is staged, the textarea becomes "enabled" (full opacity). */
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-gm-qv-message,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-message,
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-gm-qv-message {
  opacity: 1;
  border-color: var(--border-2);
}
/* Replace placeholder text once the input is enabled. */
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-gm-qv-message-placeholder,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-message-placeholder {
  content: 'Commit message…';
}
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage1"] .am-gm-qv-message-placeholder::before,
.app-mock[data-active-demo="git-manager"][data-gm-phase="stage2"] .am-gm-qv-message-placeholder::before {
  content: 'Commit message…';
}
/* Hide the placeholder the instant any text is typed into the
 * message field, regardless of which phase is active. The previous
 * rule gated on data-gm-phase="typing", but the orchestrator sets
 * that phase AFTER the typing completes (~msgPhase) - so during
 * the actual typing window the placeholder stayed visible underneath
 * the growing text. Using `:has(.am-gm-qv-message-text:not(:empty))`
 * makes the rule reactive to real content. */
.am-gm-qv-message:has(.am-gm-qv-message-text:not(:empty)) .am-gm-qv-message-placeholder {
  display: none;
}

/* Belt-and-braces phase hide so the placeholder stays gone after
 * the message is cleared on commit (when the text span becomes
 * empty again). */
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-gm-qv-message-placeholder,
.app-mock[data-active-demo="git-manager"][data-gm-phase="pushed"]    .am-gm-qv-message-placeholder,
.app-mock[data-active-demo="git-manager"][data-gm-phase="rest"]      .am-gm-qv-message-placeholder {
  display: none;
}
/* Caret blink while typing window is active. */
.am-demo-git-manager.is-playing .am-gm-qv-message-caret {
  animation:
    am-gm-msg-caret-show 0.1s linear var(--anim-gm-msg-type-delay) forwards,
    am-gm-msg-caret-blink 1.05s steps(2, start) calc(var(--anim-gm-msg-type-delay) + 0.05s) infinite,
    am-gm-msg-caret-hide 0.1s linear var(--anim-gm-commit-click-delay) forwards;
}
@keyframes am-gm-msg-caret-show  { to { opacity: 1; } }
@keyframes am-gm-msg-caret-hide  { to { opacity: 0; } }
@keyframes am-gm-msg-caret-blink { 50% { opacity: 0; } }

/* Clear typed text after commit. */
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-gm-qv-message-text,
.app-mock[data-active-demo="git-manager"][data-gm-phase="pushed"]    .am-gm-qv-message-text,
.app-mock[data-active-demo="git-manager"][data-gm-phase="rest"]      .am-gm-qv-message-text {
  display: none;
}

/* Action row */
.am-gm-qv-inline-actions {
  display: flex;
  gap: 5px;
}

.am-gm-qv-action {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 24px;
  padding: 0 8px;
  border-radius: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 11px;
  font-weight: 500;
  cursor: default;
  position: relative;
}
.am-gm-qv-action.primary {
  background: var(--accent);
  color: #0a1518;
  border-color: var(--accent);
  font-weight: 600;
  opacity: 0.55;
}
/* Commit enabled when a file is staged + message exists. */
.app-mock[data-active-demo="git-manager"][data-gm-phase="typing"] .am-gm-qv-action.primary {
  opacity: 1;
}

/* Push button hidden until ahead > 0 (after commit). */
.am-gm-qv-action-push { display: none; }
.app-mock[data-active-demo="git-manager"][data-gm-phase="committed"] .am-gm-qv-action-push {
  display: inline-flex;
}

/* Commit click pulse. */
.am-demo-git-manager.is-playing .am-gm-qv-action.primary {
  animation: am-gm-commit-pulse 0.40s var(--ease) var(--anim-gm-commit-click-delay) 2 alternate;
}
@keyframes am-gm-commit-pulse {
  to { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 30%, transparent); }
}

/* Push click pulse. */
.am-demo-git-manager.is-playing .am-gm-qv-action-push {
  animation: am-gm-push-pulse 0.40s var(--ease) var(--anim-gm-push-click-delay) 2 alternate;
}
@keyframes am-gm-push-pulse {
  to { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 25%, transparent); }
}

/* Mobile: narrower popover so it fits inside the .app-mock. */
@media (max-width: 720px) {
  .am-gm-qv {
    width: 260px;
    max-width: 84%;
    font-size: 10px;
  }
  .am-gm-qv-path { font-size: 9.5px; }
  .am-gm-qv-stats { font-size: 9px; }
}

/* =============================================================
   Demo: voice plugin recording -> transcribing -> inject
   -----------------------------------------------------------------
   Mirrors the PySide6 voice indicator (overlay_qt.py). Pill at the
   bottom-center of the app-mock with a red recording dot, three-
   layered waveform bars, an `m:ss` timer ticking up, and an
   `Alt+Q` hotkey hint. Transitions to a transcribing state (red
   dot -> orange spinner, timer freezes), then exits and reveals
   transcribed text at the focused Claude terminal's prompt.

   Phase progression on .app-mock[data-voice-phase=...]:
       recording -> transcribing -> exited -> injecting -> settled
   ============================================================= */

.am-demo-voice {
  /* Covers the .app-mock (statusbar visible at the bottom). The
     indicator anchors to bottom-center via CSS; no header
     interaction needed. */
  top: 0;
  left: 0;
  right: 0;
  bottom: 24px;
  font-family: var(--font-sans);

  /* Master cycle + per-stage timing tokens. */
  --anim-cycle: 11s;
  --anim-voice-in-delay: 0.30s;
  --anim-voice-in-dur: 0.30s;
  --anim-voice-record-start: 0.55s;
  --anim-voice-record-dur: 3.00s;
  --anim-voice-transcribe-start: 3.55s;
  --anim-voice-transcribe-dur: 0.80s;
  --anim-voice-out-delay: 4.35s;
  --anim-voice-out-dur: 0.20s;
  --anim-voice-inject-delay: 4.70s;
  --anim-voice-inject-dur: 1.70s;
  --anim-voice-rest-delay: 6.50s;

  /* Production overlay colour tokens (overlay_qt.py defaults). */
  --voice-bg:      #131a25;
  --voice-border:  #2a3245;
  --voice-text:    #e6ecf5;
  --voice-muted:   #8a93a8;
  --voice-error:   #e85c66;   /* recording */
  --voice-warning: #e8a85c;   /* transcribing */
  --voice-accent:  #5cc1d4;   /* idle wave */
}

/* ─── Indicator pill ──────────────────────────────────────────── */

.am-voice-indicator {
  position: absolute;
  bottom: 8px;
  left: 50%;
  transform: translateX(-50%) scale(0.4);
  transform-origin: bottom center;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 7px 13px;
  background: var(--voice-bg);
  border: 1px solid var(--voice-border);
  border-radius: 13px;
  color: var(--voice-text);
  font-size: 10px;
  /* Three-pass shadow approximates the QPainter halo on the
   * production overlay (8px contact + 16px + 24px). */
  box-shadow:
    0 2px 8px  rgba(0, 0, 0, 0.50),
    0 6px 16px rgba(0, 0, 0, 0.35),
    0 14px 32px rgba(0, 0, 0, 0.25);
  opacity: 0;
  z-index: 9;
  pointer-events: none;
}

/* Pop-in (elastic-out) + shrink-out (scale-shrink). */
.am-demo-voice.is-playing .am-voice-indicator {
  animation:
    am-voice-pop-in  var(--anim-voice-in-dur)  cubic-bezier(0.34, 1.56, 0.64, 1) var(--anim-voice-in-delay) forwards,
    am-voice-pop-out var(--anim-voice-out-dur) cubic-bezier(0.55, 0, 0.7, 0.2) var(--anim-voice-out-delay) forwards;
}

@keyframes am-voice-pop-in  { to { opacity: 1; transform: translateX(-50%) scale(1); } }
@keyframes am-voice-pop-out { to { opacity: 0; transform: translateX(-50%) scale(0.4); } }

/* ─── Status indicator: red dot (recording) / orange spinner ─── */

.am-voice-status {
  position: relative;
  width: 14px;
  height: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.am-voice-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--voice-error);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--voice-error) 60%, transparent);
  animation: am-voice-dot-pulse 1.4s ease-in-out infinite;
}
@keyframes am-voice-dot-pulse {
  0%, 100% { transform: scale(0.92); box-shadow: 0 0 0 0 color-mix(in srgb, var(--voice-error) 55%, transparent); }
  50%      { transform: scale(1.08); box-shadow: 0 0 0 6px color-mix(in srgb, var(--voice-error) 0%,  transparent); }
}

.am-voice-spinner {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--voice-warning);
}
.am-voice-spinner svg { animation: am-voice-spinner-rot 0.9s linear infinite; }
@keyframes am-voice-spinner-rot { to { transform: rotate(360deg); } }

/* Phase switch: hide red dot, show spinner. */
.app-mock[data-active-demo="voice"][data-voice-phase="transcribing"] .am-voice-dot {
  display: none;
}
.app-mock[data-active-demo="voice"][data-voice-phase="transcribing"] .am-voice-spinner {
  display: inline-flex;
}

/* ─── Waveform bars ────────────────────────────────────────────── */

.am-voice-waveform {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 18px;
  flex-shrink: 0;
}

.am-voice-bar {
  width: 2px;
  border-radius: 1px;
  background: var(--voice-error);
  height: 3px;
}

/* Recording state: bars bounce with varied amplitudes/delays so
 * the wave doesn't feel mechanical. Production uses three overlapping
 * sine waves; this 16-bar approximation reads similarly at the
 * marketing scale. */
.am-demo-voice.is-playing .am-voice-bar {
  animation: am-voice-bar-bounce 0.70s ease-in-out infinite;
}

/* Per-bar variation: alternating phase + amplitudes so the wave has
 * a natural-feeling envelope (mid bars taller, edges shorter). */
.am-voice-bar:nth-child(1)  { animation-delay: -0.05s; animation-duration: 0.62s; --amp-h: 5px; }
.am-voice-bar:nth-child(2)  { animation-delay: -0.18s; animation-duration: 0.78s; --amp-h: 9px; }
.am-voice-bar:nth-child(3)  { animation-delay: -0.32s; animation-duration: 0.55s; --amp-h: 13px; }
.am-voice-bar:nth-child(4)  { animation-delay:  0.00s; animation-duration: 0.70s; --amp-h: 11px; }
.am-voice-bar:nth-child(5)  { animation-delay: -0.10s; animation-duration: 0.82s; --amp-h: 15px; }
.am-voice-bar:nth-child(6)  { animation-delay: -0.25s; animation-duration: 0.60s; --amp-h: 17px; }
.am-voice-bar:nth-child(7)  { animation-delay: -0.05s; animation-duration: 0.74s; --amp-h: 16px; }
.am-voice-bar:nth-child(8)  { animation-delay: -0.15s; animation-duration: 0.66s; --amp-h: 17px; }
.am-voice-bar:nth-child(9)  { animation-delay: -0.22s; animation-duration: 0.78s; --amp-h: 16px; }
.am-voice-bar:nth-child(10) { animation-delay: -0.08s; animation-duration: 0.58s; --amp-h: 15px; }
.am-voice-bar:nth-child(11) { animation-delay: -0.30s; animation-duration: 0.72s; --amp-h: 13px; }
.am-voice-bar:nth-child(12) { animation-delay: -0.12s; animation-duration: 0.64s; --amp-h: 12px; }
.am-voice-bar:nth-child(13) { animation-delay: -0.04s; animation-duration: 0.80s; --amp-h: 10px; }
.am-voice-bar:nth-child(14) { animation-delay: -0.20s; animation-duration: 0.56s; --amp-h: 9px; }
.am-voice-bar:nth-child(15) { animation-delay: -0.28s; animation-duration: 0.70s; --amp-h: 7px; }
.am-voice-bar:nth-child(16) { animation-delay: -0.06s; animation-duration: 0.66s; --amp-h: 5px; }

@keyframes am-voice-bar-bounce {
  0%, 100% { height: 3px; }
  50%      { height: var(--amp-h, 12px); }
}

/* Transcribing state: bars switch to idle accent colour and a
 * subtler oscillation (matches the production "soft sine, ~30%
 * amplitude" idle pulse). */
.app-mock[data-active-demo="voice"][data-voice-phase="transcribing"] .am-voice-bar {
  background: var(--voice-warning);
  animation: am-voice-bar-idle 1.4s ease-in-out infinite;
}
@keyframes am-voice-bar-idle {
  0%, 100% { height: 3px; opacity: 0.55; }
  50%      { height: calc(var(--amp-h, 12px) * 0.35); opacity: 0.85; }
}

/* ─── Timer + hotkey ───────────────────────────────────────────── */

.am-voice-meta {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  flex-shrink: 0;
}

.am-voice-timer {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  color: var(--voice-text);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.am-voice-hotkey {
  font-family: var(--font-mono);
  font-size: 8.5px;
  color: var(--voice-muted);
  line-height: 1.1;
  letter-spacing: 0.02em;
}

/* ─── Inject content in the focused Claude terminal ───────────── */

.am-voice-inject {
  /* Block-level so the inject sits on its own line below the previous
     prompt, irrespective of `white-space: pre-wrap` in the parent
     terminal body. Margin-top creates a visible gap without relying
     on literal newlines in the HTML (which would render as text and
     interact badly with the typing animation). */
  display: block;
  margin-top: 1em;
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  white-space: nowrap;  /* keep the prompt + transcript on one line */
}

/* Reveal the inject during the voice demo. Hardcoded delay/dur
 * because the timing tokens live on .am-demo-voice (a sibling of
 * .am-grid, where this element lives) and CSS vars don't cascade
 * laterally. Keep these in sync with --anim-voice-inject-*. */
.app-mock[data-active-demo="voice"].is-anim-playing .am-voice-inject {
  animation: am-voice-inject-reveal 1.7s var(--ease) 4.7s forwards;
}
@keyframes am-voice-inject-reveal {
  0%   { opacity: 0; max-height: 0; }
  35%  { opacity: 1; max-height: 6em; }
  100% { opacity: 1; max-height: 6em; }
}

/* Hide the original prompt's blinking cursor while the voice
 * inject is appearing - reads as "the voice replaced the prompt". */
.app-mock[data-active-demo="voice"].is-anim-playing .am-cursor-base {
  animation: am-anim-fade-out 0.2s var(--ease) 4.6s forwards;
}

.am-cursor-voice-end {
  /* Standard blinking-cursor styling; uses the global @keyframes
   * am-blink from the static cursor rules. */
  opacity: 0;
}
.app-mock[data-active-demo="voice"].is-anim-playing .am-cursor-voice-end {
  animation: am-voice-end-cursor-show 0.1s linear 5.4s forwards;
}
@keyframes am-voice-end-cursor-show { to { opacity: 1; } }

/* Focused-terminal glow during the voice demo - matches the
 * browser-plugin "term-receive" treatment for visual consistency. */
.app-mock[data-active-demo="voice"].is-anim-playing .am-term-large.focused {
  animation: am-anim-term-receive 1.0s var(--ease) 4.7s forwards;
}

/* Mobile adjustments: pill scales down slightly so it doesn't
 * dominate a narrow .app-mock. */
@media (max-width: 720px) {
  .am-voice-indicator {
    padding: 6px 11px;
    gap: 8px;
    font-size: 9.5px;
  }
  .am-voice-waveform { height: 16px; }
  .am-voice-bar { width: 1.5px; }
  .am-voice-timer { font-size: 10px; }
  .am-voice-hotkey { font-size: 8px; }
}

/* =============================================================
   Demo: workspace-plugin
   -----------------------------------------------------------------
   Demonstrates the full enable-a-plugin -> dock-its-panel flow:

     cursor + Ctrl+, kbd chip  -> Settings modal in (Plugins tab)
                               -> cursor lands on Workspace toggle
                               -> click pulse + toggle flips on
                               -> Settings modal out
                               -> Workspace pin slides into header
                                  plugin row with glow pulse
                               -> cursor moves to the new pin -> click
                               -> Workspace side panel slides in
                                  from the right (between header +
                                  statusbar) and tree rows cascade in
                               -> cursor drops to the filter input,
                                  types "comp"; matching rows stay
                                  opaque, non-matching rows dim

   Wrapper covers the full .app-mock minus the statusbar so the
   cursor can reach both the new header pin AND the docked panel.

   Effects on elements OUTSIDE the wrapper - the workspace pin in
   the header plugin row and the focused-terminal dim - are gated on
   `.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="..."]`
   so the orchestrator drives multi-stage state via a single attr.

   All timing tokens are scoped to .am-demo-workspace-plugin so a
   designer can retune any single beat without touching JS.
   ============================================================= */

.am-demo-workspace-plugin {
  /* Same geometry as terminal-spawn: full mock minus the statusbar. */
  top: 0;
  left: 0;
  right: 0;
  bottom: 24px;
  font-family: var(--font-sans);
  color: var(--text);

  /* ─ Master cycle + per-stage timing tokens ─ */
  --anim-cycle: 22s;

  /* Stage 1: cursor enters from off-mock; kbd chip pops as a brief
     blip that signals "Ctrl + , was pressed", then fades just before
     the settings modal slides in (so the modal owns the focus). */
  --anim-wsp-cursor-enter-delay: 0.20s;
  --anim-wsp-kbd-chip-in-delay: 0.30s;
  --anim-wsp-kbd-chip-in-dur: 0.20s;
  --anim-wsp-kbd-chip-out-delay: 0.65s;
  --anim-wsp-kbd-chip-out-dur: 0.25s;

  /* Stage 2: settings modal slides in (centered). */
  --anim-wsp-settings-in-delay: 0.85s;
  --anim-wsp-settings-in-dur: 0.45s;

  /* Stage 3: cursor lands on the target toggle. */
  --anim-wsp-cursor-to-toggle-delay: 2.10s;

  /* Stage 4: click pulse + toggle flip-on. */
  --anim-wsp-toggle-pulse-delay: 2.55s;
  --anim-wsp-toggle-pulse-dur: 0.65s;
  --anim-wsp-toggle-flip-delay: 2.65s;
  --anim-wsp-toggle-flip-dur: 0.30s;

  /* Stage 5: settings modal slides back out. */
  --anim-wsp-settings-out-delay: 3.40s;
  --anim-wsp-settings-out-dur: 0.45s;

  /* Stage 6: workspace pin appears in the header plugin row with a
     glow + slide-in. The pin's `display` is toggled to inline-flex
     here so it actually claims layout; the orchestrator JS flips
     `data-wsp-phase` to drive the reveal across stages. */
  --anim-wsp-pin-reveal-delay: 3.85s;
  --anim-wsp-pin-reveal-dur: 0.55s;

  /* Stage 7: cursor moves to the new pin. */
  --anim-wsp-cursor-to-pin-delay: 4.60s;

  /* Stage 8: pin click pulse. */
  --anim-wsp-pin-pulse-delay: 5.15s;
  --anim-wsp-pin-pulse-dur: 0.55s;

  /* Stage 9: workspace side panel slides in from the right. */
  --anim-wsp-panel-in-delay: 5.40s;
  --anim-wsp-panel-in-dur: 0.55s;

  /* Stage 10: tree rows cascade in (per-row CSS delay handles the stagger). */
  --anim-wsp-tree-cascade-delay: 6.05s;

  /* Stage 11: cursor moves to the filter input. */
  --anim-wsp-cursor-to-filter-delay: 8.10s;

  /* Stage 12: filter typing. */
  --anim-wsp-filter-type-delay: 8.60s;
  --anim-wsp-filter-type-dur: 1.10s;
  --anim-wsp-filter-applied-delay: 10.0s;

  /* Stage 13: cursor descends to a matching tree row + double-click. */
  --anim-wsp-cursor-to-file-delay: 11.6s;
  --anim-wsp-file-dblclick-1-delay: 12.0s;
  --anim-wsp-file-dblclick-2-delay: 12.20s;
  --anim-wsp-file-dblclick-pulse-dur: 0.50s;

  /* Stage 14: editor overlay slides in over the terminal grid. */
  --anim-wsp-editor-in-delay: 12.5s;
  --anim-wsp-editor-in-dur: 0.55s;

  /* Stage 15: cursor moves into the editor body, types an edit. */
  --anim-wsp-cursor-to-edit-delay: 13.4s;
  --anim-wsp-edit-type-delay: 13.9s;
  --anim-wsp-edit-type-dur: 1.40s;

  /* Stage 16: cursor moves to the tab; tab is grabbed. */
  --anim-wsp-cursor-to-tab-delay: 15.6s;
  --anim-wsp-tab-grab-delay: 16.0s;

  /* Stage 17: tab is dragged away; once past the drag-distance threshold
     the docked editor swaps for a floating undocked window. */
  --anim-wsp-tab-drag-delay: 16.2s;
  --anim-wsp-undock-delay: 17.2s;
  --anim-wsp-undock-dur: 0.55s;

  /* Stage 18: cursor drift + cycle hold. */
  --anim-wsp-cursor-exit-delay: 19.5s;

  /* Cursor / pulse anchor positions (in wrapper-relative percentages).
     JS writes these on every cycle by measuring the rendered targets
     - the fallbacks below place the cursor / pulses reasonably
     before the first measurement lands. */
  --anim-wsp-cursor-enter-x: 110%;
  --anim-wsp-cursor-enter-y: 8%;
  --anim-wsp-toggle-cursor-x: 56%;
  --anim-wsp-toggle-cursor-y: 48%;
  --anim-wsp-pin-cursor-x: 60%;
  --anim-wsp-pin-cursor-y: 6%;
  --anim-wsp-filter-cursor-x: 76%;
  --anim-wsp-filter-cursor-y: 14%;
  --anim-wsp-toggle-pulse-x: 56%;
  --anim-wsp-toggle-pulse-y: 48%;
  --anim-wsp-pin-pulse-x: 60%;
  --anim-wsp-pin-pulse-y: 6%;

  /* Editor-flow cursor targets (JS-written; fallbacks placed inside the
     left workspace panel + docked editor area on the right). */
  --anim-wsp-file-cursor-x: 18%;
  --anim-wsp-file-cursor-y: 48%;
  --anim-wsp-file-pulse-x: 19%;
  --anim-wsp-file-pulse-y: 49%;
  --anim-wsp-edit-cursor-x: 70%;
  --anim-wsp-edit-cursor-y: 55%;
  --anim-wsp-tab-cursor-x: 56%;
  --anim-wsp-tab-cursor-y: 16%;
  /* Final drag-out target for the cursor: down and left of the tab
     bar so the drag distance exceeds the undock threshold AND the
     resulting floating window lands mostly inside the mock (not
     extending off the bottom-right edge). The floating window's
     transform offsets a fraction up + left from this point so the
     cursor lands on its titlebar after the drop. */
  --anim-wsp-drag-end-x: 44%;
  --anim-wsp-drag-end-y: 42%;

  /* Reusable colour token: enabled-toggle accent matches the rest of
     the marketing surface (var(--accent) is the cyan brand colour). */
  --anim-wsp-toggle-on: var(--accent);
  --anim-wsp-toggle-on-bg: color-mix(in srgb, var(--accent) 70%, transparent);
}

/* @property declarations let the cursor animation pick up live
   updates to these custom properties (e.g. when the window resizes
   mid-cycle and the orchestrator re-runs onMeasure). Without these
   the browser treats var() values as opaque strings, so a JS write
   doesn't propagate to running animations. Match the syntax to the
   underlying value type so the engine can interpolate too. */
@property --anim-wsp-toggle-cursor-x { syntax: '<percentage>'; inherits: true; initial-value: 56%; }
@property --anim-wsp-toggle-cursor-y { syntax: '<percentage>'; inherits: true; initial-value: 48%; }
@property --anim-wsp-pin-cursor-x    { syntax: '<percentage>'; inherits: true; initial-value: 60%; }
@property --anim-wsp-pin-cursor-y    { syntax: '<percentage>'; inherits: true; initial-value: 6%; }
@property --anim-wsp-filter-cursor-x { syntax: '<percentage>'; inherits: true; initial-value: 76%; }
@property --anim-wsp-filter-cursor-y { syntax: '<percentage>'; inherits: true; initial-value: 14%; }
@property --anim-wsp-toggle-pulse-x  { syntax: '<percentage>'; inherits: true; initial-value: 56%; }
@property --anim-wsp-toggle-pulse-y  { syntax: '<percentage>'; inherits: true; initial-value: 48%; }
@property --anim-wsp-pin-pulse-x     { syntax: '<percentage>'; inherits: true; initial-value: 60%; }
@property --anim-wsp-pin-pulse-y     { syntax: '<percentage>'; inherits: true; initial-value: 6%; }
@property --anim-wsp-file-cursor-x   { syntax: '<percentage>'; inherits: true; initial-value: 18%; }
@property --anim-wsp-file-cursor-y   { syntax: '<percentage>'; inherits: true; initial-value: 48%; }
@property --anim-wsp-file-pulse-x    { syntax: '<percentage>'; inherits: true; initial-value: 19%; }
@property --anim-wsp-file-pulse-y    { syntax: '<percentage>'; inherits: true; initial-value: 49%; }
@property --anim-wsp-edit-cursor-x   { syntax: '<percentage>'; inherits: true; initial-value: 70%; }
@property --anim-wsp-edit-cursor-y   { syntax: '<percentage>'; inherits: true; initial-value: 55%; }
@property --anim-wsp-tab-cursor-x    { syntax: '<percentage>'; inherits: true; initial-value: 56%; }
@property --anim-wsp-tab-cursor-y    { syntax: '<percentage>'; inherits: true; initial-value: 16%; }
@property --anim-wsp-drag-end-x      { syntax: '<percentage>'; inherits: true; initial-value: 75%; }
@property --anim-wsp-drag-end-y      { syntax: '<percentage>'; inherits: true; initial-value: 72%; }

/* ─── Cursor (workspace-plugin) ────────────────────────────────── */

.am-demo-workspace-plugin .am-anim-cursor {
  position: absolute;
  top: 8%;
  left: 110%;
  width: 14px;
  height: 16px;
  display: inline-flex;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
  opacity: 0;
  z-index: 9;
}

.am-demo-workspace-plugin.is-playing .am-anim-cursor {
  animation: am-wsp-cursor-path var(--anim-cycle) linear forwards;
}

@keyframes am-wsp-cursor-path {
  /* Each percentage is mapped to seconds via `--anim-cycle = 22s`
     (1% = 220ms). The phase machine fires JS-driven attribute
     transitions at the timestamps in parentheses below; the cursor
     keyframes are positioned so the visual click-press lands a
     fraction BEFORE each phase transition that depends on it, which
     reads as cause-and-effect in the eye. */

  /* Cursor offscreen-right. Brief hold so the eye registers the
     keyboard chip + modal animation before tracking the cursor. */
  0%    { opacity: 0; top: var(--anim-wsp-cursor-enter-y); left: var(--anim-wsp-cursor-enter-x); }
  1.0%  { opacity: 0; top: var(--anim-wsp-cursor-enter-y); left: var(--anim-wsp-cursor-enter-x); }

  /* ~2.10s (9.5%): land on the target toggle. Pulse 2.55s (11.6%);
     flip 2.65s (12.0%). Press at 11.7% lands between them. */
  9.5%  { opacity: 1; top: var(--anim-wsp-toggle-cursor-y);  left: var(--anim-wsp-toggle-cursor-x); }
  11.2% { opacity: 1; top: var(--anim-wsp-toggle-cursor-y);  left: var(--anim-wsp-toggle-cursor-x); }
  11.7% { opacity: 1; top: calc(var(--anim-wsp-toggle-cursor-y) + 1.5%); left: var(--anim-wsp-toggle-cursor-x); }  /* press */
  12.3% { opacity: 1; top: var(--anim-wsp-toggle-cursor-y);  left: var(--anim-wsp-toggle-cursor-x); }                /* release */

  /* Brief hold while the toggle flips + settings modal closes. */
  17.5% { opacity: 1; top: var(--anim-wsp-toggle-cursor-y);  left: var(--anim-wsp-toggle-cursor-x); }

  /* ~5.04s (22.9%): arrive at the newly-revealed pin in the header.
     Pulse 5.15s (23.4%); panel-in 5.40s (24.5%). Press at 24.1%
     lands between pulse and panel-in. */
  22.9% { opacity: 1; top: var(--anim-wsp-pin-cursor-y);    left: var(--anim-wsp-pin-cursor-x); }
  23.7% { opacity: 1; top: var(--anim-wsp-pin-cursor-y);    left: var(--anim-wsp-pin-cursor-x); }
  24.1% { opacity: 1; top: calc(var(--anim-wsp-pin-cursor-y) + 1.6%); left: var(--anim-wsp-pin-cursor-x); }  /* press */
  24.7% { opacity: 1; top: var(--anim-wsp-pin-cursor-y);    left: var(--anim-wsp-pin-cursor-x); }              /* release */

  /* Brief hold while the panel slides in. */
  32.0% { opacity: 1; top: var(--anim-wsp-pin-cursor-y);    left: var(--anim-wsp-pin-cursor-x); }

  /* ~8.48s (38.5%): drop to the filter input. Phase `filtering` 8.52s
     (38.7%); typing begins 8.60s (39.1%). Press at 38.8% lands just
     before typing starts. */
  38.5% { opacity: 1; top: var(--anim-wsp-filter-cursor-y); left: var(--anim-wsp-filter-cursor-x); }
  38.8% { opacity: 1; top: calc(var(--anim-wsp-filter-cursor-y) + 1%); left: var(--anim-wsp-filter-cursor-x); }  /* press */
  39.1% { opacity: 1; top: var(--anim-wsp-filter-cursor-y); left: var(--anim-wsp-filter-cursor-x); }              /* release */

  /* Hold while typing. */
  50.0% { opacity: 1; top: var(--anim-wsp-filter-cursor-y); left: var(--anim-wsp-filter-cursor-x); }

  /* ~11.6s (52.7%): descend to the matching tree row.
     Double-click pulses 12.0s (54.5%) + 12.20s (55.5%); editor opens
     12.5s (56.8%). Two press-release pairs read as a double-click. */
  52.7% { opacity: 1; top: var(--anim-wsp-file-cursor-y);   left: var(--anim-wsp-file-cursor-x); }
  54.3% { opacity: 1; top: var(--anim-wsp-file-cursor-y);   left: var(--anim-wsp-file-cursor-x); }
  54.7% { opacity: 1; top: calc(var(--anim-wsp-file-cursor-y) + 0.8%); left: var(--anim-wsp-file-cursor-x); }  /* press 1 */
  55.0% { opacity: 1; top: var(--anim-wsp-file-cursor-y);   left: var(--anim-wsp-file-cursor-x); }              /* release 1 */
  55.3% { opacity: 1; top: calc(var(--anim-wsp-file-cursor-y) + 0.8%); left: var(--anim-wsp-file-cursor-x); }  /* press 2 */
  55.6% { opacity: 1; top: var(--anim-wsp-file-cursor-y);   left: var(--anim-wsp-file-cursor-x); }              /* release 2 */

  /* Hold while the editor overlay slides in (~12.5-13.1s = 56.8-59.5%). */
  60.0% { opacity: 1; top: var(--anim-wsp-file-cursor-y);   left: var(--anim-wsp-file-cursor-x); }

  /* ~13.4s (60.9%): move into the editor body. Click into the target
     line at 14.4s-ish so typing (14.5s = 65.9%) starts immediately
     after the press. */
  60.9% { opacity: 1; top: var(--anim-wsp-edit-cursor-y);   left: var(--anim-wsp-edit-cursor-x); }
  63.0% { opacity: 1; top: var(--anim-wsp-edit-cursor-y);   left: var(--anim-wsp-edit-cursor-x); }
  63.3% { opacity: 1; top: calc(var(--anim-wsp-edit-cursor-y) + 0.8%); left: var(--anim-wsp-edit-cursor-x); }  /* press */
  63.7% { opacity: 1; top: var(--anim-wsp-edit-cursor-y);   left: var(--anim-wsp-edit-cursor-x); }              /* release */

  /* Hold while typing fake edits (~13.9-15.3s = 63.2-69.5%). */
  70.0% { opacity: 1; top: var(--anim-wsp-edit-cursor-y);   left: var(--anim-wsp-edit-cursor-x); }

  /* ~15.6s (70.9%): move up to the editor tab. Grab/press at 16.0s
     (72.7%) so the drag (16.2s = 73.6%) starts immediately after
     the press registers. */
  70.9% { opacity: 1; top: var(--anim-wsp-tab-cursor-y);    left: var(--anim-wsp-tab-cursor-x); }
  72.3% { opacity: 1; top: var(--anim-wsp-tab-cursor-y);    left: var(--anim-wsp-tab-cursor-x); }
  72.7% { opacity: 1; top: calc(var(--anim-wsp-tab-cursor-y) + 0.6%); left: var(--anim-wsp-tab-cursor-x); }  /* press */

  /* Drag motion: cursor sweeps down + right to the drag-end target,
     dragging the tab ghost with it. Undock at 17.2s (78.2%) - by
     then the cursor is well past the drag-distance threshold. */
  73.6% { opacity: 1; top: var(--anim-wsp-tab-cursor-y);    left: var(--anim-wsp-tab-cursor-x); }              /* drag begins */
  78.0% { opacity: 1; top: var(--anim-wsp-drag-end-y);      left: var(--anim-wsp-drag-end-x); }              /* mid-drag */
  82.0% { opacity: 1; top: var(--anim-wsp-drag-end-y);      left: var(--anim-wsp-drag-end-x); }              /* settled at drop */

  /* Hold while the floating window settles into place. */
  87.0% { opacity: 1; top: var(--anim-wsp-drag-end-y);      left: var(--anim-wsp-drag-end-x); }

  /* Cursor drifts down + fades. */
  92.0% { opacity: 1; top: calc(var(--anim-wsp-drag-end-y) + 5%); left: calc(var(--anim-wsp-drag-end-x) + 4%); }
  96.0% { opacity: 0; top: calc(var(--anim-wsp-drag-end-y) + 8%); left: calc(var(--anim-wsp-drag-end-x) + 6%); }
  100%  { opacity: 0; top: calc(var(--anim-wsp-drag-end-y) + 8%); left: calc(var(--anim-wsp-drag-end-x) + 6%); }
}

/* ─── Keyboard shortcut chip ───────────────────────────────────── */

/* Brief affordance that pops just below the header to signal "Ctrl +
   , was pressed to open Settings". Centered horizontally so it
   doesn't overlap the static window controls / search trigger in the
   top-right of the header. Same look as the static `.am-kbd` in the
   search trigger so it reads as the same widget. */
.am-wsp-kbd-chip {
  position: absolute;
  top: 48px;
  left: 50%;
  padding: 5px 10px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text);
  box-shadow: 0 4px 14px -4px rgba(0, 0, 0, 0.55), 0 0 0 1px color-mix(in srgb, var(--accent) 26%, transparent);
  opacity: 0;
  transform: translate(-50%, -6px) scale(0.95);
  pointer-events: none;
  z-index: 12;
  white-space: nowrap;
}

.am-demo-workspace-plugin.is-playing .am-wsp-kbd-chip {
  animation:
    am-wsp-kbd-in  var(--anim-wsp-kbd-chip-in-dur)  var(--ease) var(--anim-wsp-kbd-chip-in-delay)  forwards,
    am-wsp-kbd-out var(--anim-wsp-kbd-chip-out-dur) var(--ease) var(--anim-wsp-kbd-chip-out-delay) forwards;
}

@keyframes am-wsp-kbd-in {
  to { opacity: 1; transform: translate(-50%, 0) scale(1); }
}
@keyframes am-wsp-kbd-out {
  to { opacity: 0; transform: translate(-50%, -6px) scale(0.96); }
}

/* ─── Settings backdrop ────────────────────────────────────────── */

.am-wsp-settings-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(7, 11, 16, 0.55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  z-index: 5;
}

.am-demo-workspace-plugin.is-playing .am-wsp-settings-backdrop {
  animation:
    am-wsp-fade-in  var(--anim-wsp-settings-in-dur)  var(--ease) var(--anim-wsp-settings-in-delay)  forwards,
    am-wsp-fade-out var(--anim-wsp-settings-out-dur) var(--ease) var(--anim-wsp-settings-out-delay) forwards;
}

@keyframes am-wsp-fade-in  { to { opacity: 1; } }
@keyframes am-wsp-fade-out { to { opacity: 0; } }

/* ─── Settings modal ───────────────────────────────────────────── */

.am-wsp-settings {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 86%;
  max-width: 520px;
  max-height: 78%;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: 10px;
  box-shadow: 0 24px 60px -16px rgba(0, 0, 0, 0.7);
  overflow: hidden;
  opacity: 0;
  transform: translate(-50%, -45%) scale(0.96);
  display: flex;
  flex-direction: column;
  z-index: 6;
}

.am-demo-workspace-plugin.is-playing .am-wsp-settings {
  animation:
    am-wsp-settings-in  var(--anim-wsp-settings-in-dur)  var(--ease) var(--anim-wsp-settings-in-delay)  forwards,
    am-wsp-settings-out var(--anim-wsp-settings-out-dur) var(--ease) var(--anim-wsp-settings-out-delay) forwards;
}

@keyframes am-wsp-settings-in {
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes am-wsp-settings-out {
  to { opacity: 0; transform: translate(-50%, -45%) scale(0.97); }
}

.am-wsp-settings-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-2);
  flex-shrink: 0;
}
.am-wsp-settings-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}
.am-wsp-settings-search {
  flex: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  height: 19px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg-3, var(--bg-2));
  color: var(--text-3);
  font-size: 10px;
  max-width: 220px;
}
.am-wsp-settings-search svg { stroke: currentColor; opacity: 0.75; }
.am-wsp-settings-close {
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  color: var(--text-3);
  background: transparent;
}

.am-wsp-settings-body {
  display: flex;
  flex: 1;
  min-height: 0;
}

/* Sidebar tabs */
.am-wsp-settings-tabs {
  width: 124px;
  min-width: 90px;
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  background: var(--bg-2);
  padding: 6px 4px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  overflow-y: auto;
}
.am-wsp-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  font-size: 10px;
  color: var(--text-2);
  border-radius: 4px;
  white-space: nowrap;
}
.am-wsp-tab-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
  flex-shrink: 0;
}
.am-wsp-tab.is-active {
  color: var(--text);
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  box-shadow: inset 2px 0 0 var(--accent);
}
.am-wsp-tab.is-active .am-wsp-tab-dot {
  background: var(--accent);
  opacity: 1;
}

/* Plugins panel content */
.am-wsp-settings-content {
  flex: 1;
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  overflow: hidden;
}
.am-wsp-settings-heading-title {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}
.am-wsp-settings-heading-sub {
  display: block;
  font-size: 10px;
  color: var(--text-3);
  margin-top: 1px;
}
.am-wsp-plugin-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  overflow: hidden;
}
.am-wsp-plugin-card {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  min-width: 0;
}
.am-wsp-plugin-card.is-enabled {
  border-color: color-mix(in srgb, var(--accent) 32%, var(--border));
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-2));
}
.am-wsp-plugin-card.am-wsp-plugin-target {
  /* Slight emphasis (subtle accent glow) so the eye is already drawn to
     the Workspace row when the modal opens - the cursor's destination
     reads as the "active" affordance even before it arrives. */
  border-color: color-mix(in srgb, var(--accent) 38%, var(--border));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 14%, transparent);
}
.am-wsp-plugin-avatar {
  width: 22px;
  height: 22px;
  border-radius: 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  flex-shrink: 0;
}
.am-wsp-plugin-avatar svg { width: 12px; height: 12px; }
.am-wsp-avatar-git { color: #f0883e; background: rgba(240, 136, 62, 0.12); border-color: rgba(240, 136, 62, 0.30); }
.am-wsp-avatar-voice { color: #c490e8; background: rgba(196, 144, 232, 0.12); border-color: rgba(196, 144, 232, 0.30); }
.am-wsp-avatar-memory { color: #6fbf73; background: rgba(111, 191, 115, 0.12); border-color: rgba(111, 191, 115, 0.30); }
.am-wsp-avatar-workspace { /* keep the accent default - Workspace is the demo's hero */ }
.am-wsp-plugin-info { min-width: 0; }
.am-wsp-plugin-name {
  display: block;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.am-wsp-plugin-desc {
  display: block;
  font-size: 9px;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Toggle switch (mirrors PluginPanel.tsx's plugin-hub-toggle). */
.am-wsp-toggle {
  position: relative;
  width: 24px;
  height: 13px;
  border-radius: 8px;
  background: var(--border);
  border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  flex-shrink: 0;
  transition: background 200ms var(--ease), border-color 200ms var(--ease);
}
.am-wsp-toggle-track {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--text-3);
  transition: transform 220ms var(--ease), background 220ms var(--ease);
}
.am-wsp-toggle.is-on {
  background: var(--anim-wsp-toggle-on-bg);
  border-color: var(--anim-wsp-toggle-on);
}
.am-wsp-toggle.is-on .am-wsp-toggle-track {
  transform: translateX(11px);
  background: #fff;
  box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 65%, transparent);
}

/* Target toggle flips on at toggle-flip-delay. The keyframe animates
   the background + thumb position via a `to`-only frame so the
   "from" state is the off-state declared on `.am-wsp-toggle`. */
.am-demo-workspace-plugin.is-playing .am-wsp-toggle[data-am-anim-el="targetToggle"] {
  animation: am-wsp-toggle-flip var(--anim-wsp-toggle-flip-dur) var(--ease) var(--anim-wsp-toggle-flip-delay) forwards;
}
.am-demo-workspace-plugin.is-playing .am-wsp-toggle[data-am-anim-el="targetToggle"] .am-wsp-toggle-track {
  animation: am-wsp-toggle-thumb var(--anim-wsp-toggle-flip-dur) var(--ease) var(--anim-wsp-toggle-flip-delay) forwards;
}

@keyframes am-wsp-toggle-flip {
  to {
    background: var(--anim-wsp-toggle-on-bg);
    border-color: var(--anim-wsp-toggle-on);
  }
}
@keyframes am-wsp-toggle-thumb {
  to {
    transform: translateX(11px);
    background: #fff;
    box-shadow: 0 0 6px color-mix(in srgb, var(--accent) 65%, transparent);
  }
}

/* Target card gets a subtle confirmation glow on enable. */
.am-demo-workspace-plugin.is-playing .am-wsp-plugin-card.am-wsp-plugin-target {
  animation: am-wsp-card-confirm 0.6s var(--ease) calc(var(--anim-wsp-toggle-flip-delay) + 0.1s) forwards;
}

@keyframes am-wsp-card-confirm {
  0%, 100% { background: color-mix(in srgb, var(--accent) 6%, var(--bg-2)); }
  40%      { background: color-mix(in srgb, var(--accent) 18%, var(--bg-2)); }
}

/* ─── Toggle click pulse ───────────────────────────────────────── */

.am-wsp-toggle-pulse {
  position: absolute;
  left: var(--anim-wsp-toggle-pulse-x);
  top: var(--anim-wsp-toggle-pulse-y);
  width: 28px;
  height: 17px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--accent);
  border-radius: 10px;
  opacity: 0;
  pointer-events: none;
  z-index: 7;
}

.am-demo-workspace-plugin.is-playing .am-wsp-toggle-pulse {
  animation: am-wsp-pulse-out var(--anim-wsp-toggle-pulse-dur) var(--ease) var(--anim-wsp-toggle-pulse-delay) 1 forwards;
}

@keyframes am-wsp-pulse-out {
  0%   { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(1.7); }
}

/* ─── Workspace pin reveal in the header plugin row ────────────── */

/* The new pin is hidden by default (display: none keeps it from claiming
   layout outside the demo) and only revealed during the workspace-plugin
   demo from the `pinned` phase onward. */
.am-plugin-workspace {
  display: none;
}

/* Reveal-only state: pin enters the header row as a NORMAL (inactive)
   pin - same styling as the dormant Tests/Memory/Voice pins. No
   accent background, no glow ring; just the default text-3 colour
   inherited from .am-plugin. This matches production behaviour:
   enabling a plugin pins its icon, but the "active" highlight only
   appears when the user actually opens that plugin's panel. */
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="pinned"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="pin-click"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="panel-open"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="panel-settled"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtering"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtered"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dblclick"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editor-open"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="rest"] .am-plugin-workspace {
  display: inline-flex;
  /* Slide-in animation runs once on the attribute transition; the
     `forwards` fill keeps the pin docked for the rest of the cycle.
     No accent styling here - the pin reads as inactive on reveal. */
  animation: am-wsp-pin-in var(--anim-wsp-pin-reveal-dur) var(--ease) both;
}

/* Active highlight: only kicks in once the cursor has actually clicked
   the pin and the panel is opening / open. Mirrors the production
   `.am-plugin.active` rule (accent background + accent text). */
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="panel-open"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="panel-settled"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtering"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtered"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dblclick"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editor-open"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-plugin-workspace,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="rest"] .am-plugin-workspace {
  background: var(--accent-soft);
  color: var(--accent);
  transition: background 220ms var(--ease), color 220ms var(--ease);
}

@keyframes am-wsp-pin-in {
  0%   { opacity: 0; transform: translateY(-4px) scale(0.6); }
  60%  { opacity: 1; transform: translateY(0)    scale(1.12); }
  100% { opacity: 1; transform: translateY(0)    scale(1); }
}

/* Pin click pulse ring (anchored on the rendered pin via JS-written vars). */
.am-wsp-pin-pulse {
  position: absolute;
  left: var(--anim-wsp-pin-pulse-x);
  top: var(--anim-wsp-pin-pulse-y);
  width: 24px;
  height: 24px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--accent);
  border-radius: 8px;
  opacity: 0;
  pointer-events: none;
  z-index: 7;
}

.am-demo-workspace-plugin.is-playing .am-wsp-pin-pulse {
  animation: am-wsp-pulse-out var(--anim-wsp-pin-pulse-dur) var(--ease) var(--anim-wsp-pin-pulse-delay) 2 forwards;
}

/* ─── Workspace side panel ────────────────────────────────────── */

.am-wsp-panel {
  position: absolute;
  top: 40px;          /* below the header */
  left: 0;
  bottom: 0;          /* statusbar offset handled by the demo wrapper */
  width: 46%;
  min-width: 240px;
  max-width: 360px;
  background: var(--surface);
  border-right: 1px solid var(--border-2);
  box-shadow:
    8px 0 22px -10px rgba(0, 0, 0, 0.55),
    2px 0 0 0 color-mix(in srgb, var(--accent) 14%, transparent);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 0.4;
  transform: translateX(-102%);
  z-index: 5;
}

.am-demo-workspace-plugin.is-playing .am-wsp-panel {
  animation: am-wsp-panel-in var(--anim-wsp-panel-in-dur) var(--ease) var(--anim-wsp-panel-in-delay) forwards;
}

@keyframes am-wsp-panel-in {
  0%   { opacity: 0.4; transform: translateX(-102%); }
  60%  { opacity: 1;   transform: translateX(0);     }
  100% { opacity: 1;   transform: translateX(0);     }
}

.am-wsp-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 5px 9px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-2);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text);
  text-transform: uppercase;
  flex-shrink: 0;
}
.am-wsp-panel-close {
  width: 14px;
  height: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-3);
  border-radius: 3px;
}

.am-wsp-panel-toolbar {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 5px 6px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

/* Filter input (purely visual span - character typing is JS-driven). */
.am-wsp-filter {
  position: relative;
  flex: 1;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 7px;
  height: 18px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text);
  min-width: 0;
}
.am-wsp-filter svg { color: var(--text-3); flex-shrink: 0; }
.am-wsp-filter-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.am-wsp-filter-caret {
  display: inline-block;
  width: 1.5px;
  height: 9px;
  background: var(--accent);
  margin-left: 1px;
  opacity: 0;
  animation: am-blink 1s steps(1) infinite;
}
.am-wsp-filter-placeholder {
  position: absolute;
  left: 22px;
  color: var(--text-3);
  opacity: 0.5;
  pointer-events: none;
}

/* Focus + reveal caret once the cursor "clicks into" the filter input.
   Persists across every post-filter phase so the typed text never
   loses its placeholder-hidden / caret-visible state. */
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtering"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtered"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dblclick"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editor-open"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-wsp-filter,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="rest"] .am-wsp-filter {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 5%, var(--bg-2));
}
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtering"] .am-wsp-filter-caret {
  opacity: 1;
}
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtering"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtered"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dblclick"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editor-open"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-wsp-filter-placeholder,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="rest"] .am-wsp-filter-placeholder {
  opacity: 0;
}

.am-wsp-panel-btn {
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-3);
  background: transparent;
  border: 0;
  border-radius: 3px;
  cursor: default;
  flex-shrink: 0;
  padding: 0;
}

.am-wsp-panel-tree {
  flex: 1;
  overflow: hidden;
  padding: 4px 0;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-2);
}

.am-wsp-tree-row {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px 2px calc(6px + (var(--depth) * 12px));
  white-space: nowrap;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 240ms var(--ease), background 200ms var(--ease);
}
.am-wsp-tree-row[data-depth="0"] { --depth: 0; }
.am-wsp-tree-row[data-depth="1"] { --depth: 1; }
.am-wsp-tree-row[data-depth="2"] { --depth: 2; }
.am-wsp-tree-row[data-depth="3"] { --depth: 3; }

.am-wsp-tree-arrow {
  display: inline-block;
  width: 8px;
  text-align: center;
  font-size: 8px;
  color: var(--text-3);
  flex-shrink: 0;
  transform-origin: center;
}
.am-wsp-tree-row[data-folder="open"] .am-wsp-tree-arrow { transform: rotate(90deg); }
.am-wsp-tree-arrow-spacer {
  display: inline-block;
  width: 8px;
  flex-shrink: 0;
}

.am-wsp-tree-dir { display: inline-flex; flex-shrink: 0; }
.am-wsp-tree-file {
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  border-radius: 2px;
  position: relative;
  background:
    linear-gradient(135deg, transparent 0 60%, color-mix(in srgb, var(--text-3) 35%, transparent) 60% 100%),
    color-mix(in srgb, var(--text-3) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--text-3) 30%, transparent);
}
.am-wsp-tree-file[data-ext="tsx"],
.am-wsp-tree-file[data-ext="ts"]   { border-color: #3178c6; background-color: rgba(49, 120, 198, 0.12); }
.am-wsp-tree-file[data-ext="js"]   { border-color: #d1c358; background-color: rgba(209, 195, 88, 0.14); }
.am-wsp-tree-file[data-ext="json"] { border-color: #a8b577; background-color: rgba(168, 181, 119, 0.14); }
.am-wsp-tree-file[data-ext="md"]   { border-color: #2b80ec; background-color: rgba(43, 128, 236, 0.14); }

.am-wsp-tree-name {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 10px;
}

/* Tree cascade: each row reveals with a staggered delay. The
   `--anim-wsp-tree-cascade-delay` token marks t=0 for the stagger.
   nth-child handles the per-row offset so JS doesn't need to
   touch each row individually. */
.am-demo-workspace-plugin.is-playing .am-wsp-tree-row {
  animation: am-wsp-tree-row-in 0.35s var(--ease) forwards;
  animation-delay: calc(var(--anim-wsp-tree-cascade-delay) + (var(--row-i, 0) * 0.06s));
}
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(1)  { --row-i: 0; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(2)  { --row-i: 1; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(3)  { --row-i: 2; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(4)  { --row-i: 3; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(5)  { --row-i: 4; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(6)  { --row-i: 5; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(7)  { --row-i: 6; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(8)  { --row-i: 7; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(9)  { --row-i: 8; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(10) { --row-i: 9; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(11) { --row-i: 10; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(12) { --row-i: 11; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(13) { --row-i: 12; }
.am-wsp-panel-tree .am-wsp-tree-row:nth-child(14) { --row-i: 13; }

@keyframes am-wsp-tree-row-in {
  to { opacity: 1; transform: translateY(0); }
}

/* Filter applied: non-matching rows dim (kept rendered so the layout
   doesn't reflow under the cursor). The matching rows stay opaque and
   pick up a subtle accent background to read as "selected". */
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtered"] .am-wsp-tree-row:not([data-am-wsp-match]),
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dblclick"] .am-wsp-tree-row:not([data-am-wsp-match]),
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editor-open"] .am-wsp-tree-row:not([data-am-wsp-match]),
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-wsp-tree-row:not([data-am-wsp-match]),
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-wsp-tree-row:not([data-am-wsp-match]),
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-tree-row:not([data-am-wsp-match]),
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-wsp-tree-row:not([data-am-wsp-match]),
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="rest"] .am-wsp-tree-row:not([data-am-wsp-match]) {
  opacity: 0.22;
}
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="filtered"] .am-wsp-tree-row[data-am-wsp-match],
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dblclick"] .am-wsp-tree-row[data-am-wsp-match],
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editor-open"] .am-wsp-tree-row[data-am-wsp-match],
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-wsp-tree-row[data-am-wsp-match],
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-wsp-tree-row[data-am-wsp-match],
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-tree-row[data-am-wsp-match],
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-wsp-tree-row[data-am-wsp-match],
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="rest"] .am-wsp-tree-row[data-am-wsp-match] {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--text);
}

/* ─── File double-click pulse ────────────────────────────────────
   Anchored on the matching tree row the cursor lands on; two
   iterations read as a double-click. Position written by JS into
   --anim-wsp-file-pulse-{x,y} so it tracks whichever row was hit. */
.am-wsp-file-pulse {
  position: absolute;
  left: var(--anim-wsp-file-pulse-x);
  top: var(--anim-wsp-file-pulse-y);
  /* Width is JS-driven from the actual file-name span's rendered
     width so the ring always fits the name on every viewport size.
     The fallback (140px) covers `DockHeaderCompact.tsx` at the
     default font size. */
  width: var(--anim-wsp-file-pulse-w, 140px);
  height: 16px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--accent);
  border-radius: 4px;
  opacity: 0;
  pointer-events: none;
  z-index: 7;
}

.am-demo-workspace-plugin.is-playing .am-wsp-file-pulse {
  animation:
    am-wsp-file-pulse-1 var(--anim-wsp-file-dblclick-pulse-dur) var(--ease) var(--anim-wsp-file-dblclick-1-delay) forwards,
    am-wsp-file-pulse-2 var(--anim-wsp-file-dblclick-pulse-dur) var(--ease) var(--anim-wsp-file-dblclick-2-delay) forwards;
}

@keyframes am-wsp-file-pulse-1 {
  0%   { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(1.4); }
}
@keyframes am-wsp-file-pulse-2 {
  0%   { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(1.4); }
}

/* ─── Docked editor overlay ──────────────────────────────────────
   Production mirror: EditorOverlay.tsx covers the full grid area
   with `inset: 0`. In the demo wrapper the grid sits to the right
   of the workspace panel (panel width: 46% / max 360px), so the
   editor overlay docks starting at the panel's right edge and
   extends to the right edge of the mock. The slide-in mirrors the
   production "paper shuffle" hand-off (subtle translate + opacity). */
.am-wsp-editor {
  position: absolute;
  top: 40px;                      /* below the header */
  left: max(46%, 0px);
  /* Width-derived left: workspace panel is min-width: 240px / max-width:
     360px / 46%, so the editor takes whatever remains. Using max() on
     left + right: 0 means the editor naturally fills the residual. */
  right: 0;
  bottom: 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 0;
  transform: translate3d(-8px, 6px, 0) scale(0.99);
  z-index: 6;
  pointer-events: none;
}

/* Override left to honour the panel's max-width on wider viewports
   (so the editor doesn't leave a gap beside a 360px-capped panel). */
@media (min-width: 800px) {
  .am-wsp-editor {
    left: min(46%, 360px);
  }
}

.am-demo-workspace-plugin.is-playing .am-wsp-editor {
  animation:
    am-wsp-editor-in var(--anim-wsp-editor-in-dur) var(--ease) var(--anim-wsp-editor-in-delay) forwards,
    am-wsp-editor-out var(--anim-wsp-undock-dur) var(--ease) var(--anim-wsp-undock-delay) forwards;
}

@keyframes am-wsp-editor-in {
  0%   { opacity: 0; transform: translate3d(-8px, 6px, 0) scale(0.99); }
  100% { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}

/* On undock: the docked overlay slides + tilts away (mirrors the
   paper-shuffle keyframe in global.css) and fades to 0 as the
   floating window fades in over it. */
@keyframes am-wsp-editor-out {
  0%   { opacity: 1; transform: translate3d(0, 0, 0) rotate(0deg); }
  100% { opacity: 0; transform: translate3d(14px, 18px, 0) rotate(0.8deg); }
}

/* Editor tab bar (mirrors .editor-tab-bar). */
.am-wsp-editor-tabbar {
  display: flex;
  align-items: stretch;
  height: 26px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  overflow: hidden;
}
.am-wsp-editor-tabs {
  display: flex;
  align-items: stretch;
  flex: 1 1 0;
  min-width: 0;
}
.am-wsp-editor-tab {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0 9px;
  height: 100%;
  font-size: 10px;
  color: var(--text-2);
  border-right: 1px solid var(--border);
  white-space: nowrap;
  font-family: var(--font-mono);
}
.am-wsp-editor-tab.is-active {
  color: var(--text);
  background: var(--surface);
  /* Accent underline matches the production `.editor-tab-active`. */
  box-shadow: inset 0 -2px 0 var(--accent);
}
.am-wsp-editor-tab-icon {
  width: 9px;
  height: 9px;
  border-radius: 1.5px;
  background:
    linear-gradient(135deg, transparent 0 60%, color-mix(in srgb, #3178c6 35%, transparent) 60% 100%),
    color-mix(in srgb, #3178c6 18%, transparent);
  border: 1px solid #3178c6;
  flex-shrink: 0;
}
.am-wsp-editor-tab-name { color: inherit; font-family: var(--font-mono); }
.am-wsp-editor-tab-dirty {
  color: var(--accent);
  font-size: 8px;
  opacity: 0;             /* hidden until typing makes the buffer dirty */
  transition: opacity 200ms var(--ease);
}
.am-wsp-editor-tab-close {
  font-size: 11px;
  color: var(--text-3);
  margin-left: 2px;
}
.am-wsp-editor-actions {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 0 5px;
  flex-shrink: 0;
  background: var(--bg-2);
  box-shadow: -6px 0 6px -4px rgba(0, 0, 0, 0.35);
}
.am-wsp-editor-btn {
  width: 17px;
  height: 17px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-3);
  background: transparent;
  border: 0;
  border-radius: 3px;
  cursor: default;
  padding: 0;
}
.am-wsp-editor-close-all { color: var(--text-3); font-size: 14px; margin-left: 2px; padding: 0 4px; }

/* Editor body: gutter + code + minimap, mirrors Monaco's layout.
   Line-height stays tight (1.35) - matches the production Monaco
   default for the dock's font size and keeps the buffer reading as
   code instead of leaving each line floating in a band of whitespace. */
.am-wsp-editor-body {
  flex: 1;
  display: grid;
  grid-template-columns: 24px 1fr 24px;
  min-height: 0;
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--text-2);
  overflow: hidden;
}
.am-wsp-editor-gutter {
  background: var(--bg-2);
  color: var(--text-3);
  font-size: 9.5px;
  text-align: right;
  padding: 4px 4px 0 0;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  user-select: none;
  line-height: 1.35;
}
.am-wsp-editor-gutter span { display: block; line-height: 1.35; }
.am-wsp-editor-code {
  padding: 4px 8px;
  overflow: hidden;
  white-space: pre;
  position: relative;
}
.am-wsp-editor-line {
  display: block;
  /* Lock line-height per row so &nbsp; blank lines (used as visual
     separators in the buffer) don't pick up the parent line-height
     and balloon vertically - keeps the sample reading like code. */
  line-height: 1.35;
  min-height: 1.35em;
}
.am-wsp-editor-line-target {
  /* Subtle active-line highlight so the user reads it as the cursor's
     resting line. The accent shifts in once the cursor clicks the line
     (phase=editing); before that it's a faint background hint. */
  background: color-mix(in srgb, var(--accent) 5%, transparent);
  margin: 0 -8px;
  padding: 0 8px;
  transition: background 220ms var(--ease);
}
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-wsp-editor-line-target,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-editor-line-target,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-wsp-editor-line-target {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* Monaco-style syntax tokens (no real highlighting; just enough
   colour separation that the buffer reads as code). */
.am-wsp-kw   { color: #c678dd; }   /* keywords */
.am-wsp-str  { color: #98c379; }   /* strings */
.am-wsp-tok  { color: #61afef; }   /* identifiers */
.am-wsp-fn   { color: #61afef; }   /* function names */
.am-wsp-tag  { color: #e06c75; }   /* JSX tags */
.am-wsp-attr { color: #d19a66; }   /* attribute names */
.am-wsp-prop { color: #e5c07b; }   /* property access */
.am-wsp-ind  { color: transparent; user-select: text; }   /* indent spaces (preserved) */
.am-wsp-typed-new {
  color: var(--text);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius: 2px;
  padding: 0 1px;
}

/* Editor caret (inline blinker inside the target line). */
.am-wsp-editor-caret {
  display: inline-block;
  width: 1.5px;
  height: 11px;
  background: var(--accent);
  margin: 0 1px;
  vertical-align: middle;
  opacity: 0;
}
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-wsp-editor-caret,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-wsp-editor-caret,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-editor-caret {
  animation: am-blink 1s steps(1) infinite;
}

/* Minimap rail. Each <span> is a "compressed line" - widths drive
   the bar-chart silhouette that real Monaco minimaps render. */
.am-wsp-editor-minimap {
  background: var(--bg-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 5px 3px;
  border-left: 1px solid var(--border);
}
.am-wsp-editor-minimap span {
  display: block;
  height: 3px;
  background: color-mix(in srgb, var(--text-3) 32%, transparent);
  border-radius: 1px;
}
/* Per-bar widths for the decorative minimap. Kept in CSS (not inline style=
   attributes) so the strict CSP style-src with no 'unsafe-inline' applies. */
.am-wsp-editor-minimap span:nth-child(1) { width: 56%; }
.am-wsp-editor-minimap span:nth-child(2) { width: 78%; }
.am-wsp-editor-minimap span:nth-child(3) { width: 64%; }
.am-wsp-editor-minimap span:nth-child(4) { width: 8%; }
.am-wsp-editor-minimap span:nth-child(5) { width: 72%; }
.am-wsp-editor-minimap span:nth-child(6) { width: 84%; }
.am-wsp-editor-minimap span:nth-child(7) { width: 90%; }
.am-wsp-editor-minimap span:nth-child(8) { width: 50%; }
.am-wsp-editor-minimap span:nth-child(9) { width: 56%; }
.am-wsp-editor-minimap span:nth-child(10) { width: 38%; }
.am-wsp-editor-minimap span:nth-child(11) { width: 14%; }

/* Dirty indicator: revealed once the typed-edit is in flight. */
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="editing"] .am-wsp-editor-tab-dirty,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-wsp-editor-tab-dirty,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-editor-tab-dirty,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-wsp-editor-tab-dirty {
  opacity: 1;
}

/* ─── Tab ghost (drag preview) ───────────────────────────────────
   Appears only during the `tab-grab` + `dragging` phases. Visually
   mirrors the active editor tab (icon + name + dirty dot) and lifts
   slightly off the surface via shadow + scale so it reads as a
   detached drag preview. JS pins its position to the cursor's
   current target every frame. */
.am-wsp-tab-ghost {
  position: absolute;
  left: var(--anim-wsp-tab-cursor-x);
  top: var(--anim-wsp-tab-cursor-y);
  /* Anchor the ghost so the cursor sits over its left edge - this is
     where the production drag handle sits visually. */
  transform: translate(-12px, 4px) scale(0.96);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  height: 22px;
  background: var(--bg-2);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text);
  box-shadow: 0 10px 22px -10px rgba(0, 0, 0, 0.6), 0 0 0 1px color-mix(in srgb, var(--accent) 20%, transparent);
  opacity: 0;
  pointer-events: none;
  z-index: 11;
}

.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="tab-grab"] .am-wsp-tab-ghost,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-tab-ghost {
  opacity: 1;
  /* Match the cursor's tab->drag-end sweep duration so the ghost and
     cursor arrive at the drop point together. The cursor keyframe
     moves from 73.6% to 78% of a 22s cycle = 0.97s; round to 1s. */
  transition: left 1s linear, top 1s linear, transform 1s var(--ease);
}

.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="dragging"] .am-wsp-tab-ghost {
  /* Once the drag truly begins (past the 20px-equivalent threshold)
     the ghost follows the cursor's drag-end target. The 1s transition
     above keeps the ghost glued to the cursor across the sweep. */
  left: var(--anim-wsp-drag-end-x);
  top: var(--anim-wsp-drag-end-y);
  transform: translate(-12px, 4px) scale(1) rotate(2deg);
}

/* ─── Floating undocked editor window ────────────────────────────
   Appears once the drag has crossed the undock threshold. Anchored
   near the drop point with rounded corners + dropdown shadow so it
   reads as a separate OS window, not part of the dock. Same chrome
   as the production detached editor: titlebar (file + project) +
   window-controls cluster, then tab bar, then body. */
.am-wsp-floating-editor {
  position: absolute;
  left: var(--anim-wsp-drag-end-x);
  top: var(--anim-wsp-drag-end-y);
  width: 50%;
  max-width: 320px;
  min-width: 220px;
  min-height: 150px;
  height: 56%;
  max-height: 210px;
  /* Anchor so the cursor lands near the floating window's titlebar.
     Translate -25%/-10% pulls the window up + left from the drop
     point so it sits comfortably inside the mock (vs. extending off
     the bottom-right when the drop was farther down + right). */
  transform: translate(-25%, -10%) scale(0.86);
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: 6px;
  box-shadow:
    0 22px 50px -16px rgba(0, 0, 0, 0.72),
    0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  z-index: 7;
}

.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="undocked"] .am-wsp-floating-editor,
.app-mock[data-active-demo="workspace-plugin"][data-wsp-phase="rest"] .am-wsp-floating-editor {
  animation: am-wsp-floating-in var(--anim-wsp-undock-dur) var(--ease) forwards;
}

@keyframes am-wsp-floating-in {
  0%   { opacity: 0; transform: translate(-25%, -10%) scale(0.86); }
  100% { opacity: 1; transform: translate(-25%, -10%) scale(1);    }
}

.am-wsp-floating-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 8px;
  height: 22px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.am-wsp-floating-title-group {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.am-wsp-floating-title {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
.am-wsp-floating-project {
  font-size: 9.5px;
  color: var(--text-3);
  white-space: nowrap;
}
.am-wsp-floating-wincontrols {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.am-wsp-floating-wc {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--text-3);
  opacity: 0.65;
}
.am-wsp-floating-wc.close { background: #e06c75; opacity: 1; }

.am-wsp-floating-tabbar {
  display: flex;
  height: 20px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.am-wsp-floating-tab {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
  font-size: 9.5px;
  color: var(--text-2);
}
.am-wsp-floating-tab.is-active {
  color: var(--text);
  background: var(--surface);
  box-shadow: inset 0 -2px 0 var(--accent);
}
.am-wsp-floating-body {
  flex: 1;
  display: grid;
  grid-template-columns: 18px 1fr;
  min-height: 0;
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 9.5px;
  line-height: 1.35;
  color: var(--text-2);
  overflow: hidden;
}
.am-wsp-floating-body .am-wsp-editor-gutter {
  font-size: 8.5px;
  padding-top: 3px;
}
.am-wsp-typed-mirror {
  color: var(--text);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius: 2px;
  padding: 0 1px;
}

/* ─── Reduced-motion + small-viewport opt-outs ─────────────────── */

@media (prefers-reduced-motion: reduce) {
  .am-demo { display: none !important; }
}

/* ─── Tablet / mobile (<= 720px): adapt demos, don't hide ─────────
 *
 * The static mock already restructures at <= 720px (see the mobile
 * media block farther down in this file): grid goes single-column,
 * the 3rd terminal hides, spaces strip hides, etc. The animations
 * follow suit:
 *
 *   - browser-plugin's docked panel goes full-width (the right-half
 *     "dock" metaphor doesn't translate to a single-column mock).
 *   - both modals shrink so they fit narrower viewports.
 *   - terminal-spawn's mobile-time grid override keeps the shell +
 *     new terminals visible during the demo (the default mobile
 *     rule `.am-term:nth-child(n+3) { display: none }` would
 *     otherwise hide them, and the close-shell stage would have
 *     nothing to close).
 *
 * Because the cursor / box / pulse positions are all percentages of
 * the demo wrapper, they automatically scale with the new wrapper
 * size; the JS measurement pass on each cycle re-pins target rects
 * to the rendered elements, so there's nothing tablet-specific to
 * configure.
 */
@media (max-width: 720px) {
  /* Browser-plugin: panel covers the full grid area on mobile. */
  .am-demo-browser-plugin {
    width: 100%;
    min-width: 0;
    max-width: none;
  }

  /* Inspector modal: scale down to fit the narrower panel. */
  .am-anim-modal {
    width: 92%;
    max-width: 380px;
  }
  /* The inspector preview image strip cap was tuned for desktop;
   * shave a bit on mobile so the modal still fits inside the panel. */
  .am-anim-modal-shot { padding: 8px 12px; }

  /* New-agent popover: narrower so it doesn't overflow the mock's
     right edge when anchored under the + button on phone widths.
     The JS pos vars (--anim-spawn-popover-{x,y}) keep the anchor
     under the + button regardless of viewport. */
  .am-spawn-modal {
    width: 200px;
    max-width: 64%;
  }

  /* Terminal-spawn: the static mobile rule hides .am-term past the
   * 2nd child so only claude + codex render by default. While the
   * demo is active, keep the shell visible (so the close stage has
   * something to close) and reveal the new terminal during the
   * spawn phases. */
  .app-mock[data-active-demo="terminal-spawn"] .am-term-shell {
    display: flex;
  }
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="spawning"]        .am-term-new,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="settled"]         .am-term-new,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-term-new,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"]     .am-term-new,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"]         .am-term-new,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"]          .am-term-new {
    display: flex;
  }

  /* Mobile grid is single-column with `height: auto`; the desktop's
   * explicit `grid-row` / `grid-column` placements (claude+new on the
   * left, codex+shell on the right) would inflate the grid into 2
   * columns again on mobile. Reset every demo-controlled placement so
   * the column flows naturally, and use `1.4fr 1fr ...` row sizing so
   * claude stays the dominant tile. */
  .app-mock[data-active-demo="terminal-spawn"] .am-term-large,
  .app-mock[data-active-demo="terminal-spawn"] .am-term-new,
  .app-mock[data-active-demo="terminal-spawn"] .am-term[data-am-term="codex"],
  .app-mock[data-active-demo="terminal-spawn"] .am-term-shell {
    grid-row: auto;
    grid-column: auto;
  }
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="spawning"]        .am-grid,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="settled"]         .am-grid,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="cursor-to-close"] .am-grid,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="close-click"]     .am-grid,
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closing"]         .am-grid {
    grid-template-rows: 1.4fr 1fr 1fr 1fr;
  }
  .app-mock[data-active-demo="terminal-spawn"][data-spawn-phase="closed"] .am-grid {
    grid-template-rows: 1.4fr 1fr 1fr;
  }

  /* Workspace-plugin: settings modal + side panel shrink to fit. The
     percentage-based panel width caps below 360px on narrow screens,
     and the inner settings modal scales to ~94% so it still fits with
     room to breathe around the dimmed backdrop. */
  .am-wsp-settings {
    width: 94%;
    max-width: 440px;
    max-height: 82%;
  }
  .am-wsp-settings-tabs {
    width: 96px;
    min-width: 80px;
  }
  .am-wsp-tab { font-size: 9.5px; padding: 4px 6px; }
  .am-wsp-panel {
    width: 60%;
    max-width: none;
    min-width: 0;
  }
  .am-wsp-kbd-chip { font-size: 9px; padding: 3px 6px; }
}

/* ─── Very narrow phones (<= 380px): keep static mock only ─────────
 *
 * Below 380px the modals get cramped, the cursor sprite loses
 * legibility against the typography, and there isn't enough vertical
 * room to read the multi-step sequence. The static mock already
 * carries the marketing message at this size.
 */
@media (max-width: 380px) {
  .am-demo { display: none !important; }
}

/* ====== Strip (adapters bar) ====== */
.strip {
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: var(--bg-2);
}
.strip-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 22px;
  /* Longhand vertical padding only - .container handles horizontal so the
     padding adapts naturally at each breakpoint. */
  padding-top: 20px;
  padding-bottom: 20px;
  flex-wrap: wrap;
}
.strip-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  flex-shrink: 0;
}
.strip-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: 10px 22px;
  flex-wrap: wrap;
}
.strip-list li {
  color: var(--text-2);
  font-size: 14px;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: 9px;
}
.strip-list .dot { flex-shrink: 0; }

/* ====== Why grid ====== */
/* Longhand padding so a `.container { padding: 0 X }` redeclaration in a
   media query (which also touches the shorthand) can't wipe the vertical
   padding when this section uses both classes on the same element. */
.why {
  padding-top: clamp(72px, 9vw, 110px);
  padding-bottom: clamp(60px, 8vw, 100px);
}

.why-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}
.why-card {
  padding: 28px 26px;
  background: linear-gradient(180deg, var(--surface) 0%, var(--bg-2) 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  position: relative;
  overflow: hidden;
  transition: transform 0.2s var(--ease), border-color 0.2s var(--ease);
}
.why-card:hover {
  transform: translateY(-2px);
  border-color: var(--border-2);
}
.why-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-soft), transparent);
}
.why-icon {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: var(--accent-soft);
  border: 1px solid rgba(92, 193, 212, 0.18);
  color: var(--accent);
  display: grid;
  place-items: center;
  margin-bottom: 18px;
}
.why-card h3 { margin-bottom: 8px; font-size: 1.05rem; }
.why-card p { color: var(--text-2); font-size: 14px; }

/* ====== Features grid ====== */
.features {
  padding-top: clamp(40px, 7vw, 80px);
  padding-bottom: clamp(40px, 7vw, 80px);
}

/* Auto-scrolling, drag-able feature carousel that dissolves into the
   page background at both edges. Motion is JS-driven (script.js); this
   stylesheet owns the look, the seamless edge mask, and a no-JS /
   reduced-motion fallback (native horizontal scroll with snap). */
/* Pull the carousel up under its section heading. */
.features .section-head { margin-bottom: 8px; }

.cohort-marquee {
  position: relative;
  margin-top: 0;
  /* Top stays as tuned; the deck's accent-glow room lives in the live
     track height (not here), so the bottom padding is modest and the
     section's footprint lines up with the others. */
  padding: 22px 0 30px;
  overflow-x: auto;            /* fallback: native scroll when JS is off */
  overflow-y: visible;
  cursor: grab;
  /* Fallback (no-JS / reduced-motion): allow BOTH the horizontal list
     swipe and vertical page scroll on touch. Live mode narrows this to
     pan-y below so JS owns the horizontal drag. */
  touch-action: pan-x pan-y;
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* The mask makes the strip fade to fully transparent at both ends,
     revealing the page background — seamless regardless of its colour. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
.cohort-marquee::-webkit-scrollbar { width: 0; height: 0; display: none; }
.cohort-marquee:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: var(--radius-lg);
}
/* JS-live: transform-driven infinite loop; native scroll suppressed.
   JS owns the horizontal drag, the browser keeps vertical page scroll
   so a touch swipe down the page still works over the strip. */
.cohort-marquee.is-live {
  overflow: hidden;
  touch-action: pan-y;
}
.cohort-marquee.is-dragging { cursor: grabbing; user-select: none; }

.cohort-track {
  display: flex;
  gap: 16px;
  width: max-content;
  will-change: transform;
}
/* Fallback (no JS / reduced-motion): flat, natively scrollable row. */
.cohort-marquee:not(.is-live) { scroll-snap-type: x mandatory; }
.cohort-marquee:not(.is-live) .cohort-card { scroll-snap-align: center; }

.cohort-card {
  position: relative;
  flex: 0 0 auto;
  /* Flat, sleek strip card: solid surface + hairline border, no
     gradient or rest shadow, so it sits cleanly in the app's palette.
     Sized ~7% down from 288x312 to line up with the site's rhythm. */
  width: clamp(228px, 22vw, 268px);
  height: clamp(256px, 22.3vw, 290px);
  padding: 24px 22px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  transition: transform 0.35s var(--ease), border-color 0.25s var(--ease), box-shadow 0.35s var(--ease);
}
.cohort-marquee.is-live .cohort-card:hover,
.cohort-card:focus-within {
  transform: translateY(-4px);
  border-color: var(--border-2);
  box-shadow: 0 18px 40px -30px rgba(0, 0, 0, 0.85);
}

.cohort-card__icon {
  width: 54px;
  height: 54px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  color: var(--accent);
  background: var(--surface-3);
  border: 1px solid var(--border);
}
/* Single source of truth for icon sizing — every glyph is a 24×24
   stroke icon, so they all render at exactly this size. */
.cohort-card__icon svg { width: 22px; height: 22px; display: block; }
.cohort-card__text { margin-top: auto; }
.cohort-card__text h3 {
  font-size: 17px;
  margin: 0 0 8px;
  letter-spacing: -0.01em;
}
.cohort-card__text p {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-2);
}
.cohort-card__text code { font-size: 0.86em; }
.cohort-card__no {
  position: absolute;
  top: 22px;
  right: 24px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}
.cohort-card__wm {
  position: absolute;
  right: -26px;
  bottom: -32px;
  color: rgba(255, 255, 255, 0.022);
  pointer-events: none;
  user-select: none;
  transition: color 0.4s var(--ease);
}
/* Oversized, hairline echo of the same icon. Thin stroke keeps it a
   subtle texture rather than a heavy blob at this scale. */
.cohort-card__wm svg {
  width: 150px;
  height: 150px;
  display: block;
  stroke-width: 0.5;
}
.cohort-card:hover .cohort-card__wm { color: rgba(255, 255, 255, 0.04); }

/* One restrained brand accent for every card. The per-feature rainbow
   read as shiny / out of place next to the rest of the app, which uses
   a single cyan accent on muted surfaces. data-accent attrs are now
   inert (kept in the markup, harmless). */
.cohort-card { --ca-rgb: 92, 193, 212; }

/* ====== Plugins preview ====== */
.plugins-preview {
  padding: clamp(60px, 8vw, 100px) 0;
  background:
    radial-gradient(700px 400px at 100% 50%, rgba(92, 193, 212, 0.06), transparent 70%),
    var(--bg);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.plugin-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}
.plugin-card {
  padding: 22px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: transform 0.2s var(--ease), border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.plugin-card:hover {
  transform: translateY(-2px);
  border-color: var(--accent-soft);
  background: var(--surface-2);
}
.plugin-icon {
  width: 36px;
  height: 36px;
  border-radius: 9px;
  background: var(--accent-soft);
  border: 1px solid rgba(92, 193, 212, 0.18);
  color: var(--accent);
  display: grid;
  place-items: center;
  margin-bottom: 14px;
}
.plugin-card h3 { font-size: 15px; margin-bottom: 6px; }
.plugin-card p { font-size: 13px; color: var(--text-2); line-height: 1.55; }

.plugin-card-feature {
  background: linear-gradient(180deg, rgba(92, 193, 212, 0.06) 0%, var(--surface) 100%);
  border-color: rgba(92, 193, 212, 0.20);
}
.plugin-card-feature .plugin-icon {
  background: rgba(92, 193, 212, 0.16);
}

/* Build-your-own call-out under the plugin grid. Intentionally quiet:
   no background, no accent border, just a hairline top divider so it
   reads as an aside to the plugin grid rather than a competing focal
   block. The link-arrow on the right keeps the action discoverable
   without commanding attention.

   Vertical rhythm: the call-out sits roughly a section's worth of
   breathing room below the grid (margin-top + padding-top together
   total ~104px), which matches the parent section's own vertical
   padding (`clamp(60px, 8vw, 100px)`) so the aside reads as a
   peer-level beat rather than a crammed appendix to the cards. */
.plugin-byo {
  margin-top: 64px;
  padding-top: 40px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.plugin-byo-text { max-width: 640px; min-width: 0; }
.plugin-byo h3 {
  font-size: 15px;
  font-weight: 600;
  margin: 0 0 4px;
  color: var(--text);
}
.plugin-byo p {
  font-size: 13.5px;
  color: var(--text-2);
  line-height: 1.55;
  margin: 0;
}
.plugin-byo .link-arrow { flex-shrink: 0; }

/* Plugin card / built-in head row - keeps the title and any status
   badges (e.g. Experimental) on the same baseline. */
.plugin-card-head,
.builtin-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
/* Higher specificity than `.plugin-card h3` and `.builtin-body h3` so the
   sibling-set heading margins don't extend the flex item and shift its
   visible text off-axis from the badge's optical center. */
.plugin-card .plugin-card-head h3,
.builtin .builtin-head h3 { margin: 0; }

/* Status badge - generic. Variants set their own color tokens.
   Fixed height + line-height:1 keeps the pill's optical center
   exactly halfway up its box, so flex `align-items: center` against
   a larger heading produces a clean visual baseline. */
.plugin-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 18px;
  padding: 0 9px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  border-radius: 999px;
  white-space: nowrap;
  flex-shrink: 0;
}
.plugin-badge svg {
  flex-shrink: 0;
  display: block;
}

/* Experimental - amber to telegraph "in early development". Mirrors
   the in-app plugin-hub badge (`experimental` flag in plugin.ts). */
.plugin-badge.experimental {
  color: var(--amber);
  background: rgba(217, 160, 80, 0.12);
  border: 1px solid rgba(217, 160, 80, 0.32);
}

/* ====== Check list (plan / comparison feature bullets) ====== */
.check-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.check-list li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  color: var(--text-2);
  font-size: 14px;
  line-height: 1.55;
}
.check-list .check {
  color: var(--accent);
  font-weight: 600;
  margin-top: -1px;
  flex-shrink: 0;
}

/* ====== Pricing ====== */
.pricing {
  padding: clamp(60px, 8vw, 100px) 0;
  border-top: 1px solid var(--border);
  background:
    radial-gradient(800px 400px at 50% 100%, rgba(92, 193, 212, 0.05), transparent 70%),
    var(--bg);
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: 40px;
}

.plan {
  display: flex;
  flex-direction: column;
  padding: 30px 26px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  position: relative;
  transition: border-color 0.2s var(--ease), transform 0.2s var(--ease);
}
.plan:hover { transform: translateY(-2px); border-color: var(--border-2); }

.plan-feature {
  background: linear-gradient(180deg, rgba(92, 193, 212, 0.08) 0%, var(--surface) 60%);
  border-color: rgba(92, 193, 212, 0.30);
  box-shadow: 0 18px 50px -28px var(--accent-glow);
}

.plan-badge {
  position: absolute;
  top: 14px;
  right: 18px;
  padding: 4px 10px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  background: var(--accent);
  color: #0a1518;
  border-radius: 999px;
}

.plan-head { margin-bottom: 22px; padding-bottom: 22px; border-bottom: 1px solid var(--border); }
.plan-name {
  font-size: 18px;
  font-weight: 600;
  margin-bottom: 4px;
  color: var(--text);
}
.plan-tagline { color: var(--text-3); font-size: 13px; margin-bottom: 16px; }
.plan-price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 6px;
}
.plan-price-amount {
  font-family: var(--font-mono);
  font-size: 38px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.02em;
  line-height: 1;
}
.plan-price-period { color: var(--text-3); font-size: 13px; }
.plan-meta {
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.plan-features {
  list-style: none;
  margin: 0 0 26px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.plan-features li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  color: var(--text-2);
  font-size: 13.5px;
  line-height: 1.55;
}
.plan-features .check {
  color: var(--accent);
  font-weight: 600;
  flex-shrink: 0;
  margin-top: 0;
}
.plan-features .coming {
  color: var(--text-muted);
  font-size: 11px;
  font-style: italic;
}
.plan-cta { width: 100%; justify-content: center; }

/* ===== Trial card (Solo pinned as a free trial) ===== */
/* The Solo card gains an accent ring + a "pinned" corner sticker so it
   reads as a trial offer placed on top of the plain Solo plan. */
.plan.is-trial {
  background: linear-gradient(180deg, rgba(92, 193, 212, 0.10) 0%, var(--surface) 60%);
  border-color: rgba(92, 193, 212, 0.45);
  box-shadow:
    0 0 0 1px rgba(92, 193, 212, 0.25),
    0 20px 54px -26px var(--accent-glow);
}
.plan-trial-pin {
  position: absolute;
  top: -10px;
  right: 16px;
  transform: rotate(5deg);
  padding: 5px 11px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #0a1518;
  background: var(--accent);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 6px;
  box-shadow: 0 6px 16px -6px var(--accent-glow);
  user-select: none;
}
/* $0 is the hero during the trial; the regular price is struck through
   and shrunk beside it so the buyer still sees what they'll pay once
   the trial ends, and a subline restates it in plain words. */
.plan.is-trial .plan-price { flex-wrap: wrap; }
.plan-price-zero {
  font-family: var(--font-mono);
  font-size: 38px;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: -0.02em;
  line-height: 1;
}
.plan-price-zero-for {
  color: var(--accent-2, var(--accent));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  align-self: baseline;
}
.plan-price-regular {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
}
.plan.is-trial .plan-price-regular {
  text-decoration: line-through;
  text-decoration-color: var(--text-muted);
  opacity: 0.6;
}
.plan.is-trial .plan-price-amount { font-size: 17px; font-weight: 600; }
.plan.is-trial .plan-price-period { font-size: 12px; }
.plan-trial-after {
  color: var(--text-3);
  font-size: 12.5px;
  line-height: 1.5;
  margin: 8px 0 14px;
}
/* Subtle text-style controls (not primary buttons): themed via these
   classes so they never fall back to the platform button look. */
.plan-trial-skip,
.plan-trial-restore {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  width: 100%;
  margin-top: 10px;
  padding: 4px 0;
  font: inherit;
  font-size: 12px;
  text-align: center;
  cursor: pointer;
  transition: color 0.15s var(--ease);
}
.plan-trial-skip { color: var(--text-3); }
.plan-trial-skip:hover { color: var(--text); }
.plan-trial-restore { color: var(--accent); }
.plan-trial-restore:hover { color: var(--accent-2, var(--accent)); }
.plan-trial-skip:focus-visible,
.plan-trial-restore:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ============================================================
   Tiered-pricing / seats / discount / period-toggle UI
   Added for the seat-quantity + yearly-period feature. Designed
   to inherit the existing dark theme tokens (--text, --text-2,
   --text-3, --border, --accent).
   ============================================================ */

.period-toggle {
  display: flex;
  width: max-content;
  align-items: center;
  gap: 4px;
  padding: 4px;
  margin: 0 auto 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.period-toggle-btn {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--text-2);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 8px 18px;
  border-radius: 7px;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.period-toggle-btn:hover { color: var(--text); }
.period-toggle-btn.is-active {
  background: var(--text);
  color: var(--bg);
}
.period-toggle-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.period-toggle-save {
  display: inline-block;
  margin-left: 6px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #5fc488;
}
.period-toggle-btn.is-active .period-toggle-save {
  color: #1f7a45;
}

/* Discount badge (above the price when plan.discount is set).
   Uses a lighter-than-token tint so the badge POPS against the dark
   card surface. --amber (#d9a050) is the closest existing token but
   too muted at this size — these brighter values are deliberately
   badge-specific. If a future design pass adds an --amber-bright
   token, swap to that and drop the literal hex. */
.plan-discount-badge {
  display: inline-block;
  margin-bottom: 8px;
  padding: 2px 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #ffe28a;
  background: rgba(255, 182, 60, 0.12);
  border: 1px solid rgba(255, 182, 60, 0.4);
  border-radius: 4px;
}

/* Strikethrough "was" price next to the live amount. */
.plan-price-compare-at {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--text-3);
  margin-left: 4px;
}

/* Live "save N%" indicator next to the per-seat price (updates as the
   user steps the seat count). */
.plan-price-savings {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #5fc488;
  background: rgba(95, 196, 136, 0.12);
  border: 1px solid rgba(95, 196, 136, 0.35);
  border-radius: 3px;
}

.plan-stepper {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 12px;
}
.plan-stepper .stepper-label {
  font-size: 12px;
  color: var(--text-3);
}
.plan-stepper .stepper-group {
  display: inline-flex;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.plan-stepper .stepper-btn {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--text-2);
  padding: 4px 10px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease;
}
.plan-stepper .stepper-btn:hover { color: var(--text); background: rgba(255,255,255,0.04); }
.plan-stepper .stepper-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.plan-stepper .stepper-input {
  appearance: textfield;
  -moz-appearance: textfield;
  width: 48px;
  background: transparent;
  border: 0;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 13px;
  text-align: center;
  padding: 4px 0;
  font-variant-numeric: tabular-nums;
}
.plan-stepper .stepper-input::-webkit-inner-spin-button,
.plan-stepper .stepper-input::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.plan-stepper .stepper-input:focus { outline: 1px solid var(--accent); }
.plan-stepper .stepper-cap {
  font-size: 10px;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.plan-tier-summary {
  font-size: 12px;
  color: #5fc488;
  margin: 0 0 8px;
}
.plan-tier-summary strong {
  color: #7fd5a3;
  font-weight: 600;
}

.plan-tier-breakdown {
  margin: 0 0 16px;
}
.plan-tier-breakdown summary {
  font-size: 12px;
  color: var(--text-3);
  cursor: pointer;
  user-select: none;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.plan-tier-breakdown summary::-webkit-details-marker { display: none; }
.plan-tier-breakdown summary::before {
  content: '▸';
  font-size: 10px;
  transition: transform 120ms ease;
}
.plan-tier-breakdown[open] summary::before {
  transform: rotate(90deg);
}
.plan-tier-breakdown summary:hover { color: var(--text-2); }
.plan-tier-table {
  width: 100%;
  margin-top: 8px;
  border-collapse: collapse;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.plan-tier-table thead th {
  font-weight: 400;
  color: var(--text-muted);
  text-align: left;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.plan-tier-table thead th:last-child { text-align: right; }
.plan-tier-table tbody th {
  font-weight: 400;
  color: var(--text-2);
  text-align: left;
  padding: 5px 0;
}
.plan-tier-table tbody td {
  color: var(--text);
  text-align: right;
  padding: 5px 0;
}
.plan-tier-table tbody tr + tr th,
.plan-tier-table tbody tr + tr td {
  border-top: 1px solid var(--border);
}

.waitlist { display: flex; justify-content: center; }
.waitlist-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 22px 26px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  width: 100%;
  max-width: 700px;
  flex-wrap: wrap;
}
.waitlist-card h3 { font-size: 15px; margin-bottom: 4px; }
.waitlist-card p { font-size: 13px; color: var(--text-3); }

/* Offer variant: subtle warm wash + accent border so the 30% off card
   reads as a premium promotional surface without becoming a billboard. */
.waitlist-card.waitlist-card-offer {
  padding: 26px 28px;
  background:
    radial-gradient(420px 220px at 0% 0%, rgba(255, 197, 110, 0.10), transparent 70%),
    radial-gradient(420px 220px at 100% 100%, rgba(92, 193, 212, 0.08), transparent 70%),
    var(--surface);
  border-color: rgba(255, 197, 110, 0.28);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    0 18px 48px -32px rgba(255, 175, 90, 0.35),
    0 8px 26px -22px rgba(0, 0, 0, 0.6);
}
.waitlist-card-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
  flex: 1 1 auto;
}
.waitlist-card-copy h3 { font-size: 17px; margin: 2px 0 2px; }
.waitlist-card-copy p { font-size: 13.5px; color: var(--text-2); max-width: 46ch; line-height: 1.5; }
.waitlist-card-cta { flex-shrink: 0; }

/* Mobile: stack the offer card. Breakpoint matches the nav-toggle so
   "mobile chrome shown" implies "card stacked" with no ambiguous middle
   range where the button squats beside the copy on one row. */
@media (max-width: 720px) {
  .waitlist-card.waitlist-card-offer {
    flex-direction: column;
    align-items: stretch;
    padding: 22px 20px;
    gap: 16px;
  }
  .waitlist-card-copy h3 { font-size: 16px; }
  .waitlist-card-copy p { font-size: 13px; max-width: none; }
  .waitlist-card-cta {
    width: 100%;
    justify-content: center;
    padding: 12px 16px;
    font-size: 14.5px;
  }
}

@media (max-width: 980px) {
  .pricing-grid { grid-template-columns: 1fr; }
}

/* ====== Download ====== */
.download {
  padding: clamp(60px, 8vw, 100px) 0;
  background:
    radial-gradient(800px 400px at 50% 0%, rgba(92, 193, 212, 0.08), transparent 70%),
    var(--bg);
  border-top: 1px solid var(--border);
}

.download-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: 30px;
}
.download-card {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  transition: all 0.2s var(--ease);
}
.download-card:hover {
  background: var(--surface-2);
  border-color: var(--accent-soft);
  color: var(--text);
  transform: translateY(-2px);
  box-shadow: 0 12px 30px -16px var(--accent-glow);
}
.download-os {
  width: 48px;
  height: 48px;
  border-radius: 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  display: grid;
  place-items: center;
  color: var(--text);
  flex-shrink: 0;
}
.download-os-name { font-size: 15px; font-weight: 600; margin-bottom: 4px; }
.download-meta { font-size: 12px; color: var(--text-3); }
.download-arrow {
  margin-left: auto;
  color: var(--accent);
  font-size: 18px;
  transition: transform 0.18s var(--ease);
}
.download-card:hover .download-arrow { transform: translateY(2px); }

.download-note {
  text-align: center;
  font-size: 13px;
  color: var(--text-3);
  margin-bottom: 32px;
}

.code-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.code-tab {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  letter-spacing: 0.04em;
}
.copy-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-2);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  padding: 4px 10px;
  border-radius: 5px;
  cursor: pointer;
  transition: all 0.15s var(--ease);
}
.copy-btn:hover { background: var(--surface-3); color: var(--text); border-color: var(--border-2); }
.copy-btn.is-copied { color: var(--accent); border-color: rgba(92, 193, 212, 0.3); }

.code-block pre {
  padding: 16px 18px;
  color: var(--text);
}

/* ====== Closer ====== */
.closer {
  padding: clamp(60px, 7vw, 90px) 0;
  border-top: 1px solid var(--border);
}
.closer-grid {
  display: grid;
  grid-template-columns: 1.4fr auto;
  gap: 32px;
  align-items: center;
}
.closer-grid h2 { margin-bottom: 8px; }
.closer-cta { display: flex; gap: 12px; flex-wrap: wrap; }

/* ====== Footer ====== */
.site-footer {
  border-top: 1px solid var(--border);
  background: var(--bg-2);
  padding: 50px 0 24px;
}
.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 36px;
  border-bottom: 1px solid var(--border);
}
.footer-brand { display: flex; flex-direction: column; gap: 12px; }
.footer-brand .brand-mark { width: 32px; height: 32px; }
/* Footer brand wordmark - Raffishly script face (the marketing site's
   sole use of the brand font, per the visual direction: header stays
   in the system sans, footer carries the wordmark as a soft anchor).
   `font-weight: 400` because Raffishly is single-weight (synthetic
   bolding looks worse than the natural strokes); `letter-spacing: 0`
   neutralises the `-0.01em` inherited from `.brand` so the script
   glyphs aren't crowded. */
.footer-brand .brand-name {
  font-family: var(--font-brand);
  font-size: 30px;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1;
}
.footer-tag { color: var(--text-3); font-size: 13px; }

.footer-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.footer-col h4 { margin-bottom: 6px; }
.footer-col a {
  color: var(--text-2);
  font-size: 13px;
}
.footer-col a:hover { color: var(--text); }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 22px;
  flex-wrap: wrap;
  gap: 12px;
}
.footer-bottom small { color: var(--text-muted); font-size: 12px; }

/* =============================================================
   PLUGINS PAGE
   ============================================================= */

.page-hero {
  position: relative;
  padding: clamp(70px, 10vw, 130px) 0 clamp(50px, 7vw, 80px);
  overflow: hidden;
  text-align: center;
}
.page-hero .container { position: relative; z-index: 1; }
.page-hero .eyebrow { margin-bottom: 22px; }
.page-hero h1 { max-width: 800px; margin: 0 auto 20px; }
.page-hero-sub {
  max-width: 660px;
  margin: 0 auto 28px;
  font-size: 17px;
  color: var(--text-2);
}
.page-hero-meta {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 22px;
}
.page-hero-meta li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-3);
  font-size: 13px;
}

/* How */
.how {
  padding-top: clamp(40px, 7vw, 80px);
  padding-bottom: clamp(40px, 7vw, 80px);
}
.how-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: 40px;
}
.how-card {
  position: relative;
  padding: 26px 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.how-step {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--accent);
  margin-bottom: 12px;
  letter-spacing: 0.06em;
}
.how-card h3 { font-size: 15px; margin-bottom: 8px; }
.how-card p { font-size: 13px; color: var(--text-2); }
.how-card .muted { color: var(--text-muted); font-weight: 400; }

.code-block {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 18px;
}
.code-block pre { padding: 18px 20px; line-height: 1.7; }

/* Token highlight colors */
.tok-key { color: #9bd2dc; }
.tok-str { color: #cda07a; }
.tok-num { color: #c4a8d8; }
.tok-pun { color: var(--text-3); }
.tok-kw  { color: #c794d4; }
.tok-fn  { color: #82c9e0; }
.tok-id  { color: var(--text); }

/* Built-ins */
.builtins {
  padding: clamp(60px, 8vw, 100px) 0;
  background: var(--bg-2);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.builtin-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.builtin {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 20px;
  padding: 26px 26px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.2s var(--ease), transform 0.2s var(--ease);
}
.builtin:hover { border-color: var(--border-2); transform: translateY(-2px); }
.builtin-feature {
  background: linear-gradient(135deg, rgba(92, 193, 212, 0.05) 0%, var(--surface) 60%);
  border-color: rgba(92, 193, 212, 0.20);
}
.builtin-id { padding-top: 2px; }
.builtin-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  padding: 4px 10px;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 5px;
  letter-spacing: 0.02em;
  white-space: nowrap;
  border: 1px solid rgba(92, 193, 212, 0.18);
}
.builtin-body h3 { margin-bottom: 8px; font-size: 17px; }
.builtin-body p { color: var(--text-2); font-size: 14px; margin-bottom: 12px; }
.builtin-meta {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.builtin-meta li {
  font-size: 11px;
  font-weight: 500;
  padding: 3px 9px;
  background: var(--surface-3);
  color: var(--text-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  letter-spacing: 0.01em;
}

/* Plugin-hub dynamic states (rendered by assets/js/plugin-hub.js
   into #plugin-grid). The loading/error states are part of the same
   surface — keep the visual change minimal so the page never feels
   broken while the catalog fetch is in flight. */
.builtin-list[data-state='loading'] {
  min-height: 200px;
  position: relative;
}
.builtin-list[data-state='loading']::after {
  content: 'Loading plugins\2026';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-3, var(--text-2));
  font-size: 13px;
  letter-spacing: 0.02em;
}
.builtin-fallback {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--text-2);
  font-size: 14px;
}
.builtin-fallback a {
  color: var(--text-1);
  text-decoration: underline;
}

/* Homepage plugin-preview dynamic states (rendered by
   assets/js/plugin-preview.js into #plugin-preview-grid). Same shape
   as the .builtin-list rules above; the selectors target the
   homepage's .plugin-grid container instead of the plugins.html
   .builtin-list one. Keeps the homepage from flashing an empty
   section between page paint and catalog fetch. */
.plugin-grid[data-state='loading'] {
  min-height: 200px;
  position: relative;
}
.plugin-grid[data-state='loading']::after {
  content: 'Loading plugins\2026';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-3, var(--text-2));
  font-size: 13px;
  letter-spacing: 0.02em;
}
.plugin-preview-fallback {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--text-2);
  font-size: 14px;
}
.plugin-preview-fallback a {
  color: var(--text-1);
  text-decoration: underline;
}
.builtin-cta {
  margin-top: 14px;
  margin-bottom: 0;
}
.builtin-link {
  font-size: 13px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}
.builtin-link:hover {
  text-decoration: underline;
}

/* Trust */
.trust {
  padding: clamp(60px, 8vw, 100px) 0;
}
.trust-grid {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 60px;
  align-items: start;
}
.trust-grid > div:first-child .eyebrow { margin-bottom: 16px; }
.trust-points {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.trust-point {
  padding: 22px 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.trust-point h3 { font-size: 14px; margin-bottom: 6px; color: var(--accent); }
.trust-point p { font-size: 13px; color: var(--text-2); line-height: 1.55; }

/* =============================================================
   MISSION PAGE
   ============================================================= */

/* Long-form prose block. Constrained width keeps lines readable
   (~70ch) so the manifesto sections feel like a position paper,
   not marketing copy stretched across the viewport. */
.prose {
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.prose p {
  font-size: 16px;
  line-height: 1.7;
  color: var(--text-2);
}
.prose-emphasis {
  margin-top: 6px;
  padding-left: 18px;
  border-left: 2px solid var(--accent);
  color: var(--text);
  font-weight: 500;
}

.mission-why {
  padding-top: clamp(60px, 8vw, 100px);
  padding-bottom: clamp(40px, 6vw, 80px);
}

.manifesto {
  padding: clamp(60px, 8vw, 100px) 0;
  background: var(--bg-2);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.tenet-list {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 880px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.tenet {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 18px;
  padding: 24px 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.2s var(--ease), transform 0.2s var(--ease);
}
.tenet:hover {
  border-color: var(--border-2);
  transform: translateY(-2px);
}
.tenet-num {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--accent);
  padding-top: 2px;
  min-width: 28px;
}
.tenet-body h3 {
  font-size: 16px;
  margin-bottom: 8px;
  color: var(--text);
}
.tenet-body p {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-2);
}

.vision {
  padding-top: clamp(60px, 8vw, 100px);
  padding-bottom: clamp(40px, 6vw, 80px);
}

@media (max-width: 720px) {
  .tenet-list { grid-template-columns: 1fr; }
  .prose p { font-size: 15px; }
}

/* =============================================================
   Legal pages (privacy.html, terms.html)
   Long-form, single-column policy copy. Extends `.prose` with
   heading, list, and emphasis support, and adds the TL;DR
   summary card + on-page table of contents that frame the body.
   ============================================================= */

/* `.legal-meta` is the small effective / last-updated line under
   the page-hero subtitle. Uses the muted text token so it sits
   below the eyebrow without competing for attention. */
.legal-meta {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px 12px;
  margin-top: 18px;
  font-size: 13px;
  color: var(--text-3);
}
.legal-meta strong { color: var(--text-2); font-weight: 600; }
.legal-meta time { font-variant-numeric: tabular-nums; }
.legal-meta-sep { color: var(--text-muted); }

/* TL;DR card — plain-language summary at the top of each policy.
   Surface-tone card with a thin accent stripe so it reads as
   "guidance, not the contract". Bullets are 1.5 line-height for
   skim-ability. */
.legal-summary {
  padding-top: clamp(28px, 4vw, 44px);
  padding-bottom: clamp(8px, 2vw, 16px);
}
.legal-tldr {
  max-width: 760px;
  margin: 0 auto;
  padding: 22px 24px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
}
.legal-tldr-title {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  margin: 0 0 12px;
}
.legal-tldr-list {
  list-style: disc;
  padding-left: 20px;
  margin: 0 0 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.legal-tldr-list li {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--text-2);
}
.legal-tldr-list li strong { color: var(--text); font-weight: 600; }
.legal-tldr-note {
  margin: 8px 0 0;
  font-size: 12px;
  color: var(--text-muted);
  font-style: italic;
}

/* Table of contents. Single column on mobile, two columns on
   wider viewports so a 20-item ToC doesn't run a page long. */
.legal-toc {
  padding-top: clamp(20px, 3vw, 28px);
  padding-bottom: clamp(20px, 3vw, 28px);
}
.legal-toc {
  max-width: 880px;
}
.legal-toc-title {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-3);
  margin: 0 0 12px;
}
.legal-toc-list {
  list-style: none;
  margin: 0;
  padding: 16px 18px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 24px;
}
.legal-toc-list li {
  font-size: 14px;
  line-height: 1.5;
}
.legal-toc-list a {
  color: var(--text-2);
  text-decoration: none;
}
.legal-toc-list a:hover,
.legal-toc-list a:focus-visible {
  color: var(--accent);
}

/* Body container — caps width at 760px so lines stay readable. */
.legal-body {
  padding-top: clamp(20px, 3vw, 36px);
  padding-bottom: clamp(60px, 8vw, 100px);
}
.legal-body .prose {
  max-width: 760px;
}

/* Extend `.prose` so policy headings, lists, and inline emphasis
   read like a position paper rather than a wall of text. Only
   the elements legal pages actually use are styled here; we
   leave `.prose p` alone so mission.html keeps its existing
   typography. */
.legal-body .prose h2 {
  font-size: clamp(1.25rem, 2.4vw, 1.5rem);
  line-height: 1.3;
  color: var(--text);
  margin: 32px 0 12px;
  scroll-margin-top: 88px; /* clears the sticky site-header on anchor jumps */
}
.legal-body .prose h2:first-child { margin-top: 0; }
.legal-body .prose h3 {
  font-size: 1.05rem;
  line-height: 1.4;
  color: var(--text);
  margin: 22px 0 8px;
  scroll-margin-top: 88px;
}
.legal-body .prose ul,
.legal-body .prose ol {
  margin: 0;
  padding-left: 22px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--text-2);
  font-size: 16px;
  line-height: 1.7;
}
.legal-body .prose ul ul,
.legal-body .prose ol ol,
.legal-body .prose ul ol,
.legal-body .prose ol ul {
  margin-top: 8px;
  padding-left: 22px;
}
.legal-body .prose li { color: var(--text-2); }
.legal-body .prose li strong { color: var(--text); font-weight: 600; }
.legal-body .prose p strong { color: var(--text); font-weight: 600; }
.legal-body .prose code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
}
.legal-body .prose a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.legal-body .prose a:hover,
.legal-body .prose a:focus-visible {
  color: var(--accent-2);
  text-decoration-color: var(--accent-2);
}

@media (max-width: 720px) {
  .legal-toc-list { grid-template-columns: 1fr; }
  .legal-body .prose ul,
  .legal-body .prose ol { font-size: 15px; }
}

/* =============================================================
   404
   ============================================================= */

.not-found {
  position: relative;
  min-height: calc(100vh - 64px - 100px);
  display: grid;
  place-items: center;
  padding: 60px 0;
  overflow: hidden;
}
.not-found-inner {
  position: relative;
  z-index: 1;
  text-align: center;
  max-width: 620px;
}
.not-found .eyebrow { margin-bottom: 22px; }
.not-found-title {
  font-size: clamp(2.4rem, 6vw, 3.8rem);
  margin-bottom: 18px;
  letter-spacing: -0.025em;
}
.not-found-sub {
  font-size: 17px;
  color: var(--text-2);
  margin-bottom: 28px;
}
.not-found .cta-row { justify-content: center; }

/* =============================================================
   RESPONSIVE
   ============================================================= */

@media (max-width: 980px) {
  .hero-stack { gap: 36px; }
  .app-mock { transform: none; }
  .am-grid { height: 300px; }
  /* Hide search text + chrome-heavy bits at tablet width to keep the header readable */
  .am-search-text { display: none; }
  .am-space-name { display: none; }
  .am-space.active .am-space-name { display: inline; }

  .why-grid { grid-template-columns: 1fr 1fr; }
  .plugin-grid { grid-template-columns: 1fr 1fr; }
  .builtin-list { grid-template-columns: 1fr; }
  .trust-grid { grid-template-columns: 1fr; gap: 32px; }
  .trust-points { grid-template-columns: 1fr; }
  .closer-grid { grid-template-columns: 1fr; }
  .download-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .section-head-row { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 720px) {
  .primary-nav { display: none; }
  .nav-toggle { display: flex; }

  /* Anchor scroll-padding follows the smaller header height */
  html { scroll-padding-top: 64px; }

  .hero { padding-top: 28px; }
  .hero-meta { gap: 14px; }
  .hero-title { font-size: clamp(2rem, 7.5vw, 2.6rem); letter-spacing: -0.02em; }
  .hero-sub { font-size: 15px; }

  /* CTAs go full-width side-by-side, then wrap if needed */
  .cta-row { gap: 10px; }
  .cta-row .btn { flex: 1 1 0; min-width: 140px; justify-content: center; }

  /* Section heads tighter */
  .section-head { margin-bottom: 32px; }
  .section-head h2 { font-size: clamp(1.5rem, 5vw, 1.9rem); }

  /* Hero meta: stack items vertically so they get a consistent rhythm */
  .hero-meta { flex-direction: column; align-items: center; gap: 10px; }
  .hero-meta li { font-size: 13.5px; }

  /* Strip: stack label + adapter list vertically with proper rhythm */
  .strip-row {
    flex-direction: column;
    align-items: stretch;
    gap: 18px;
    padding-top: 36px;
    padding-bottom: 32px;
  }
  .strip-label {
    padding-bottom: 14px;
    border-bottom: 1px solid var(--border);
    width: 100%;
    letter-spacing: 0.16em;
  }
  .strip-list {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    width: 100%;
  }
  .strip-list li {
    font-size: 15px;
    color: var(--text);
    width: 100%;
    line-height: 1.45;
  }

  /* Card grids → single column */
  .why-grid { grid-template-columns: 1fr; }
  .plugin-grid { grid-template-columns: 1fr; }
  /* BYO call-out stacks: link slides under copy on narrow screens.
     Pull the desktop ~104px gap down to ~64px on phones where vertical
     real estate is dearer. */
  .plugin-byo {
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
    margin-top: 40px;
    padding-top: 24px;
  }
  /* Carousel: tighter, flat strip cards on phones */
  .cohort-card { width: clamp(220px, 72vw, 260px); height: clamp(248px, 70vw, 288px); }
  .cohort-marquee { padding: 18px 0 32px; }
  .how-grid { grid-template-columns: 1fr; }

  /* Footer stacks fully */
  .footer-grid { grid-template-columns: 1fr; gap: 28px; }
  .footer-bottom { flex-direction: column; align-items: flex-start; }

  /* Cards: a touch more vertical breathing room on phones */
  .feature, .plugin-card, .why-card, .how-card, .builtin, .plan, .trust-point {
    padding: 22px 20px;
  }

  /* Pricing */
  .plan { padding: 26px 22px; }
  .plan-price-amount { font-size: 32px; }
  .plan-price-zero { font-size: 32px; }
  .waitlist-card { flex-direction: column; align-items: flex-start; gap: 14px; padding: 20px; text-align: left; }
  .waitlist-card .btn { align-self: stretch; justify-content: center; }

  /* Closer + plugins-preview head */
  .closer-grid { gap: 24px; }
  .closer-cta .btn, .plan-cta { width: 100%; justify-content: center; }

  /* Download cards full-width with tap-friendly padding */
  .download-card { padding: 18px; gap: 14px; }
  .download-os { width: 42px; height: 42px; }
  .download-os-name { font-size: 14px; }

  /* App mock on phones: compact header, single-column grid showing the
     focused Claude term + one secondary so the visual stays informative
     without dominating the hero. */
  .am-header { padding-left: 8px; gap: 5px; flex-wrap: nowrap; overflow: hidden; }
  .am-project-sub, .am-chev { display: none; }
  .am-search { display: none; }
  .am-divider { display: none; }
  .am-plugins .am-plugin:nth-child(n+4) { display: none; }
  /* Spaces strip: hide entirely on phone-width viewports - a lone
     tab in a recessed track looks orphaned, and the plugin toolbar
     plus project chip already convey "this is a multi-pane app". */
  .am-spaces { display: none; }
  .am-grid {
    grid-template-columns: 1fr;
    grid-template-rows: 1.4fr 1fr;
    height: auto;
    padding: 5px;
    gap: 5px;
  }
  .am-term-large { grid-row: auto; }
  .am-term:nth-child(n+3) { display: none; }
  .am-term-large .am-term-body { min-height: 180px; }
  .am-term-body { font-size: 10.5px; padding: 9px 10px; }
  .am-statusbar { gap: 10px; padding: 0 10px; font-size: 10px; }
  .am-statusbar .am-status-item:nth-child(n+4) { display: none; }

  /* Code/CLI blocks readable at small widths */
  pre { font-size: 12px; }
  .code-block pre { padding: 14px 16px; }
}

@media (max-width: 460px) {
  .container { padding-left: 18px; padding-right: 18px; }
  .nav-row { height: 56px; gap: 12px; }
  .btn { padding: 10px 16px; font-size: 13px; }

  /* Small phones: a touch smaller so more of the strip stays in view. */
  .cohort-card { width: clamp(208px, 66vw, 248px); height: clamp(236px, 64vw, 276px); }
}

/* ====== Small phones (≤380px) ====== */
@media (max-width: 380px) {
  .container { padding-left: 16px; padding-right: 16px; }
  .nav-row { gap: 8px; }
  .brand-name { font-size: 14px; }
  .brand-mark { width: 24px; height: 24px; padding: 3px; }

  .hero-title { font-size: 1.85rem; }
  .hero-sub { font-size: 14px; }
  .eyebrow { font-size: 11px; padding: 3px 8px; letter-spacing: 0.12em; }

  .nav-github span,
  .nav-github { padding: 7px 10px; font-size: 12.5px; }
  .btn { padding: 10px 14px; font-size: 12.5px; }

  .feature, .plugin-card, .why-card, .how-card, .builtin, .plan, .trust-point {
    padding: 20px 18px;
  }
  .plan-price-amount { font-size: 28px; }
  .plan-price-zero { font-size: 28px; }

  .am-header { padding-left: 6px; gap: 4px; }
  .am-project { padding: 0 8px 0 7px; font-size: 11px; }
  .am-plugins { padding: 0; }
  .am-plugins .am-plugin:nth-child(n+3) { display: none; }
  .am-term-body { font-size: 10px; }
  .am-term-meta { display: none; }

  .builtin { grid-template-columns: 1fr; gap: 12px; }
  .builtin-id { padding-top: 0; }
}

/* ====== iOS safe areas (notches, home indicators) ====== */
@supports (padding: max(0px)) {
  .site-header { padding-top: env(safe-area-inset-top); }
  .site-header .nav-row {
    padding-left: max(24px, env(safe-area-inset-left));
    padding-right: max(24px, env(safe-area-inset-right));
  }
  .site-footer { padding-bottom: max(24px, env(safe-area-inset-bottom)); }
}

/* ====== Touch hygiene ======
   - Eliminate iOS double-tap zoom delay on interactive elements.
   - Disable lift-on-hover transforms on touch devices (sticky hover state). */
.btn, .copy-btn, .nav-toggle, .nav-github,
.download-card, .plugin-card, .feature, .why-card, .how-card,
.builtin, .plan, .trust-point, a {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

@media (hover: none) {
  .download-card:hover,
  .plugin-card:hover,
  .feature:hover,
  .why-card:hover,
  .builtin:hover,
  .plan:hover,
  .how-card:hover,
  .trust-point:hover,
  .cohort-card:hover,
  .app-mock:hover {
    transform: none;
  }
  .download-card:hover .download-arrow,
  .link-arrow:hover svg { transform: none; }
}

/* Minimum tap target for primary actionable elements (≥ Apple HIG 44pt) */
.btn { min-height: 44px; }

/* ====== Reduced motion ====== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .app-mock { transform: none !important; }
}

/* =============================================================
   ENTERPRISE / CONTACT FORM
   ============================================================= */
.enterprise-form-section {
  padding: clamp(20px, 4vw, 40px) 24px clamp(60px, 10vw, 110px);
}

.enterprise-form-wrap {
  max-width: 760px;
  margin: 0 auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: clamp(20px, 4vw, 36px);
  box-shadow: 0 18px 60px -32px rgba(0, 0, 0, 0.55);
}

.enterprise-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.enterprise-form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

.enterprise-form-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0; /* allow grid children to shrink under their content */
}
.enterprise-form-field-full {
  /* Spans the full width inside .enterprise-form (it lives outside a row). */
  width: 100%;
}
.enterprise-form-field-spacer {
  /* Holds the second column in a one-input row so the layout doesn't
     collapse from 2 columns to 1 mid-form. */
  display: block;
}

.enterprise-form-field label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-2);
  letter-spacing: 0.01em;
}
.enterprise-required {
  color: var(--accent);
  margin-left: 3px;
}
.enterprise-optional {
  color: var(--text-muted);
  font-weight: 400;
  font-size: 12px;
}

.enterprise-form-field input,
.enterprise-form-field textarea {
  width: 100%;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.4;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease), box-shadow 0.15s var(--ease);
  /* Strip default UA appearance so the dark theme isn't broken by
     platform widget chrome on number/email/tel inputs. */
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: textfield;
}
.enterprise-form-field input::placeholder,
.enterprise-form-field textarea::placeholder {
  color: var(--text-muted);
}
.enterprise-form-field input:hover,
.enterprise-form-field textarea:hover {
  border-color: var(--border-2);
}
.enterprise-form-field input:focus,
.enterprise-form-field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
  background: var(--surface);
}
.enterprise-form-field input[aria-invalid="true"],
.enterprise-form-field textarea[aria-invalid="true"] {
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(215, 119, 128, 0.12);
}
.enterprise-form-field textarea {
  resize: vertical;
  min-height: 120px;
}
/* Spin-button neutralisation on number inputs so the field matches the
   visual weight of every other input. */
.enterprise-form-field input[type="number"]::-webkit-inner-spin-button,
.enterprise-form-field input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Honeypot wrapper: visually + a11y hidden. The `<div class="enterprise-hp">`
   markup carries `aria-hidden="true"` and `tabindex="-1"` on the input, but
   without an off-screen rule the unstyled "Website" input renders inline —
   real users see and try to fill it. Use the classic clipped-square trick so
   it stays in the layout/accessibility tree just enough for crawlers that
   ignore aria-hidden, while being invisible to humans and assistive tech. */
.enterprise-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.enterprise-field-error {
  margin: 0;
  font-size: 12px;
  color: var(--red);
  line-height: 1.4;
}
.enterprise-field-meta {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-top: 2px;
}
.enterprise-usecase-counter {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.enterprise-usecase-counter.is-over {
  color: var(--red);
}

.enterprise-form-status {
  margin: 0;
  font-size: 13px;
  color: var(--text-2);
  min-height: 1.4em; /* Reserve vertical space so the form doesn't jump. */
}
.enterprise-form-status.is-error {
  color: var(--red);
}
.enterprise-form-status a {
  color: var(--accent);
}

.enterprise-form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  margin-top: 4px;
}
.enterprise-form-submit:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.enterprise-form-note {
  margin: 0;
  font-size: 12px;
  color: var(--text-3);
  flex: 1 1 240px;
}
.enterprise-form-note a {
  color: var(--accent);
}

/* Success card (replaces the form on a 202). */
.enterprise-success {
  text-align: center;
  padding: 28px 20px 20px;
}
.enterprise-success h2 {
  margin: 0 0 12px;
  color: var(--text);
}
.enterprise-success p {
  max-width: 560px;
  margin: 0 auto 8px;
  color: var(--text-2);
}
.enterprise-success-id {
  font-size: 12px;
  color: var(--text-3);
  margin-top: 18px;
}
.enterprise-success-id code {
  margin-left: 6px;
}
.enterprise-success-note {
  font-size: 12px;
  color: var(--text-3);
  margin-top: 16px;
}

@media (max-width: 640px) {
  .enterprise-form-row {
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .enterprise-form-field-spacer {
    display: none;
  }
  .enterprise-form-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .enterprise-form-submit {
    width: 100%;
    justify-content: center;
  }
}

/* ====== Launch banner ======
   Pre-launch announcement strip pinned above the sticky site header.
   Stays in the DOM after dismissal (`is-dismissed` + display:none) so
   a future re-open from settings can show it again without a rebuild. */
.launch-banner {
  position: relative;
  z-index: 50; /* below modal (1000) but above page content */
  background:
    linear-gradient(90deg, rgba(92, 193, 212, 0.18), rgba(122, 223, 242, 0.12) 60%, rgba(92, 193, 212, 0.18));
  border-top: 1px solid rgba(92, 193, 212, 0.4);
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-size: 13px;
  line-height: 1.35;
}
.launch-banner.is-dismissed { display: none; }
.launch-banner-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 9px 18px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.launch-banner-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.launch-banner-pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 var(--accent-glow);
  animation: launch-banner-pulse 2.4s ease-in-out infinite;
  flex-shrink: 0;
}
@keyframes launch-banner-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); }
  50%      { box-shadow: 0 0 0 7px rgba(92, 193, 212, 0); }
}
.launch-banner-eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 11px;
  font-weight: 600;
  color: var(--accent-2);
  flex-shrink: 0;
}
.launch-banner-text {
  color: var(--text);
  flex: 1 1 200px;
  min-width: 0;
}
.launch-banner-cta {
  appearance: none;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: linear-gradient(180deg, var(--accent) 0%, #4ab3c8 100%);
  color: #0a1518;
  font: inherit;
  font-weight: 700;
  padding: 6px 15px;
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  flex-shrink: 0;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.18) inset,
    0 8px 22px -12px var(--accent-glow);
  transition: transform 120ms ease, box-shadow 160ms ease, background-color 120ms ease;
}
.launch-banner-cta:hover {
  background: linear-gradient(180deg, var(--accent-2) 0%, var(--accent) 100%);
  transform: translateY(-1px);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.24) inset,
    0 12px 28px -12px var(--accent-glow);
}
.launch-banner-cta:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}
.launch-banner-close {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-3);
  cursor: pointer;
  padding: 4px;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.launch-banner-close:hover { color: var(--text); }
.launch-banner-close:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}

@media (max-width: 640px) {
  /* Phone layout: a labeled chip on top, the message, then a full-width
     primary CTA. The close button is pinned to the top-right corner, out of
     flow, so nothing wraps into a cramped pile. */
  .launch-banner-inner {
    position: relative;
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    padding: 13px 44px 14px 16px;
  }
  .launch-banner-eyebrow { font-size: 10.5px; }
  .launch-banner-text {
    flex: 0 0 auto;
    font-size: 13px;
    line-height: 1.45;
  }
  .launch-banner-cta {
    width: 100%;
    justify-content: center;
    padding: 11px 16px;
    font-size: 14px;
  }
  .launch-banner-close {
    position: absolute;
    top: 9px;
    right: 9px;
    padding: 8px;
  }
}

/* ====== Discount badge ======
   Reusable promotional chip used by the launch banner and the waitlist
   card. Warm amber gradient + soft shimmer reads as a "deal" marker that
   visually contrasts the site's cyan accent without breaking the palette.
   Sized for inline use; `discount-badge-lg` is the card-sized variant. */
.discount-badge {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 11px;
  border-radius: 999px;
  background:
    linear-gradient(135deg, rgba(255, 200, 110, 0.18) 0%, rgba(255, 165, 75, 0.10) 100%),
    rgba(20, 14, 6, 0.4);
  border: 1px solid rgba(255, 197, 110, 0.42);
  color: #ffd79c;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.16),
    0 8px 22px -14px rgba(255, 175, 90, 0.55);
}
.discount-badge::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg, transparent 30%, rgba(255, 230, 175, 0.22) 50%, transparent 70%);
  transform: translateX(-130%);
  animation: discount-badge-shimmer 5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes discount-badge-shimmer {
  0%   { transform: translateX(-130%); }
  55%  { transform: translateX(130%); }
  100% { transform: translateX(130%); }
}
.discount-badge-text {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}
.discount-badge-percent {
  color: #fff1d4;
  font-weight: 800;
  letter-spacing: 0.04em;
}

/* Larger variant for in-card placement (waitlist card hero). */
.discount-badge-lg {
  padding: 5px 14px;
  font-size: 11.5px;
  gap: 7px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 10px 28px -16px rgba(255, 175, 90, 0.6);
}
.discount-badge-lg .discount-badge-percent { font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  .discount-badge::after { animation: none; }
}

@media (max-width: 640px) {
  /* Banner badge sits between the "Releasing soon" label and the message
     on phone layout. Self-align so the chip hugs the left edge instead of
     stretching to full row width when the banner becomes a column flex. */
  .launch-banner .discount-badge {
    align-self: flex-start;
    font-size: 10px;
    padding: 3px 10px;
  }
}

/* ====== Waitlist modal ======
   Dialog + overlay. Hidden by default (`hidden` attribute drives both
   display + a11y). Focus trap + Esc + backdrop-close handled in JS. */
.waitlist-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.waitlist-modal[hidden] { display: none; }
.waitlist-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(5, 6, 10, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  cursor: pointer;
}
.waitlist-modal-dialog {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 32px 28px 24px;
  width: 100%;
  max-width: 460px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.02);
  animation: waitlist-modal-in 160ms cubic-bezier(.2,.8,.2,1);
}
@keyframes waitlist-modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0)  scale(1); }
}
.waitlist-modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-3);
  cursor: pointer;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.waitlist-modal-close:hover { color: var(--text); background: var(--surface-2); }
.waitlist-modal-close:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}
.waitlist-modal-title {
  font-size: 20px;
  margin: 0 0 6px;
  color: var(--text);
}
.waitlist-modal-sub {
  font-size: 13.5px;
  color: var(--text-2);
  margin: 0 0 22px;
}
.waitlist-form-field {
  margin-bottom: 14px;
}
.waitlist-form-field label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.waitlist-form-field input[type="email"] {
  width: 100%;
  appearance: none;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  padding: 10px 12px;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.waitlist-form-field input[type="email"]::placeholder { color: var(--text-muted); }
.waitlist-form-field input[type="email"]:hover { border-color: var(--border-2); }
.waitlist-form-field input[type="email"]:focus-visible {
  border-color: var(--accent);
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.waitlist-form-field input[aria-invalid="true"] {
  border-color: #e85c66;
  box-shadow: 0 0 0 3px rgba(232, 92, 102, 0.18);
}
.waitlist-field-error {
  color: #e85c66;
  font-size: 12px;
  margin: 6px 0 0;
  line-height: 1.3;
}
.waitlist-hp {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.waitlist-turnstile {
  min-height: 0;
  margin: 10px 0 6px;
}
.waitlist-turnstile:empty { display: none; }
/* Consent row: the native checkbox is visually hidden but stays focusable
   and screen-reader-reachable; `.waitlist-consent-box` is the painted
   surface that mirrors its checked / focus state via sibling combinators. */
.waitlist-form-consent {
  margin: 14px 0 0;
}
.waitlist-consent-label {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 10px;
  align-items: start;
  cursor: pointer;
  user-select: none;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-2);
}
.waitlist-consent-checkbox {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.waitlist-consent-box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  color: transparent;
  transition: background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease, color 120ms ease;
}
.waitlist-consent-box svg {
  width: 11px;
  height: 11px;
  transform: scale(0.6);
  opacity: 0;
  transition: transform 140ms cubic-bezier(.2,.8,.2,1), opacity 120ms ease;
}
.waitlist-consent-label:hover .waitlist-consent-box {
  border-color: var(--accent);
}
.waitlist-consent-checkbox:focus-visible + .waitlist-consent-box {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.waitlist-consent-checkbox:checked + .waitlist-consent-box {
  background: var(--accent);
  border-color: var(--accent);
  color: #0a0a0f;
}
.waitlist-consent-checkbox:checked + .waitlist-consent-box svg {
  opacity: 1;
  transform: scale(1);
}
.waitlist-consent-checkbox[aria-invalid="true"] + .waitlist-consent-box {
  border-color: #e85c66;
  box-shadow: 0 0 0 3px rgba(232, 92, 102, 0.18);
}
.waitlist-consent-text { display: block; }
.waitlist-consent-link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.waitlist-consent-link:hover { color: var(--accent-2); }
.waitlist-consent-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}
.waitlist-form-status {
  margin: 8px 0 0;
  min-height: 18px;
  font-size: 13px;
  color: var(--text-2);
}
.waitlist-form-status.is-error { color: #e85c66; }
.waitlist-form-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-top: 18px;
  flex-wrap: wrap;
}
.waitlist-form-submit { flex: 1 1 auto; justify-content: center; }
.waitlist-form-submit[disabled],
.waitlist-form-submit[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
.waitlist-form-note {
  margin: 14px 0 0;
  font-size: 11.5px;
  color: var(--text-3);
  line-height: 1.4;
}
.waitlist-form-success {
  text-align: center;
  padding: 6px 4px 8px;
}
.waitlist-form-success-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  margin-bottom: 10px;
}
.waitlist-form-success h3 {
  font-size: 18px;
  margin: 0 0 4px;
  color: var(--text);
}
.waitlist-form-success p {
  font-size: 13px;
  color: var(--text-2);
  margin: 0 0 18px;
}

@media (max-width: 520px) {
  .waitlist-modal { padding: 14px; }
  .waitlist-modal-dialog { padding: 26px 20px 20px; }
  .waitlist-form-actions { flex-direction: column; align-items: stretch; }
  .waitlist-form-submit { width: 100%; }
  /* Consent row: extend the tap target across the full row by giving
     the label a comfortable min-height. The checkbox + text alignment
     stays correct because the grid layout already keys off `align-items: start`. */
  .waitlist-form-consent { margin-top: 12px; }
  .waitlist-consent-label {
    min-height: 44px;
    font-size: 13.5px;
    column-gap: 12px;
  }
  .waitlist-consent-box { width: 20px; height: 20px; margin-top: 2px; }
}

/* ====== Prelaunch mode ======
   Active when `<html data-prelaunch="true">` is set (production until
   the product ships - see assets/prelaunch.js for the flip switch).
   Hides pre-launch CTAs so the only path off the marketing site is
   the waitlist + enterprise contact. Per-element opt-in via:
     [data-prelaunch-hide]      - display:none.
     [data-prelaunch-disable]   - visible but unclickable + a11y.
   Plus blanket rules for nav chrome + pricing-grid that are
   structurally always hidden in this mode. */
html[data-prelaunch="true"] [data-prelaunch-hide] { display: none !important; }
/* Inverse: elements that ONLY render under prelaunch. Hidden by default. */
[data-prelaunch-show] { display: none; }
html[data-prelaunch="true"] [data-prelaunch-show] { display: inline-flex; }
/* Block-level variant: a panel/section that should render as a block. */
html[data-prelaunch="true"] [data-prelaunch-show].download-prelaunch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
  padding: 36px 26px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  max-width: 540px;
  margin: 0 auto;
}
.download-prelaunch h3 { font-size: 18px; margin: 0; color: var(--text); }
.download-prelaunch p { font-size: 14px; color: var(--text-2); margin: 0; }
html[data-prelaunch="true"] [data-prelaunch-disable] {
  pointer-events: none;
  opacity: 0.55;
  cursor: not-allowed;
  filter: saturate(0.6);
}
/* aria-disabled lift; the surrounding wrapper paints a "coming soon"
   strip on hero CTAs so the user understands why the buttons don't act. */
html[data-prelaunch="true"] [aria-disabled="true"] {
  pointer-events: none;
}
/* Header chrome: GitHub icon hidden (no functional release flow yet);
   sign-in stays visible but disabled (the sign-in app exists; we just
   don't want pre-launch traffic landing on it). */
html[data-prelaunch="true"] .nav-github {
  display: none !important;
}
html[data-prelaunch="true"] .nav-signin {
  pointer-events: none;
  opacity: 0.55;
  cursor: not-allowed;
  filter: saturate(0.6);
}
/* Header Download nav link sits inside .primary-nav + .mobile-nav and
   uses [data-prelaunch-disable] (set on each page's nav) for the same
   disabled treatment. Pricing-grid + per-period toggle are hidden
   in-block via the rule above. */
/* Catch-all: any download / releases link is HIDDEN under
   prelaunch unless marked `data-prelaunch-disable` (the header nav
   download link, sign-in, and the hero CTAs take that opt-out so
   they stay visible-but-grayed). Matches both the canonical clean
   URL (`href="download"`) and the legacy `.html` form so a stray
   link without the marker still gets caught. */
html[data-prelaunch="true"] a[href="download"]:not([data-prelaunch-disable]):not(.nav-signin),
html[data-prelaunch="true"] a[href="download.html"]:not([data-prelaunch-disable]):not(.nav-signin),
html[data-prelaunch="true"] a[href$="/download"]:not([data-prelaunch-disable]):not(.nav-signin),
html[data-prelaunch="true"] a[href^="https://github.com/BenDol/Agent-Cohort-Release/releases"]:not([data-prelaunch-disable]):not(.nav-github) {
  display: none !important;
}
/* Pricing block: hide the section header (eyebrow + tagline), the
   yearly/monthly toggle, and the card grid. The `.waitlist` block
   that lives inside the same <section class="pricing"> intentionally
   stays visible so the only call-to-action surfaces is "Join waitlist". */
html[data-prelaunch="true"] .pricing > .container > .section-head,
html[data-prelaunch="true"] .pricing .period-toggle,
html[data-prelaunch="true"] .pricing .pricing-grid {
  display: none !important;
}
/* Tighten the waitlist card spacing now that nothing precedes it. */
html[data-prelaunch="true"] .pricing { padding-top: clamp(40px, 6vw, 64px); }

/* ====== Print ====== */
@media print {
  .site-header, .site-footer, .hero-bg, .nav-toggle, .launch-banner, .waitlist-modal { display: none; }
  body { background: white; color: black; }
}

/* ============================================================
   VIDEO TOUR  (home page "See it in action" section)
   Section rhythm mirrors .why; the frame mirrors the .app-mock
   treatment (border + accent-glow shadow). Self-contained.
   ============================================================ */
.tour {
  padding-top: clamp(72px, 9vw, 110px);
  padding-bottom: clamp(60px, 8vw, 100px);
}
.tour-frame {
  position: relative;
  max-width: 980px;
  margin: 0 auto;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--bg-2);
  box-shadow:
    0 30px 80px -20px rgba(0, 0, 0, 0.7),
    0 0 0 1px rgba(255, 255, 255, 0.03),
    0 18px 60px -28px var(--accent-glow);
}
.tour-glow {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.tour-video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  object-fit: cover;
}
/* Responsive cut switch: landscape on desktop, portrait on mobile viewports.
   Only the visible <video> ever downloads (both are preload="none"). */
.tour-frame--portrait {
  display: none;
  max-width: 420px;
}
.tour-video--portrait {
  aspect-ratio: 9 / 16;
  max-height: 82vh;
  object-fit: contain;
}
@media (max-width: 760px) {
  .tour-frame--landscape {
    display: none;
  }
  .tour-frame--portrait {
    display: block;
  }
}
@media (max-width: 640px) {
  .tour-frame { border-radius: var(--radius); }
}

/* Custom centered play button. Hidden by default so the no-JS fallback is the
   native <video controls>; script.js adds .js-enhanced (revealing this button
   and stripping native controls until playback starts). It fades out once the
   video is playing (.is-playing) and returns on end as a replay affordance. */
.tour-play {
  position: absolute;
  inset: 0;
  margin: auto;
  z-index: 3;
  display: none;
  align-items: center;
  justify-content: center;
  width: clamp(64px, 8vw, 88px);
  height: clamp(64px, 8vw, 88px);
  padding: 0;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: #fff;
  background: rgba(12, 17, 25, 0.5);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  backdrop-filter: blur(8px) saturate(120%);
  box-shadow:
    0 10px 34px -8px rgba(0, 0, 0, 0.65),
    inset 0 0 0 1px rgba(255, 255, 255, 0.1),
    0 0 30px -6px var(--accent-glow);
  transition:
    transform 200ms cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 220ms ease,
    opacity 280ms ease;
}
.tour-frame.js-enhanced .tour-play {
  display: flex;
}
/* Thin accent ring that sits flush inside the button rim. */
.tour-play-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--accent);
  opacity: 0.85;
  transition: opacity 220ms ease, transform 220ms ease;
}
.tour-play-icon {
  position: relative;
  width: 38%;
  height: 38%;
  margin-left: 6%; /* optical centering of the play triangle */
  fill: currentColor;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45));
}
.tour-play:hover,
.tour-play:focus-visible {
  transform: scale(1.07);
  background: rgba(16, 22, 32, 0.62);
  box-shadow:
    0 12px 40px -8px rgba(0, 0, 0, 0.7),
    inset 0 0 0 1px rgba(255, 255, 255, 0.16),
    0 0 46px -4px var(--accent-glow);
}
.tour-play:hover .tour-play-ring,
.tour-play:focus-visible .tour-play-ring {
  opacity: 1;
  transform: scale(1.04);
}
.tour-play:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 4px;
}
.tour-play:active {
  transform: scale(0.97);
}
/* Once playback starts, dissolve the button (native controls take over). */
.tour-frame.is-playing .tour-play {
  opacity: 0;
  transform: scale(0.86);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .tour-play,
  .tour-play-ring { transition: opacity 200ms ease; }
  .tour-play:hover,
  .tour-play:focus-visible { transform: none; }
  .tour-frame.is-playing .tour-play { transform: none; }
}


/* ===== Cookie consent banner ===========================================
   Footer-attached, fixed to viewport bottom on mobile and sticky-style on
   desktop. Shown on first visit until the user picks Accept or Essential
   only; preference persists in localStorage under
   `agent-cohort:cookie-consent` ("all" | "essential").

   Accept = the script.js banner module injects the Google Fonts
   stylesheet into <head>. Essential = no font request fires; the site
   falls back to the system stack baked into `--font-sans`. */
.cookie-banner {
  position: fixed;
  left: 50%;
  bottom: 18px;
  width: calc(100% - 32px);
  max-width: 720px;
  z-index: 2147483600;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.25);
  font-size: 13.5px;
  line-height: 1.5;
  padding: 14px 16px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  align-items: center;
  opacity: 0;
  transform: translate(-50%, 12px);
  pointer-events: none;
  transition: opacity 220ms ease, transform 260ms cubic-bezier(.2, .8, .2, 1);
}
.cookie-banner[data-visible="true"] {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}
.cookie-banner[hidden] { display: none !important; }
.cookie-banner-copy {
  color: var(--text-2);
  margin: 0;
}
.cookie-banner-copy strong {
  color: var(--text);
  font-weight: 600;
}
.cookie-banner-link {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.cookie-banner-link:hover { color: var(--accent-2); }
.cookie-banner-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: stretch;
}
.cookie-banner-btn {
  appearance: none;
  border: 1px solid var(--border-2);
  background: var(--bg-2);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 9px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  flex: 1 1 auto;
  min-height: 38px;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease, box-shadow 120ms ease;
}
.cookie-banner-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.cookie-banner-btn:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.cookie-banner-btn.is-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #0a0a0f;
}
.cookie-banner-btn.is-primary:hover {
  background: var(--accent-2);
  border-color: var(--accent-2);
  color: #0a0a0f;
}
@media (min-width: 640px) {
  .cookie-banner {
    grid-template-columns: 1fr auto;
    gap: 18px;
    padding: 14px 18px;
  }
  .cookie-banner-actions { flex-wrap: nowrap; }
  .cookie-banner-btn { flex: 0 0 auto; }
}
@media (max-width: 420px) {
  .cookie-banner {
    bottom: 12px;
    width: calc(100% - 20px);
    padding: 12px 14px;
  }
  .cookie-banner-btn { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .cookie-banner { transition: none; }
}
