/* ============================================================================
   demos.css — everything inside a documentation demonstration frame
   ----------------------------------------------------------------------------
   The other half of this section's styling is src/docs/docs.css: the shell, the
   sidebar, the document column, prose, the index cards, the search dialog and
   the pager. The line between the two files is the frame's own border. Anything
   that draws INSIDE a `.demo-frame` — the stage and its container-query scale,
   the entrance vocabulary, the four drawings, the phone variant and the
   reduced-motion block — is here. Anything that positions a frame on a page, or
   dresses the card around it, is there.

   WHAT THE CLOCK ACTUALLY CHANGES. clock.js writes four things and nothing
   else: `.entered` on elements carrying `data-from` / `data-only`, `.checked`
   on the rail's rows, `.parked` on the frame, and two custom properties,
   `--progress` and `--travel`. Every rule below is one of those four landing.
   That is the whole mechanism, and it is why a demonstration can be pinned by
   suppressing transitions rather than by being rewritten.

   OPACITY AND TRANSFORM ONLY. Every transition in this file moves opacity, a
   translate, a scaleX or a scaleY. Nothing animates width, height, top, left,
   margin or padding, so no demonstration can reflow a pixel of the page around
   it and every transition runs on the compositor. The rail fills with scaleY
   from a top origin, the explorer sweep with scaleX from a left origin, and the
   payment chip travels in percentages of its own wrapper — no width is ever
   measured, and nothing is reparented mid-pass.

   FOUR DURATIONS, ONE EASE. 380 ms for an entrance, a crossfade or a check;
   420 ms for a fill or a sweep, which cover more distance; 460 ms for the
   payment chip, which covers the most. The one curve is
   cubic-bezier(.23,1,.32,1), an exponential ease-out: it decelerates into a
   stop and never overshoots. docs/design/v3-docs.md's motion tables are the
   source for all four values; nothing here invents a fifth.

   PARKED IS THE OFF SWITCH. `.parked *` drops every transition inside the
   frame, so the instant reset before beat 0, a cancelled pass, an off-screen
   frame and reduced motion all settle without a single thing sliding. The theme
   commit is already covered by shell.css's own `html[data-theme-switching] *`
   rule, so it is not restated here.

   TOKENS ONLY. There is not one literal colour in this file. Every value
   resolves through src/shared/tokens.css, and tokens-dark.css re-points those
   same names, so dark mode costs these drawings nothing.

   THE MINIATURE IS THE SAME DRAWING. `.demo-stage` is always the true
   940 × 587.5 composition; the container query scales it to whatever width the
   frame has. A card is that scale applied to a 419 px cell, not a second,
   simpler picture, which is why a card can never drift from the page it
   previews.

   Load order: tokens.css, tokens-dark.css, shell.css, docs.css, this file.
   ========================================================================== */

/* ── shared values ─────────────────────────────────────────────────────────
   docs.css owns these two names for the whole section; they are repeated here
   at ZERO specificity, so docs.css's own `:root` declaration always wins and
   this file still renders correctly on its own. The two demo-local properties
   are declared on the frame itself, at the value the resting final frame wants,
   because that is the frame a demonstration is painted at before any clock
   runs. */
:where(:root){ --docs-ease:cubic-bezier(.23,1,.32,1); --docs-line:var(--brand-20) }

/* ── 1 · the frame and the stage ───────────────────────────────────────────
   16:10 at every size. `container-type:inline-size` is what makes the scale
   below possible; `isolation` keeps the stage's own stacking context off the
   page. A thumbnail takes no pointer events at all: the card's anchor owns the
   whole gesture, and the picture must never intercept a click meant for it. */

.demo-frame{position:relative;aspect-ratio:16/10;container-type:inline-size;isolation:isolate;border:1px solid var(--docs-line);border-radius:16px;overflow:hidden;background:var(--paper-66);box-shadow:var(--sh-4-panel);color:var(--ink);--progress:1;--travel:200%}
/* The exact same 940 px drawing appears in each index card, scaled to the
   card's own width. Not a screenshot, not a redraw: one transform. */
.demo-stage{position:absolute;left:0;top:0;width:940px;height:587.5px;transform-origin:top left;transform:scale(calc(100cqw / 940px));overflow:hidden;background:radial-gradient(ellipse at 24% 40%,var(--brand-06),transparent 64%);font-family:var(--font-body)}
.thumbnail,.thumbnail .demo-stage{pointer-events:none}
/* The miniature gives up its border, radius and shadow to the card that wraps
   it, and keeps only the hairline under the drawing. That rule belongs to the
   card, so it lives with the card in docs.css (`.section-card .demo-frame`)
   rather than being restated here. */

/* ── 2 · the vocabulary every drawing shares ───────────────────────────────
   The lid across the top, the method strip across the bottom, the contour
   material, and the labelled figures. */

.demo-lid{position:absolute;left:32px;right:32px;top:24px;display:flex;justify-content:space-between;font:10px var(--font-mono);letter-spacing:.14em;color:var(--graphite)}
.method-strip{position:absolute;bottom:0;left:0;right:0;min-height:48px;display:flex;align-items:center;gap:10px;padding:0 28px;border-top:1px solid var(--docs-line);background:var(--paper-66);font:12px var(--font-mono);color:var(--graphite)}
.method-strip>i{width:5px;height:5px;background:var(--brand);border-radius:50%}
.sequence-kind{margin-left:auto;font-size:8px;letter-spacing:.14em}
/* Contours are decorative material. They describe the surface a capsule is
   suspended over; they are never a measurement, a network edge or a graph. */
.terrain{color:var(--brand);stroke:currentColor;stroke-width:.65;opacity:.7}
.terrain .thread{stroke:var(--graphite);stroke-width:.6;opacity:.5;stroke-dasharray:2 5}
.terrain .seed{fill:var(--brand);stroke:none}
.terrain .faint{opacity:.4}
.datum{display:flex;flex-direction:column;gap:7px}
.datum>span{font:9px var(--font-mono);letter-spacing:.12em;color:var(--graphite)}
.datum strong{font-size:21px;font-weight:500;letter-spacing:-.02em}
.datum:last-child strong{text-transform:capitalize}

/* ── 3 · the entrance, and the one way to switch it off ────────────────────
   Every arrival in every drawing is this pair of rules. 7 px of travel for a
   block that arrives on its own; 5 px for a status layer, which is crossfading
   against the layer it replaces in the same grid cell and would otherwise read
   as a jump. `.parked` is the off switch: an instant reset, a cancelled pass,
   an off-screen frame and reduced motion all park, and all settle silently. */

.entrance{opacity:0;transform:translateY(7px);transition:opacity 380ms var(--docs-ease),transform 380ms var(--docs-ease)}
.entrance.entered{opacity:1;transform:translateY(0)}
.status.entrance{transform:translateY(5px)}
.status.entrance.entered{transform:translateY(0)}
.parked *{transition:none!important;animation:none!important}
/* The phone drawing sits in the markup at every width and is shown only by the
   520 px block at the foot of this file. Declared here, ahead of that block, so
   the media query is the exception rather than being overridden by it. */
.phone-demo{display:none}

/* ── 4 · memory lifecycle ──────────────────────────────────────────────────
   A suspended capsule over contours on the left; one grid cell of overlapping
   status layers and the four-marker rail on the right; the capsule's recorded
   aggregates along the bottom. */

.lifecycle-art{position:absolute;left:20px;top:80px;width:510px;height:350px}
.lifecycle-art .terrain{position:absolute;left:-12px;top:38px;width:550px;height:340px}
.floating-capsule{position:absolute;left:82px;top:58px;width:285px;padding:24px;border:1px solid var(--brand-20);border-radius:16px;background:var(--paper-66);backdrop-filter:var(--blur-panel);box-shadow:var(--sh-4-panel);transform:rotate(-5deg)}
.floating-capsule>b{display:block;font-size:21px;line-height:1.4;font-weight:500;letter-spacing:-.03em;margin:13px 0 20px}
.floating-capsule .micro{font-size:9px;color:var(--graphite)}
.art-caption{position:absolute;left:85px;bottom:0;font-size:12px;color:var(--graphite)}
.lifecycle-state{position:absolute;left:575px;right:50px;top:101px}
/* All four layers occupy one cell, so the crossfade needs no height animation
   and the block beneath it never moves. */
.status-layers{display:grid;min-height:121px}
.status{grid-area:1/1}
.status .micro{color:var(--graphite);font-size:9px}
.status h3{font-size:27px;line-height:1.2;font-weight:500;letter-spacing:-.03em;margin:10px 0;text-transform:none}
.status p{font-size:12px;color:var(--graphite)}
.rail{position:relative;margin-top:18px;display:grid;gap:22px;padding:0 0 0 3px}
.rail-line,.rail-fill{position:absolute;left:8px;top:12px;bottom:12px;width:1px;background:var(--docs-line)}
/* One scaleY from a top origin fills the line to the beat the clock is on. */
.rail-fill{background:var(--brand);transform-origin:top;transform:scaleY(var(--progress));transition:transform 420ms var(--docs-ease)}
.rail-row{position:relative;display:flex;align-items:center;gap:16px;font-size:13px;color:var(--graphite);min-height:24px}
.rail-row>i{width:11px;height:11px;border:1px solid var(--docs-line);background:var(--paper);border-radius:50%}
.rail-row.checked>i{background:var(--brand);border-color:var(--brand)}
.rail-row.checked{color:var(--ink)}
.rail-check{margin-left:auto;opacity:0;transition:opacity 380ms var(--docs-ease)}
.rail-check>.icon{width:16px;height:16px;color:var(--brand)}
.checked .rail-check{opacity:1}
.demo-metrics{position:absolute;left:60px;right:50px;bottom:73px;display:flex;gap:45px;padding-top:21px;border-top:1px solid var(--docs-line)}

/* ── 5 · pricing & royalties ───────────────────────────────────────────────
   The quote on the left, the three-station route on the right, the recorded
   split beneath it, and the selected lineage under that. */

.pricing-title{position:absolute;left:47px;top:80px}
.pricing-title .micro{color:var(--graphite)}
.pricing-title h3{font-size:50px;font-weight:400;letter-spacing:-.055em;line-height:1.1;margin:12px 0}
.pricing-title p{font-size:13px;color:var(--graphite)}
.payment-path{position:absolute;left:312px;right:35px;top:112px;height:132px;display:grid;grid-template-columns:repeat(3,1fr);align-items:start}
.payment-wire{position:absolute;height:1px;left:16.66%;right:16.66%;top:25px;background:var(--docs-line)}
.payment-station{z-index:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:9px}
.payment-station>.icon{padding:12px;width:52px;height:52px;border:1px solid var(--docs-line);background:var(--paper);border-radius:14px;color:var(--brand)}
.payment-station b{font-size:13px;font-weight:500}
.payment-station>span{font:9px var(--font-mono);color:var(--graphite);max-width:140px}
/* The chip's wrapper is exactly one column wide, so 0%, 100% and 200% of ITS
   OWN width land it on the three stations. Nothing is measured, and nothing is
   moved between parents mid-travel. */
.travel{position:absolute;top:-31px;left:0;width:33.333333%;text-align:center;transform:translateX(var(--travel));transition:transform 460ms var(--docs-ease)}
.travel span{padding:6px 9px;background:var(--brand);color:var(--on-brand,var(--ink));font:11px var(--font-mono);border-radius:7px}
.split-grid{position:absolute;left:47px;right:47px;top:272px;display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--docs-line);border-bottom:1px solid var(--docs-line);padding:19px 0}
.split-cell{display:grid;grid-template-columns:auto 1fr;gap:4px 16px;align-items:center;padding:0 22px}
.split-cell+.split-cell{border-left:1px solid var(--docs-line)}
.split-cell>strong{grid-row:span 2;font-size:42px;font-weight:400;letter-spacing:-.05em}
.split-cell em{font-style:normal;font-size:20px;margin-left:2px}
.split-cell>span{font-size:12px}
.split-cell>small{font:11px var(--font-mono);color:var(--graphite)}
.fork-chain{position:absolute;top:396px;left:135px;right:135px;display:flex;align-items:center;justify-content:space-between;gap:24px}
.fork-node{display:grid;grid-template-columns:8px auto;gap:7px 10px;align-items:center}
.fork-node>i{width:7px;height:7px;border:1px solid var(--brand);border-radius:50%}
.fork-node:last-child>i{background:var(--brand)}
.fork-node>b{font:13px var(--font-mono);font-weight:400}
.fork-node>small{grid-column:2;font-size:10px;color:var(--graphite)}
.fork-edge{color:var(--brand-38)}
.fork-edge .icon{width:55px;height:23px}
.demo-footnote{position:absolute;bottom:68px;left:32px;right:32px;font-size:11px;text-align:center;color:var(--graphite)}

/* ── 6 · connect an agent ──────────────────────────────────────────────────
   One suspended session surface. The lines accumulate into a transcript rather
   than replacing one another, because the reader is meant to see the exchange
   as a whole by the end. No blinking cursor: nothing here is live. */

.session-surface{position:absolute;left:61px;right:61px;top:67px;bottom:101px;border:1px solid var(--docs-line);border-radius:14px;overflow:hidden;background:var(--paper-66);box-shadow:var(--sh-4-panel)}
.session-head{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--docs-line);font:10px var(--font-mono);color:var(--graphite)}
.session-head>span:first-child{display:flex;align-items:center;gap:8px}
.session-head .icon{width:15px;height:15px;color:var(--brand)}
.session-lines{padding:12px 20px;font:11px/1.6 var(--font-mono)}
.session-line{display:flex;align-items:baseline;gap:12px;padding:7px 0}
.session-line b{font-weight:500}
.session-line .prompt{color:var(--brand)}
.session-line em{font-style:normal;font-size:9px;color:var(--graphite);margin-left:auto}
.session-line small{font:10px var(--font-mono);color:var(--graphite)}
.search-rows{margin:6px 0 5px 23px;border:1px solid var(--docs-line);border-radius:9px;overflow:hidden}
.result-head{display:flex;justify-content:space-between;font-size:8px;padding:7px 12px;background:var(--brand-06);color:var(--graphite)}
.result-row{display:flex;align-items:center;gap:15px;justify-content:space-between;padding:9px 12px;border-top:1px solid var(--docs-line);font-size:10px}
.result-row>div{display:flex;align-items:center;gap:15px;min-width:0}
.result-row>div>b{font-size:10px;font-weight:400;flex:none;color:var(--graphite)}
/* Titles are real records and some of them are long. One line, clipped, so a
   long title can never change the height of a row mid-sequence. */
.result-row>div>span{font-family:var(--font-body);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:460px;font-size:11px}
.result-row strong{font-weight:400}
.result-note{font-size:8px;padding:6px 12px;border-top:1px solid var(--docs-line);color:var(--graphite)}

/* ── 7 · chain & contracts ─────────────────────────────────────────────────
   The target on the left, the three role checks on the right, one advancing
   load line, and the two local counters. The sweep is a review advancing
   through three roles, not a fetch: it is driven by the same `--progress` the
   rail uses, so it cannot get ahead of the checks beside it. */

.chain-art{position:absolute;left:18px;top:82px;width:365px;height:303px;overflow:hidden}
.chain-art .terrain{position:absolute;width:410px;height:200px;left:-35px;top:155px;opacity:.5}
.chain-stone{position:absolute;top:32px;left:95px}
.chain-stone>.icon{color:var(--brand);width:44px;height:44px;stroke-width:1}
.chain-stone h3{font-size:30px;font-weight:400;letter-spacing:-.03em;line-height:1.2;margin:17px 0 12px}
.chain-stone>span{font:9px var(--font-mono);color:var(--graphite)}
.registry-list{position:absolute;left:432px;right:53px;top:82px}
.registry-row{display:flex;gap:18px;padding:16px 0;border-bottom:1px solid var(--docs-line);min-height:88px}
.registry-row>div:last-child{display:flex;flex-direction:column;gap:4px}
.registry-row b{font-size:17px;font-weight:500}
.registry-row span{font-size:11px;color:var(--graphite)}
.registry-row small{font:9px var(--font-mono);color:var(--graphite)}
.registry-check{display:grid;place-items:center;width:25px;height:25px;border:1px solid var(--docs-line);border-radius:50%;margin-top:3px;color:var(--brand)}
.registry-check .icon{width:15px;height:15px}
.explorer-strip{position:absolute;left:52px;right:52px;top:382px;display:flex;align-items:center;gap:20px;padding:17px 18px;overflow:hidden;border:1px solid var(--docs-line);border-radius:9px;font:10px var(--font-mono);background:var(--paper-66)}
.explorer-strip>span{color:var(--graphite)}
.explorer-strip>span:last-child{margin-left:auto}
.explorer-strip b{font-weight:500}
.explorer-sweep{position:absolute;left:0;bottom:0;right:0;height:2px;background:var(--brand);transform-origin:left;transform:scaleX(var(--progress));transition:transform 420ms var(--docs-ease)}
.chain-data{position:absolute;left:52px;right:52px;top:460px;display:flex;align-items:center;gap:43px}
.chain-data p{font-size:10px;line-height:1.7;color:var(--graphite);margin-left:auto}
.chain-data .datum{gap:4px}
.chain-data .datum strong{font:19px var(--font-mono)}

/* ── 8 · responsive ────────────────────────────────────────────────────────
   The phone drawing is hidden at every width above 520 px and the desktop stage
   is hidden below it. Both are in the DOM at once and both carry a method
   strip, so the clock paints whichever one is showing without knowing which
   that is. A card never has a phone variant: demos.js does not render one. */

@media(max-width:760px){
  .hero-frame{max-width:100%}
}

@media(max-width:520px){
  .hero-frame .demo-stage{display:none}
  .phone-demo{position:absolute;inset:0;display:block;padding:19px 20px;font-family:var(--font-body)}
  .phone-caption{font:8px var(--font-mono);color:var(--graphite);letter-spacing:.07em}
  .phone-demo h3{font-size:22px;line-height:1.2;letter-spacing:-.03em;font-weight:500;margin:15px 0}
  .phone-demo>p{font-size:9px;color:var(--graphite);margin-top:15px}
  .phone-rail{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-top:17px}
  .phone-rail>span{position:relative;display:flex;flex-direction:column;gap:8px;font-size:9px;color:var(--graphite)}
  .phone-rail>span::before{content:'';position:absolute;top:3px;left:5px;right:-6px;height:1px;background:var(--docs-line)}
  .phone-rail>span:last-child::before{display:none}
  .phone-rail i{width:7px;height:7px;border:1px solid var(--brand);border-radius:50%;position:relative;background:var(--paper)}
  .phone-rail .checked i{background:var(--brand)}
  .phone-method{position:absolute;bottom:0;left:0;right:0;min-height:31px;padding:8px 15px;border-top:1px solid var(--docs-line);background:var(--paper-66);font:8px var(--font-mono);color:var(--graphite)}
  .phone-split{display:grid;grid-template-columns:repeat(3,1fr);gap:13px}
  .phone-split>span{display:flex;flex-direction:column;font-size:9px;color:var(--graphite)}
  .phone-split b{font:21px var(--font-mono);color:var(--ink);margin-bottom:3px}
  .phone-demo[data-demo="pricing"] p{font-size:8px}
  .phone-terminal{font:9px/1.65 var(--font-mono);margin-top:12px;display:grid;gap:5px}
  .phone-terminal span{color:var(--graphite);padding-left:12px;font-size:8px}
  .phone-registry{display:grid;gap:9px;font-size:10px}
  .phone-registry .icon{width:14px;height:14px;color:var(--brand);margin-right:7px}
  .hero-frame[data-demo="chain"] .phone-demo h3{font-size:21px;margin:16px 0}
  .hero-frame[data-demo="chain"] .phone-demo>p{margin-top:10px;font-size:8px}
}

/* ── 9 · reduced motion ────────────────────────────────────────────────────
   The preference is honoured for the whole section, not only for these frames:
   clock.js never starts a timer under it, and this block guarantees that
   whatever is already painted stays painted. The card arrow is named
   explicitly because it is a hover transform rather than a sequence beat, and
   `transition:none` alone would still let it move. */

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
  .section-card:is(:hover,:focus-visible) .card-title>.icon:last-child{transform:none}
}
