/* ==========================================================================
   ideaboxd - interactive product-story demos (motion layer)
   --------------------------------------------------------------------------
   Paired with js/demos.js, which does nothing but advance `data-step` on each
   .demo. Every transition in the five demos is declared here.

   WHY IT LOOKS LIKE THE PRODUCT
   -----------------------------
   Nothing in this file styles a card, a chip, a status pill or a plan
   section. Those come from the site's existing product-visual components
   (.ui-card, .ui-status, .ui-chip, .ui-composer, .ui-search, .plan-card,
   .q-card, .drafted, .keep-row, .src, .ui-steps, .ui-note, .fmt-row,
   .md-preview, .act-frame, .act-inner, .pin) - declared once in
   landing/index.html and mirrored in styles/site.css, with tokens lifted
   verbatim from src/constants/theme.ts. This file only moves them.

   THE SHARED MOTION VOCABULARY
   ----------------------------
   Five attributes, understood by every demo. There is no per-demo entrance
   logic anywhere below - add a demo, reuse these.

     data-at="N"      element enters at step N and stays
     data-until="N"   element leaves after step N
     data-motion=…    entrance direction: rise (default) | pop | fade
                      | slide-l | slide-r | draw-x | draw-y
     data-delay="1-5" stagger within a step, ~90ms apart
     data-hl="N"      a <mark> that lights (var(--highlight)) from step N

   Enter/leave are enumerated per step with :is() rather than computed -
   five steps means five short rules, and the browser gets a plain selector
   match instead of per-frame JS.

   PERFORMANCE
   -----------
   Only `opacity` and `transform` transition, so every demo composites on the
   GPU and nothing reflows mid-animation. Stacked scenes share one grid cell
   so cross-fades cannot shift layout. `will-change` is applied only while a
   demo is actually playing, and dropped the moment it finishes.

   NO-SCRIPT / REDUCED MOTION
   --------------------------
   Every hiding rule is gated on `html.js` AND on the `data-step` that
   demos.js stamps at mount. So scripting off, script blocked or script failed
   all land in the same place: the markup renders as one finished product
   screenshot, never a blank frame. `prefers-reduced-motion` is handled by the
   site-wide rule in index.html/site.css (transitions collapse to ~0ms) plus
   demos.js jumping straight to the last step.
   ========================================================================== */

/* ============================================================
   SECTION LAYOUT - a caption above, the product below
   ============================================================ */
.story-block {
  margin-top: clamp(60px, 8vw, 112px);
}
.story-block + .story-block {
  margin-top: clamp(60px, 8vw, 112px);
}
.story-block:first-of-type {
  margin-top: clamp(44px, 6vw, 76px);
}
.story {
  max-width: 720px;
}
.story .act-num {
  margin-bottom: 16px;
}
.story h3 {
  font-size: clamp(23px, 2.6vw, 33px);
}
.story p {
  color: var(--muted);
  font-size: 16px;
  margin-top: 12px;
  max-width: 56ch;
}

/* ============================================================
   DEMO CARD ROW - the link out to the per-demo pages
   ============================================================ */
.demo-links {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(12px, 1.5vw, 18px);
  margin: clamp(34px, 4.4vw, 52px) 0 0;
  padding: 0;
}
.demo-links a {
  display: flex;
  flex-direction: column;
  height: 100%;
  gap: 8px;
  padding: clamp(18px, 2.2vw, 24px);
  /* Accent-tinted at rest, not only on hover: the audit found these read as
     descriptive text rather than links, so the affordance has to be there
     before the pointer is. */
  border: 1px solid color-mix(in srgb, var(--accent) 20%, var(--line));
  border-radius: var(--radius-lg);
  background: var(--surface);
  transition:
    transform 0.32s cubic-bezier(0.2, 0.7, 0.2, 1),
    border-color 0.32s ease,
    box-shadow 0.32s ease,
    background-color 0.32s ease;
}
.demo-links a:hover {
  transform: translateY(-3px);
  background: var(--panel);
  border-color: color-mix(in srgb, var(--accent) 46%, var(--line));
  box-shadow: var(--shadow);
}
/* Keyboard users get the same "this is a card you can open" signal. */
.demo-links a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  background: var(--panel);
  border-color: color-mix(in srgb, var(--accent) 46%, var(--line));
}
.demo-links .n {
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--accent);
}
.demo-links strong {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.25;
}
.demo-links .d {
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--muted);
}
/* "Watch it →" carries a play badge, so the card announces that something
   plays rather than that something is written. Drawn with pseudo-elements -
   the CSP forbids fetching anything, and a border triangle needs no asset. */
.demo-links .go {
  position: relative;
  margin-top: auto;
  padding-top: 14px;
  padding-left: 30px;
  min-height: 22px;
  display: flex;
  align-items: center;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--accent);
}
.demo-links .go::before {
  content: '';
  position: absolute;
  left: 0;
  top: calc(50% + 7px);
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--line));
  background: var(--chip);
  transition:
    background-color 0.22s ease,
    border-color 0.22s ease;
}
.demo-links .go::after {
  content: '';
  position: absolute;
  left: 9px;
  top: calc(50% + 7px);
  transform: translateY(-50%);
  border-style: solid;
  border-width: 4px 0 4px 6.5px;
  border-color: transparent transparent transparent var(--accent);
}
.demo-links a:hover .go::before,
.demo-links a:focus-visible .go::before {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: var(--accent);
}
.demo-links .arw {
  display: inline-block;
  margin-left: 6px;
  transition: transform 0.22s ease;
}
.demo-links a:hover .arw {
  transform: translateX(3px);
}

/* The Capture / Explore / Shape / Plan row above the demo. Same card language
   as .demo-links, and not links - the demo below is the proof. Three up on the
   demo pages that still describe a triad, four up on the homepage workflow. */
.steps {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.5vw, 18px);
  margin: clamp(34px, 4.4vw, 48px) 0 0;
  padding: 0;
}
.steps-four {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
/* The line that keeps AI in its place: the four steps above are the product,
   this is the footnote. */
.steps-note {
  margin: clamp(16px, 2vw, 22px) auto 0;
  max-width: 60ch;
  text-align: center;
  font-size: 14px;
  line-height: 1.6;
  color: var(--muted);
}
.steps li {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: clamp(18px, 2.2vw, 24px);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.steps .n {
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--accent);
}
.steps strong {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.02em;
}
.steps .d {
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}

/* The "what you just watched" list on a demo page. */
.facts li {
  font-size: 15.5px;
  line-height: 1.6;
}

/* ============================================================
   SHELL
   ============================================================ */
.demo {
  margin: clamp(34px, 4.4vw, 52px) 0 0;
  position: relative;
}
.demo-frame {
  /* .act-frame supplies the panel, glow and shadow. */
  overflow: visible;
}
.demo-screen {
  /* .act-inner supplies the inset screen. */
  position: relative;
}
.demo-screen .ui-body {
  padding: clamp(14px, 1.8vw, 22px);
  gap: clamp(10px, 1.2vw, 14px);
}

/* Two-pane demos (capture | plan). Collapses to one column on narrow screens. */
.demo-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(12px, 1.6vw, 20px);
  padding: clamp(14px, 1.8vw, 22px);
}
.demo-pane {
  display: flex;
  flex-direction: column;
  gap: clamp(9px, 1.1vw, 13px);
  min-width: 0;
}
.demo-pane.is-plan {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(13px, 1.5vw, 18px);
}

/* The beat strip under each demo: what the visitor is currently watching. */
.demo-beats {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 8px;
  margin: 16px 0 0;
  padding: 0;
}
.demo-beats li {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 5px 11px;
  transition: color 0.4s ease, border-color 0.4s ease, background-color 0.4s ease;
}
.demo-beats li::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.45;
  flex-shrink: 0;
  transition: opacity 0.4s ease, transform 0.4s ease;
}
.demo-cap {
  margin-top: 14px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--muted);
  max-width: 62ch;
}
.demo-cap b {
  color: var(--ink2);
  font-weight: 500;
}

/* A floating callout, same component as the step story's .pin. */
.demo .pin {
  right: -8px;
  bottom: -14px;
}

/* ============================================================
   SHARED MOTION VOCABULARY
   ============================================================ */

/* --- the no-scheduler state -------------------------------------------
   demos.js stamps data-step on every demo it mounts. Until that happens -
   scripting off, script blocked, script failed - the demo has to read as one
   finished product screenshot: nothing pre-hidden (so it can never end up
   blank), and the transient scenes gone (so the stacked ones can't overlap).
   -------------------------------------------------------------------- */
.demo:not([data-step]) [data-until] {
  display: none;
}
.demo:not([data-step]) [data-hl] {
  background: var(--highlight);
}

/* --- pre-roll: everything that enters starts hidden (scheduler only) --- */
.js .demo[data-step] [data-at] {
  opacity: 0;
  transform: translate3d(0, 10px, 0) scale(0.985);
  transition:
    opacity 0.45s ease,
    transform 0.55s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.js .demo[data-step] [data-at][data-motion='pop'] {
  transform: translate3d(0, 0, 0) scale(0.94);
}
.js .demo[data-step] [data-at][data-motion='fade'] {
  transform: none;
}
.js .demo[data-step] [data-at][data-motion='slide-l'] {
  transform: translate3d(-14px, 0, 0);
}
.js .demo[data-step] [data-at][data-motion='slide-r'] {
  transform: translate3d(14px, 0, 0);
}
/* Connections draw rather than fade in. */
.js .demo[data-step] [data-at][data-motion='draw-x'] {
  opacity: 1;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.js .demo[data-step] [data-at][data-motion='draw-y'] {
  opacity: 1;
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

/* --- entered: step N reveals everything authored at 0..N --- */
.js .demo[data-step='0'] :is([data-at='0']),
.js .demo[data-step='1'] :is([data-at='0'], [data-at='1']),
.js .demo[data-step='2'] :is([data-at='0'], [data-at='1'], [data-at='2']),
.js .demo[data-step='3'] :is([data-at='0'], [data-at='1'], [data-at='2'], [data-at='3']),
.js .demo[data-step='4']
  :is([data-at='0'], [data-at='1'], [data-at='2'], [data-at='3'], [data-at='4']),
.js .demo[data-step='5'] :is([data-at]),
.js .demo[data-done] :is([data-at]) {
  opacity: 1;
  transform: none;
}

/* --- left: step N hides everything authored to last through 0..N-1 --- */
.js .demo [data-until] {
  transition:
    opacity 0.4s ease,
    transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.js .demo[data-step='1'] :is([data-until='0']),
.js .demo[data-step='2'] :is([data-until='0'], [data-until='1']),
.js .demo[data-step='3'] :is([data-until='0'], [data-until='1'], [data-until='2']),
.js .demo[data-step='4']
  :is([data-until='0'], [data-until='1'], [data-until='2'], [data-until='3']),
.js .demo[data-step='5'] :is([data-until]) {
  opacity: 0;
  transform: translate3d(0, -8px, 0);
  pointer-events: none;
}

/* --- stagger --- */
.js .demo [data-delay='1'] { transition-delay: 0.09s; }
.js .demo [data-delay='2'] { transition-delay: 0.18s; }
.js .demo [data-delay='3'] { transition-delay: 0.27s; }
.js .demo [data-delay='4'] { transition-delay: 0.36s; }
.js .demo [data-delay='5'] { transition-delay: 0.45s; }
/* A leaving element never waits its turn - exits read as instant. */
.js .demo [data-until][data-delay] { transition-delay: 0s; }

/* --- highlight, using the app's own selection colour --- */
.js .demo [data-hl] {
  background: transparent;
  transition: background-color 0.5s ease, box-shadow 0.5s ease;
}
.js .demo[data-step='1'] :is([data-hl='0'], [data-hl='1']),
.js .demo[data-step='2'] :is([data-hl='0'], [data-hl='1'], [data-hl='2']),
.js .demo[data-step='3'] :is([data-hl='0'], [data-hl='1'], [data-hl='2'], [data-hl='3']),
.js .demo[data-step='4'] :is([data-hl]),
.js .demo[data-done] :is([data-hl]) {
  background: var(--highlight);
}

/* --- beat strip state --- */
.demo[data-step='0'] .demo-beats :is([data-beat='0']),
.demo[data-step='1'] .demo-beats :is([data-beat='0'], [data-beat='1']),
.demo[data-step='2'] .demo-beats :is([data-beat='0'], [data-beat='1'], [data-beat='2']),
.demo[data-step='3'] .demo-beats :is([data-beat='0'], [data-beat='1'], [data-beat='2'], [data-beat='3']),
.demo[data-step='4'] .demo-beats li,
.demo[data-done] .demo-beats li {
  color: var(--ink2);
}
.demo[data-step='0'] .demo-beats [data-beat='0'],
.demo[data-step='1'] .demo-beats [data-beat='1'],
.demo[data-step='2'] .demo-beats [data-beat='2'],
.demo[data-step='3'] .demo-beats [data-beat='3'],
.demo[data-step='4'] .demo-beats [data-beat='4'] {
  color: var(--ink);
  border-color: color-mix(in srgb, var(--accent) 34%, var(--line));
  background: var(--chip);
}
.demo[data-step='0'] .demo-beats [data-beat='0']::before,
.demo[data-step='1'] .demo-beats [data-beat='1']::before,
.demo[data-step='2'] .demo-beats [data-beat='2']::before,
.demo[data-step='3'] .demo-beats [data-beat='3']::before,
.demo[data-step='4'] .demo-beats [data-beat='4']::before {
  opacity: 1;
  transform: scale(1.25);
  background: var(--accent);
}

/* Hint the compositor only while a demo is actually running. */
.demo[data-playing] :is([data-at], [data-until]) {
  will-change: transform, opacity;
}

/* Scenes that cross-fade share one grid cell, so nothing reflows. */
.demo-stack {
  display: grid;
  align-items: start;
}
.demo-stack > * {
  grid-area: 1 / 1;
}

/* ============================================================
   DEMO 1 - messy thought → structured plan
   ============================================================ */
.dm-shape .demo-pane.is-capture {
  justify-content: flex-start;
}
.dm-shape .ui-composer {
  align-items: flex-start;
  min-height: 78px;
}
.dm-shape .ui-typed {
  min-height: 1.55em;
}
/* Capture box, saved card, the AI pass and the Shape pane are four views of
   one thing, so they share a slot: the thought settles into the inbox and then
   opens as the original note, rather than four boxes stacking up the column. */
.dm-shape .shape-capture {
  align-content: start;
  min-height: 196px;
}
.dm-shape .shape-card {
  min-height: 108px;
}
/* The "Draft with AI" control gets its own row under the capture stack. It
   used to share the stack's single grid cell, which put it straight across the
   saved card's header at step 2 - visible on a phone, where the card is
   tallest. The row is always reserved, so nothing shifts when it appears. */
.dm-shape .shape-ai {
  display: grid;
  align-items: center;
  min-height: 52px;
}
.dm-shape .shape-ai .ai-step {
  padding: 0;
}
.dm-shape .shape-original .ui-note {
  font-size: 14.5px;
}
.dm-shape .shape-original .ui-hint {
  margin-top: 14px;
}
.dm-shape .ai-step {
  padding: 6px 0;
  gap: 10px;
}
.dm-shape .ai-step .orb {
  width: 34px;
  height: 34px;
  font-size: 14px;
}
.dm-shape .ai-step .chip {
  font-size: 9.5px;
  padding: 5px 11px;
}
/* The processing pass: a soft band travels once across the saved dump. */
.js .dm-shape[data-step='2'] .shape-card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 38%, var(--highlight) 50%, transparent 62%);
  animation: demo-scan 1s cubic-bezier(0.4, 0, 0.2, 1) 1;
}
@keyframes demo-scan {
  from { transform: translate3d(-100%, 0, 0); }
  to { transform: translate3d(100%, 0, 0); }
}
.dm-shape .shape-card {
  position: relative;
  overflow: hidden;
}
.dm-shape .plan-card:last-of-type {
  margin-bottom: 0;
}
.dm-shape .plan-empty {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  line-height: 1.6;
  color: var(--faint);
  max-width: 28ch;
}

/* ============================================================
   DEMO 2 - brain dump → organised workspace
   ============================================================ */
/* Same seven cards throughout: they start scattered via transform only, so
   their grid slots are already correct and the glide is real movement, not a
   swap between two sets of elements.

   The markup is ordered by folder (3 side projects, 3 home, 1 ceramics), and
   each card's --x/--y start it somewhere else entirely. So the settle IS the
   organising: loose notes fly to the row their folder occupies. Offsets are
   percentages of the card itself, so they track the column width at every
   breakpoint. */
.dm-work .wsp-grid {
  gap: clamp(8px, 1vw, 11px);
}
.js .dm-work .wsp-card {
  opacity: 1;
  transition:
    transform 0.85s cubic-bezier(0.22, 0.72, 0.16, 1),
    opacity 0.4s ease,
    border-color 0.5s ease,
    box-shadow 0.5s ease;
}
.js .dm-work[data-step='idle'] .wsp-card,
.js .dm-work[data-step='0'] .wsp-card {
  transform: translate3d(var(--x, 0), var(--y, 0), 0) rotate(var(--r, 0deg)) scale(0.97);
  box-shadow: var(--shadow-sm);
}
.js .dm-work .wsp-card[data-delay='1'] { transition-delay: 0.05s; }
.js .dm-work .wsp-card[data-delay='2'] { transition-delay: 0.1s; }
.js .dm-work .wsp-card[data-delay='3'] { transition-delay: 0.15s; }
.js .dm-work .wsp-card[data-delay='4'] { transition-delay: 0.2s; }
.js .dm-work .wsp-card[data-delay='5'] { transition-delay: 0.25s; }
.js .dm-work .wsp-card[data-delay='6'] { transition-delay: 0.3s; }


/* The AI pass sweeps card by card rather than all at once. */
.js .dm-work[data-step='2'] .wsp-card::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 40%, var(--highlight) 50%, transparent 60%);
  animation: demo-scan 0.9s cubic-bezier(0.4, 0, 0.2, 1) 1 both;
}
.js .dm-work[data-step='2'] .wsp-card:nth-child(2)::after { animation-delay: 0.08s; }
.js .dm-work[data-step='2'] .wsp-card:nth-child(3)::after { animation-delay: 0.16s; }
.js .dm-work[data-step='2'] .wsp-card:nth-child(4)::after { animation-delay: 0.24s; }
.js .dm-work[data-step='2'] .wsp-card:nth-child(5)::after { animation-delay: 0.32s; }
.js .dm-work[data-step='2'] .wsp-card:nth-child(6)::after { animation-delay: 0.4s; }
.js .dm-work[data-step='2'] .wsp-card:nth-child(7)::after { animation-delay: 0.48s; }
.dm-work .wsp-card {
  position: relative;
  overflow: hidden;
}

/* A status chip swaps in place; both labels share one grid cell. */
.dm-work .wsp-status {
  display: grid;
  justify-items: end;
}
.dm-work .wsp-status > * {
  grid-area: 1 / 1;
}
/* The folder dot, same 8px swatch the app puts on a filed card. */
.dm-work .wsp-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--dot, var(--accent));
  display: block;
  flex-shrink: 0;
}
.dm-work .ui-card-top {
  margin-bottom: 6px;
}
.dm-work .wsp-meta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.dm-work .ui-chip i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--dot, var(--accent));
  display: inline-block;
  margin-right: 6px;
  vertical-align: -1px;
}
/* The AI row and the folder bar that follows it share one slot. */
.dm-work .wsp-head {
  min-height: 46px;
  align-items: center;
}
.dm-work .wsp-folders {
  align-content: center;
}

/* ============================================================
   DEMO 3 - one idea, the whole journey
   ============================================================ */
/* MaxContentWidth in src/constants/theme.ts - the app never runs wider. */
.dm-journey .ui-body {
  width: 100%;
  max-width: 900px;
  margin-inline: auto;
}
.dm-journey .jr-steps {
  margin: 2px 0 clamp(12px, 1.6vw, 18px);
}
.dm-journey .jr-steps .st i,
.dm-journey .jr-steps .st span {
  transition: background-color 0.5s ease, color 0.5s ease;
}
.dm-journey .jr-steps .bar {
  position: relative;
  overflow: hidden;
}
.dm-journey .jr-steps .bar span {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: left center;
}
/* Nodes light, and the connector between them draws. */
.js .dm-journey[data-step='1'] .jr-steps :is([data-node='0']),
.js .dm-journey[data-step='2'] .jr-steps :is([data-node='0'], [data-node='1']),
.js .dm-journey[data-step='3'] .jr-steps :is([data-node='0'], [data-node='1'], [data-node='2']),
.js .dm-journey[data-step='4'] .jr-steps .st,
.js .dm-journey[data-done] .jr-steps .st {
  color: var(--ink2);
}
.js .dm-journey[data-step='1'] .jr-steps [data-node='0'] i,
.js .dm-journey[data-step='2'] .jr-steps :is([data-node='0'], [data-node='1']) i,
.js .dm-journey[data-step='3'] .jr-steps :is([data-node='0'], [data-node='1'], [data-node='2']) i,
.js .dm-journey[data-step='4'] .jr-steps .st i,
.js .dm-journey[data-done] .jr-steps .st i {
  background: var(--accent);
}
.js .dm-journey[data-step='1'] .jr-steps [data-node='0'] span,
.js .dm-journey[data-step='2'] .jr-steps :is([data-node='0'], [data-node='1']) span,
.js .dm-journey[data-step='3'] .jr-steps :is([data-node='0'], [data-node='1'], [data-node='2']) span,
.js .dm-journey[data-step='4'] .jr-steps .st span,
.js .dm-journey[data-done] .jr-steps .st span {
  color: var(--ink2);
}
.dm-journey .jr-scene {
  display: flex;
  flex-direction: column;
  gap: 11px;
  align-items: flex-start;
}
.dm-journey .demo-stack {
  min-height: 214px;
}
.dm-journey .plan-card {
  width: 100%;
  margin-bottom: 0;
}
.dm-journey .q-card,
.dm-journey .drafted {
  width: 100%;
  margin: 0;
}
.dm-journey .bubble {
  max-width: 30ch;
}
.dm-journey .jr-plan {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 10px;
  width: 100%;
}

/* ============================================================
   DEMO 4 - AI expansion, growing out of one line
   ============================================================ */
.dm-expand .xp-seed {
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--radius);
  padding: clamp(14px, 1.6vw, 18px);
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(12px, 1.6vw, 20px);
  align-items: center;
}
.dm-expand .xp-seed .plan-card {
  align-self: center;
}
.dm-expand .ui-note {
  font-size: clamp(15px, 1.6vw, 17.5px);
}
/* The AI row and the "questions to consider" count share one slot. */
.dm-expand .xp-head {
  min-height: 58px;
  align-items: center;
}
.dm-expand .ai-step {
  padding: clamp(10px, 1.4vw, 16px) 0;
}
.dm-expand .xp-count {
  text-align: center;
}
.dm-expand .xp-grow {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(10px, 1.3vw, 16px);
  align-items: start;
}
.dm-expand .xp-branch {
  position: relative;
  padding-top: 22px;
  min-width: 0;
}
/* The line from the original note down into each branch. */
.dm-expand .xp-link {
  position: absolute;
  top: 0;
  left: 50%;
  width: 1px;
  height: 18px;
  background: linear-gradient(180deg, var(--line), color-mix(in srgb, var(--accent) 55%, transparent));
  display: block;
}
.dm-expand .xp-slot {
  display: grid;
  align-items: start;
  min-height: 158px;
}
.dm-expand .xp-slot > * {
  grid-area: 1 / 1;
}
.dm-expand .q-card,
.dm-expand .drafted,
.dm-expand .plan-card {
  margin: 0;
  width: 100%;
}
.dm-expand .q-card strong {
  font-size: 13px;
}
.dm-expand .q-card p {
  font-size: 11.5px;
  margin-top: 5px;
}

/* ============================================================
   DEMO 5 - search the inbox
   ============================================================ */
.dm-search .ui-search {
  gap: 10px;
}
/* Placeholder and typed query occupy the same slot, so the caret sits where
   the placeholder was instead of after it. */
.dm-search .sr-field {
  display: grid;
  align-items: center;
  flex: 1;
  min-width: 0;
}
.dm-search .sr-field > * {
  grid-area: 1 / 1;
}
.dm-search .sr-q {
  color: var(--ink);
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  min-height: 1.2em;
}
/* One row, so filtering never leaves a vertical hole where a row used to be.
   Cards fade in place instead of the grid reflowing - no layout shift, and the
   two matches stay exactly where the eye already found them. */
.dm-search .sr-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 11px);
}
.js .dm-search .sr-card {
  transition:
    opacity 0.28s ease,
    transform 0.42s cubic-bezier(0.2, 0.7, 0.2, 1),
    box-shadow 0.42s ease,
    border-color 0.42s ease;
}
/* Results are instant: misses drop out, hits lift. */
.js .dm-search[data-step='2'] .sr-card[data-miss],
.js .dm-search[data-step='3'] .sr-card[data-miss],
.js .dm-search[data-done] .sr-card[data-miss] {
  opacity: 0;
  transform: scale(0.955);
  pointer-events: none;
}
.js .dm-search[data-step='2'] .sr-card[data-hit],
.js .dm-search[data-step='3'] .sr-card[data-hit],
.js .dm-search[data-done] .sr-card[data-hit] {
  transform: translate3d(0, -2px, 0);
  box-shadow: var(--shadow);
  border-color: color-mix(in srgb, var(--accent) 28%, var(--line));
}
.dm-search .ui-hint {
  margin-top: 2px;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1040px) {
  .demo-links {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 1000px) {
  .steps-four {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 720px) {
  .steps,
  .steps-four {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .steps li {
    flex-direction: row;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 10px;
    padding: 14px 16px;
  }
  .steps .d {
    flex-basis: 100%;
  }
}

/* On phones the hero demo is already one column - wrap it in the site's phone:
   the same device body, bezel, pill notch and rounded screen as .phone, with
   the new demo playing inside. */
@media (max-width: 640px) {
  .hero .demo {
    max-width: 380px;
    margin-inline: auto;
  }
  .hero .demo-frame {
    background: var(--device);
    border: 1px solid rgba(255, 255, 255, 0.07);
    border-radius: 44px;
    padding: 12px;
    box-shadow: var(--shadow-lg), 0 0 0 1px rgba(0, 0, 0, 0.14);
  }
  .hero .demo-frame::after {
    /* the pill notch, matching .phone .notch */
    content: '';
    position: absolute;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    width: 84px;
    height: 22px;
    background: #000;
    border-radius: 999px;
    z-index: 6;
  }
  .hero .demo-screen {
    border-radius: 33px;
  }
  /* Push the app content clear of the notch. */
  .hero .demo-split {
    padding-top: 42px;
  }
  /* A clean device - no callout hanging off the frame. */
  .hero .demo .pin {
    display: none;
  }
}

@media (max-width: 900px) {
  /* The scatter's --x offsets assume three columns. Below that a card is most
     of the row, so a sideways start would push it out of the frame - the pile
     jumbles vertically instead. */
  .js .dm-work[data-step='idle'] .wsp-card,
  .js .dm-work[data-step='0'] .wsp-card {
    transform: translate3d(0, calc(var(--y, 0px) * 0.42), 0) rotate(var(--r, 0deg)) scale(0.96);
  }
  .demo-split {
    grid-template-columns: 1fr;
  }
  .dm-expand .xp-grow {
    grid-template-columns: 1fr;
  }
  .dm-expand .xp-seed {
    grid-template-columns: 1fr;
    align-items: stretch;
  }
  .dm-search .sr-grid {
    grid-template-columns: 1fr 1fr;
  }
  /* Two columns puts the misses on a row of their own, and an emptied row is a
     hole. Below the single-row layout they leave the flow instead, exactly as
     they do in the app's own inbox. */
  .js .dm-search[data-step='2'] .sr-card[data-miss],
  .js .dm-search[data-step='3'] .sr-card[data-miss],
  .js .dm-search[data-done] .sr-card[data-miss] {
    display: none;
  }
  .dm-expand .xp-slot {
    min-height: 0;
  }
  .dm-expand .xp-branch {
    padding-top: 18px;
  }
  .dm-journey .demo-stack {
    min-height: 250px;
  }
}

@media (max-width: 720px) {
  .demo-beats li {
    font-size: 9px;
    padding: 4px 9px;
  }
  .dm-work .wsp-grid {
    grid-template-columns: 1fr 1fr;
  }
  .dm-journey .jr-plan {
    grid-template-columns: 1fr;
  }
  .demo .pin {
    position: static;
    margin-top: 12px;
    max-width: none;
  }
}

@media (max-width: 520px) {
  .demo-links {
    grid-template-columns: 1fr;
  }
  .dm-work .wsp-grid,
  .dm-search .sr-grid {
    grid-template-columns: 1fr;
  }
  .dm-shape .shape-capture {
    min-height: 0;
  }
  .demo-screen .ui-tabs {
    gap: 12px;
    font-size: 11.5px;
  }
}

/* ============================================================
   REDUCED MOTION - the site-wide rule already collapses durations;
   this makes sure nothing is left mid-transform or mid-sweep.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .js .demo :is([data-at], [data-until], .wsp-card, .sr-card) {
    opacity: 1 !important;
    transform: none !important;
  }
  .js .demo [data-until] {
    display: none;
  }
  .js .demo .sr-card[data-miss] {
    display: none;
  }
  .js .demo .wsp-card::after,
  .js .demo .shape-card::after {
    animation: none;
    display: none;
  }
}
