/* Below-the-fold devices (in-body <link>). No content-visibility. */

/* ---- quickstart, drawn as a CI run */
.how { padding-block: calc(var(--section-y) * 0.7) calc(var(--section-y) * 0.4) }
.how-head, .film-head { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); align-items: end }
.how-head h2, .film-head h2 { grid-column: 1 / 7 }
:is(.how-head, .film-head) .sec-sub { grid-column: 8 / 13; margin: 0 0 4px }
.run { margin-top: 48px }
.run-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 24px; padding-bottom: 18px; border-bottom: 1px solid var(--line) }
.run-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; font-size: 15px; font-weight: 500; letter-spacing: -0.01em }
.run-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); align-self: center }
.run-meta { font-size: 13px; font-weight: 400; color: var(--fg-2) }
.run-sum { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; font-size: 13px; color: var(--fg-1); font-variant-numeric: tabular-nums }
.run-sum .ok::before, .run-sum .bad::before { content: ""; display: inline-block; width: 6px; height: 6px; margin: 0 8px 1px 0; border-radius: 50%; background: var(--accent) }
.run-sum .bad::before { border-radius: 1px; background: var(--danger); transform: rotate(45deg) }
.run-sum .bad { transition: opacity var(--d-base) ease }
.run.is-playing .run-sum .bad { opacity: 0 }

/* Two lanes: A passes; B forks from Classify. */
.run-graph { --gx: 28px; --gy: 32px; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); column-gap: var(--gx); row-gap: var(--gy); margin-top: 36px }
.node { position: relative; min-width: 0 }
.n-a1 { grid-area: 1 / 1 } .n-a2 { grid-area: 1 / 2 } .n-a3 { grid-area: 1 / 3 } .n-a4 { grid-area: 1 / 4 } .n-a5 { grid-area: 1 / 5 } .n-a6 { grid-area: 1 / 6 }
.n-b1 { grid-area: 2 / 4 } .n-b2 { grid-area: 2 / 5 } .n-b3 { grid-area: 2 / 6 }
.node button { position: relative; z-index: 1; display: grid; height: 100%; grid-template-columns: 16px minmax(0, 1fr); column-gap: 10px; row-gap: 4px; align-items: center; width: 100%; min-height: 64px; padding: 11px 12px 11px 14px; border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-1); color: var(--fg-0); text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent; transition: border-color var(--d-instant) ease, background-color var(--d-instant) ease, transform var(--d-spring) var(--spring) }
.node button:hover { border-color: var(--line-hover) }
.node button:active { transform: scale(0.97); transition-duration: 100ms }
.node button[aria-current="true"] { border-color: var(--fg-2); background: var(--bg-3); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.04) }
.nm { overflow: hidden; font-size: 15px; font-weight: 500; line-height: 1.2; letter-spacing: -0.01em; white-space: nowrap; text-overflow: ellipsis }
.du { grid-column: 2; font: 400 11px/1.2 var(--font-mono); color: var(--fg-2); font-variant-numeric: tabular-nums; transition: opacity var(--d-base) ease }
.st { grid-row: 1 / 3; position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1px solid var(--fg-3); transition: background-color var(--d-fast) ease, border-color var(--d-fast) ease }
.st[data-st="pass"] { border-color: var(--accent); background: var(--accent) }
.st[data-st="pass"]::after { content: ""; position: absolute; left: 4.5px; top: 2.5px; width: 4px; height: 7px; border: solid var(--on-accent); border-width: 0 1.5px 1.5px 0; transform: rotate(45deg) }
.st[data-st="fail"] { border-color: var(--danger); background: var(--danger) }
.st[data-st="fail"]::after { content: ""; position: absolute; inset: 3px; background: linear-gradient(45deg, transparent 43%, var(--bg-0) 43% 57%, transparent 57%), linear-gradient(-45deg, transparent 43%, var(--bg-0) 43% 57%, transparent 57%) }
.st[data-st="run"] { border-color: var(--line-strong); background: conic-gradient(var(--info) 0 25%, transparent 25%); -webkit-mask: radial-gradient(circle, transparent 5px, #000 5.5px); mask: radial-gradient(circle, transparent 5px, #000 5.5px); animation: spin 700ms linear infinite }
.node:has(.st[data-st="queue"]) .du, .node:has(.st[data-st="run"]) .du { opacity: 0 }
@keyframes spin { to { transform: rotate(1turn) } }
.node::before { content: ""; position: absolute; right: 100%; top: 50%; width: var(--gx); border-top: 1px solid var(--line-hover); transition: border-color var(--d-base) ease }
.n-a1::before, .n-b1::before { display: none }
.node:has(.st[data-st="pass"])::before { border-top-color: var(--accent-line) }
.n-b1::after { content: ""; position: absolute; right: 100%; bottom: 50%; width: calc(var(--gx) + 50%); height: calc(var(--gy) + 50%); border: 1px dashed var(--danger-line); border-width: 0 0 1px 1px; border-bottom-left-radius: 14px; pointer-events: none }
.fail button { border-color: var(--danger-line) }
.fail .du { color: var(--danger) }
.gate::after { content: ""; position: absolute; z-index: 2; right: calc(100% + var(--gx) / 2 - 5px); top: 50%; width: 9px; height: 9px; margin-top: -5px; border: 1px solid var(--fg-1); background: var(--bg-0); transform: rotate(45deg); transition: border-color var(--d-base) ease, background-color var(--d-base) ease }
.gate:has(.st[data-st="pass"])::after { border-color: var(--accent); background: var(--accent) }
/* The first artifact stamp lands: the Promote gate answers, once (app.js). */
.gate.is-pulse::after { animation: gate-pulse 720ms var(--ease-out) }
@keyframes gate-pulse { 0% { box-shadow: 0 0 0 0 var(--accent-line) } 30% { transform: rotate(45deg) scale(1.45) } 100% { box-shadow: 0 0 0 10px transparent; transform: rotate(45deg) } }

/* Each node's log: what the step does beside its terminal (real commands, illustrative output). */
.run-log { display: grid; grid-template-columns: minmax(0, 1fr); gap: 40px; margin-top: 32px; padding-top: 28px; border-top: 1px solid var(--line) }
.rl-panel { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 16px var(--grid-gap); align-items: start; min-width: 0 }
.rl-copy { grid-column: 1 / 6; min-width: 0 }
.rl-term { grid-column: 6 / 13; min-width: 0 }
.rl-term .term-body { padding-block: 14px 16px }
.rl-term .out { color: var(--fg-1) }
.rl-term :is(.out.ok, .out.ad, .out .ok) { color: var(--accent) }
.rl-term :is(.out.rm, .out .rm) { color: var(--danger) }
.rl-term :is(.out.wn, .out .wn) { color: var(--warn) }
.rl-term .out .note { color: var(--fg-2) }
/* Scripting: every log in one cell, the tallest sets the height, so a pick never moves the page. */
@media (scripting: enabled) {
  .run-log > .rl-panel { grid-area: 1 / 1 }
  .rl-panel:not(.is-on) { visibility: hidden }
}
.run-log h3 { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px; font-size: var(--t-h4); font-weight: 500; line-height: 1.3; letter-spacing: -0.01em }
.run-log .k, .trust-detail .k, .fc-t .k, .fr-ch .k { font: 500 11px/1 var(--font-mono); letter-spacing: 0.06em; color: var(--fg-2); font-variant-numeric: tabular-nums }
.rl-copy p { margin-top: 10px; max-width: 37.128em; font-size: 15px; line-height: 1.6; color: var(--fg-1) }
.rl-copy .check-list { margin-top: 16px }
.run-log.is-swap > .is-on { animation: detail-in var(--d-base) var(--ease-out) }
@keyframes detail-in { from { opacity: 0; transform: translateY(4px) } }
.run-foot { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: baseline; gap: 14px 20px; margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line); perspective: 1500px }
.run-note { justify-self: end; margin-top: 4px; font-size: 13px; color: var(--fg-2) }
/* The artifacts: a deck of Candidates over the registry tray. As each gate passes (app.js) a card
 * lifts off the deck, is stamped Verified and drops into its slot; the tray's lip hides its feet.
 * Without scripting or motion they are already filed. */
.run-art { --tilt: 0deg; position: relative; grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px var(--grid-gap); margin-top: 6px; padding-bottom: 34px; transform-style: preserve-3d; transform: rotateX(var(--tilt)); transition: transform 700ms var(--spring) }
.run-art::before, .run-art::after { content: ""; position: absolute; left: -10px; right: -10px; pointer-events: none }
/* Floor (with the cards' contact shadows) and lip, in the list's 3D space. */
.run-art::before { top: calc(100% - 34px); height: 100px; border: 1px solid var(--line); border-radius: 18px; background: radial-gradient(15% 22% at 17% 50%, rgba(0, 0, 0, 0.7), transparent), radial-gradient(15% 22% at 50% 50%, rgba(0, 0, 0, 0.7), transparent), radial-gradient(15% 22% at 83% 50%, rgba(0, 0, 0, 0.7), transparent), linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.015)), var(--bg-sunken); transform-origin: 50% 0; transform: translateZ(-50px) rotateX(90deg) }
.run-art::after { content: "Registry \00B7  local"; content: "Registry \00B7  local" / ""; top: calc(100% - 40px); height: 36px; display: flex; align-items: flex-end; padding: 0 20px 8px; border: 1px solid var(--line-strong); border-bottom: 0; border-top-color: rgba(255, 255, 255, 0.2); border-radius: 12px 12px 0 0; background: linear-gradient(rgba(255, 255, 255, 0.07), transparent 10px), var(--bg-1); box-shadow: 0 -12px 22px -12px rgba(0, 0, 0, 0.9); font: 500 10px/1 var(--font-mono); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--fg-2); -webkit-mask: linear-gradient(#000 55%, transparent); mask: linear-gradient(#000 55%, transparent); transform: translateZ(50px) }
/* Tilted only while the deck deals (app.js drops data-pose when a card settles): at rest the cards
 * read flat, on the screen's plane. */
.run-art:has(.art[data-pose]) { --tilt: -15deg }
.run-art .art { transform-origin: 50% 100%; transition: transform var(--d-spring) var(--spring) }
/* The deck: every card over the middle slot, fanned behind the top one. */
.run-art .art[data-pose="deck"] { transform: translate3d(calc((1 - var(--i)) * (100% + var(--grid-gap)) + var(--fx, 0px)), var(--fy, -30px), calc(76px - var(--i) * 22px)) rotate(var(--fr, 0deg)) }
.run-art .art[data-pose="deck"]:nth-child(2) { --fx: -10px; --fy: -38px; --fr: -3.5deg }
.run-art .art[data-pose="deck"]:nth-child(3) { --fx: 10px; --fy: -46px; --fr: 3.5deg }
.run-art .art[data-pose="lift"] { transform: translate3d(calc((1 - var(--i)) * (100% + var(--grid-gap)) * 0.5), -64px, 140px) }
/* Phones: the log opens under the tapped step (app.js moves it). */
.run-slot { margin-left: 22px; padding-left: 20px; border-left: 1px solid var(--line-strong) }
.run-slot .run-log { margin-top: 0; padding: 14px 0 8px; border-top: 0 }

/* ---- trust: the mark is the product diagram */
.trust { padding-block: calc(var(--section-y) * 0.6) }
.trust-stage { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--grid-gap); align-items: center }
.trust-copy { grid-column: 1 / 6 }
/* Observed at the bottom, Compiled on top, as the staircase climbs; the DOM (and a screen reader) still
 * reads Observed up to Compiled. */
.trust-steps { display: flex; flex-direction: column-reverse; margin-top: 36px; border-top: 1px solid var(--line) }
.trust-steps li { border-bottom: 1px solid var(--line) }
.trust-steps button { position: relative; display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 14px; width: 100%; min-height: 48px; padding: 0 2px 0 16px; border: 0; background: transparent; color: var(--fg-1); text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent; transition: color var(--d-instant) ease, background-color var(--d-instant) ease }
.trust-steps button::before { content: ""; position: absolute; left: 0; top: 10px; bottom: 10px; width: 2px; border-radius: 2px; background: var(--accent); transform: scaleY(0); transition: transform var(--d-spring) var(--spring) }
.trust-steps button:hover { color: var(--fg-0) }
.trust-steps button:active { background: var(--bg-1) }
.trust-steps button[aria-current="true"] { color: var(--fg-0) }
.trust-steps button[aria-current="true"]::before { transform: scaleY(1) }
.trust-steps .lv { font: 500 11px/1 var(--font-mono); letter-spacing: 0.06em; color: var(--fg-2); font-variant-numeric: tabular-nums }
.trust-steps button[aria-current="true"] .lv { color: var(--accent) }
.trust-steps .t { font-size: var(--t-h4); font-weight: 500; letter-spacing: -0.015em }
/* Other rungs' tags dim to 0.8, no lower: every state colour keeps >= 5.26:1 (AA) at 11px. */
.trust-steps .tag { opacity: 0.8; transition: opacity var(--d-fast) ease }
.trust-steps button[aria-current="true"] .tag { opacity: 1 }
/* A resting Compiled tag drops its fill. */
.trust-steps button:not([aria-current="true"]) .t-state-compiled { background: var(--accent-soft); color: var(--accent) }
.exits { margin-top: 28px; container-type: inline-size }
.exit-rail { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px }
/* Exits that cannot share a line form a 2 x 2, never 3 + 1. */
@container (max-width: 391px) {
  .exit-rail { display: grid; grid-template-columns: repeat(2, max-content) }
}
.exit-rail .tag { height: 26px; padding: 0 9px }
.exits-note { margin-top: 12px; font-size: 13px; line-height: 1.5; color: var(--fg-2) }
/* The revocation's cause (app.js shows it where the story can play); spent once the tombstone is written. */
.exits [data-revoke] { margin-top: 14px }
.exits [data-revoke][aria-disabled="true"] { border-color: var(--line); background: transparent; color: var(--fg-2); cursor: default }
.trust-figure { grid-column: 6 / 13; display: flex; flex-direction: column; align-items: stretch; gap: 24px; padding: 44px 40px 32px }

/* The staircase: the mark extruded 20 deep and seen from above, so its left flank is a flight of
 * treads. --rx/--ry turn it (it arrives flat, as the mark, and turns once on first view: app.js);
 * labels turn back by the same angles, so they always face the reader. */
.ladder { --lad: min(340px, 100% - 230px); position: relative; width: var(--lad); aspect-ratio: 1; margin: 8px auto 12px; container-type: inline-size; perspective: 1800px; transform: translateX(-6px) }
.st-body { --dz: calc(20 * var(--u)); --rx: -30deg; --ry: 28deg; transform: rotateX(var(--rx)) rotateY(var(--ry)) translateZ(calc(var(--dz) / -2)); transition: transform 1200ms var(--spring) }
.ladder.is-flat .st-body { --rx: 0deg; --ry: 0deg }
.st-face path { fill: #141417; stroke: rgba(161, 161, 170, 0.45); stroke-width: 0.24; stroke-linejoin: miter }
.st-face .st-edge { fill: none; stroke: var(--accent); stroke-width: 0.5; stroke-linecap: square; opacity: 0; transition: opacity var(--d-slow) ease }
.st-face .is-on { opacity: 1 }
/* Climbed treads (and the Compiled slope) light up, from the packet's colour. */
.xw[data-lv]::after { content: ""; position: absolute; inset: 0; background: linear-gradient(rgba(91, 208, 138, 0.06), rgba(91, 208, 138, 0.3)); box-shadow: inset 0 1px 0 var(--accent-line), inset 0 -1px 0 var(--accent); opacity: 0; transition: opacity var(--d-slow) ease }
.xw.is-on::after { opacity: 1 }
.st-floor { position: absolute; left: calc(-14 * var(--u)); top: calc(56 * var(--u)); width: calc(92 * var(--u)); height: calc(var(--dz) + 30 * var(--u)); transform-origin: 0 0; transform: translateZ(calc(-8 * var(--u))) rotateX(90deg); background: radial-gradient(circle, var(--tex-dot) 0.9px, transparent 1.3px) 0 0 / 8px 8px; -webkit-mask: radial-gradient(closest-side, #000 30%, transparent); mask: radial-gradient(closest-side, #000 30%, transparent) }
/* The packet (.st-pk, .cube: styles.css) stands in a pool of its own light. */
.st-pool { position: absolute; left: calc(-7 * var(--u)); top: calc(-7 * var(--u) - 0.5px); width: calc(14 * var(--u)); height: calc(14 * var(--u)); background: radial-gradient(closest-side, rgba(91, 208, 138, 0.5), rgba(91, 208, 138, 0.12) 55%, transparent); transform: rotateX(90deg) }
/* The revoked guard: a Verified cube on its rung that slides off the side and falls; a tombstone
 * (the record, never a delete) stays on the floor. It shows only once "Revoke guard@v1" arms it
 * (app.js), and it rests in front of the packet's lane, so the two never cross. Its faces fade, not
 * the body: opacity on a preserve-3d body would flatten the cube. */
.st-tomb { position: absolute; left: 0; top: 0; transform-style: preserve-3d; transform: translate3d(calc(15.5 * var(--u)), calc(24 * var(--u)), calc(var(--dz) * 0.86)) rotateX(0deg) }
.st-tomb .cube { --c: calc(4 * var(--u)) }
.st-tomb .cube > i { border-color: var(--accent-line); background: #1f1f23; opacity: 0; transition: opacity 200ms ease }
.st-tomb .cube > :nth-child(2) { background: #17171a }
.st-tomb .cube > :nth-child(3) { background: #2b2b30 }
.st-body.is-arming .st-tomb .cube > i { opacity: 1 }
.st-grave { position: absolute; left: 0; top: 0; width: calc(8 * var(--u)); height: calc(8 * var(--u)); border: 1px dashed var(--danger-line); border-radius: 3px; background: var(--bg-2); opacity: 0; transform: translate3d(calc(11.5 * var(--u)), calc(52 * var(--u) - 0.5px), calc(var(--dz) + 8 * var(--u))) rotateX(90deg); transition: opacity var(--d-slow) ease }
.trust:has([data-tomb][data-st="revoked"]) .st-body:not(.is-fall) .st-tomb { visibility: hidden }
.trust:has([data-tomb][data-st="revoked"]) .st-grave { opacity: 1; transition-delay: 900ms }
.st-body.is-fall .st-tomb { animation: st-fall 1000ms both }
.st-body.is-fall .st-tomb .cube > i { animation: fade-out 1000ms both }
@keyframes st-fall {
  0% { animation-timing-function: var(--ease-in-out) }
  40% { transform: translate3d(calc(15.5 * var(--u)), calc(24 * var(--u)), calc(var(--dz) + 3 * var(--u))) rotateX(0deg); animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6) }
  100% { transform: translate3d(calc(15.5 * var(--u)), calc(56 * var(--u)), calc(var(--dz) + 8 * var(--u))) rotateX(-100deg) }
}
@keyframes fade-out { 0%, 70% { opacity: 1 } 100% { opacity: 0 } }
/* E9 "three runs to Trusted": three ghost cubes hop the ground-to-Verified treads while the packet
 * waits, each arrival lighting the tally on the Trusted tread; then they dissolve into the packet
 * and it climbs. Faces fade, never bodies (opacity on preserve-3d would flatten the cubes). */
.st-ghost { pointer-events: none }
.st-ghost .cube { --c: calc(3 * var(--u)) }
.st-ghost .cube > i { border-color: var(--accent-line); background: rgba(91, 208, 138, 0.22); opacity: 0; transition: opacity 200ms ease }
.st-ghost .cube > :nth-child(2) { background: rgba(91, 208, 138, 0.13) }
.st-ghost .cube > :nth-child(3) { background: rgba(91, 208, 138, 0.32) }
.st-ghost.is-run .cube > i { opacity: 1 }
.st-ghost.is-merge .cube { scale: 0.6; transition: scale 260ms var(--ease-in) }
.st-ghost.is-merge .cube > i { opacity: 0; transition: opacity 260ms ease }
/* Teleporting home between runs (faces already dark). */
.is-snap, .is-snap .st-pky { transition: none }
/* The tally: how many independent runs stand behind Trusted right now. */
.tread[data-runs]::after { content: " " attr(data-runs) "/3"; color: var(--accent) }
.ladder .tread { position: absolute; left: 0; top: 0; transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), var(--dz)) rotateY(calc(-1 * var(--ry))) rotateX(calc(-1 * var(--rx))) translate(-100%, -50%); white-space: nowrap; transition: color var(--d-base) ease, transform 1200ms var(--spring) }
.ladder .tread-r { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), var(--dz)) rotateY(calc(-1 * var(--ry))) rotateX(calc(-1 * var(--rx))) translate(0, -50%) }
.tread.is-on { color: var(--fg-0) }
/* Pointing at a state on the figure picks it (app.js forwards the click to its rung). The solids let
 * the pointer through: the front face's box stands in front of the labels' far ends. */
@media (hover: hover) and (pointer: fine) {
  .ladder { pointer-events: none }
  .ladder .tread { pointer-events: auto; cursor: pointer }
  .ladder .tread:hover { color: var(--fg-0) }
}
.tread .sub { display: block; margin-top: 6px; font: 400 13px/1.3 var(--font-sans); letter-spacing: 0; text-transform: none; color: var(--fg-2) }
.trust-detail { min-height: 198px; padding: 20px 4px 0; border-top: 1px solid var(--line) }
.trust-detail h3 { display: flex; align-items: baseline; gap: 12px; font-size: var(--t-h4); font-weight: 500; line-height: 1.3; letter-spacing: -0.01em }
.trust-detail .k, .fc-t .k, .fr-ch .k { color: var(--accent) }
.trust-detail p { margin-top: 8px; font-size: 15px; line-height: 1.55; color: var(--fg-1) }
.trust-detail dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 24px; margin-top: 14px }
.trust-detail dd { margin-top: 4px; font-size: 15px; line-height: 1.45 }
.trust-detail.is-swap > :not(.td-ghost) { animation: detail-in var(--d-base) var(--ease-out) }
/* All states stacked in one cell (app.js): the figure never moves. */
.trust-detail.has-ghost { display: grid; grid-template-columns: minmax(0, 1fr) }
.trust-detail.has-ghost > * { grid-area: 1 / 1; min-width: 0 }
.td-ghost { display: grid; visibility: hidden; pointer-events: none }
.td-ghost > * { grid-area: 1 / 1; min-width: 0 }

@media (min-width: 1024px) and (max-width: 1279px) {
  .run-graph { --gx: 18px }
  .node button { padding-inline: 11px 8px }
  .nm { white-space: normal; text-wrap: balance }
}
@media (min-width: 1024px) and (max-height: 900px) {
  .ladder { --lad: min(300px, 100% - 230px) }
  .trust-figure { padding-top: 36px; gap: 18px }
  .trust-steps { margin-top: 28px }
  .trust-steps button { min-height: 44px }
}

/* ---- tablet */
@media (max-width: 1023px) {
  :is(.how-head, .film-head) :is(h2, .sec-sub) { grid-column: 1 / -1 }
  :is(.how-head, .film-head) .sec-sub { margin-top: 18px }
  .run { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--grid-gap); margin-top: 48px }
  .run-bar, .run-foot { grid-column: 1 / -1 }
  .run-graph { grid-column: 1 / 6; grid-template-columns: minmax(0, 1fr); row-gap: 10px; margin-top: 28px }
  .run-graph .node { grid-area: auto }
  .node::before { right: auto; left: 22px; top: auto; bottom: 100%; width: 0; height: 10px; border-top: 0; border-left: 1px solid var(--line-hover) }
  .node:has(.st[data-st="pass"])::before { border-left-color: var(--accent-line) }
  .node button { grid-template-columns: 16px minmax(0, 1fr) auto; min-height: 48px; padding-block: 10px }
  .st { grid-row: auto }
  .du { grid-column: 3 }
  .gate::after { right: auto; left: 18px; top: -5px; margin-top: 0 }
  .n-b1 { margin-top: 44px }
  .n-b1::after { content: "Forks from Classify when a session ends red"; right: auto; bottom: calc(100% + 12px); left: 0; width: auto; height: auto; border: 0; font-size: 13px; line-height: 1.3; color: var(--fg-2); white-space: nowrap }
  .n-b2, .n-b3 { margin-left: 20px }
  .n-b2::before, .n-b3::before { left: 2px; border-left: 1px dashed var(--danger-line) }
  .run-log { grid-column: 6 / -1; align-content: start; margin-top: 28px; padding-top: 0; border-top: 0 }
  .rl-panel { grid-template-columns: minmax(0, 1fr) }
  .rl-copy, .rl-term { grid-column: 1 / -1 }
  .run-note { justify-self: start }
  .trust-copy { display: contents }
  .trust-copy > h2, .trust-copy > .sec-sub, .trust-figure, .trust-steps, .exits { grid-column: 1 / -1 }
  .trust-stage { align-items: start; row-gap: 0 }
  .trust-copy > .sec-sub { order: 1 }
  .trust-steps { order: 2 }
  .trust-figure { order: 3; margin-top: 24px }
  /* app.js moves the readout under the rungs. */
  .trust-copy > .trust-detail { order: 2; grid-column: 1 / -1; padding: 20px 0 0; border-top: 0 }
  .exits { order: 4 }
  .ladder { --lad: min(300px, 100% - 230px) }
}

/* ---- phone: one column */
@media (max-width: 767px) {
  .run-graph, .run-log { grid-column: 1 / -1 }
  .run-log { margin-top: 32px }
  /* The figure sits above the rungs, so each tap climbs where the finger can see it. */
  .trust-figure { order: 1; margin-top: 24px; padding: 20px 16px 18px; border-radius: var(--r-lg) }
  .trust-copy > .trust-steps { margin-top: 20px }
  /* The figure large; only the active state is named on it (the rungs below name them all). The
   * gutter is the widest labels' overhang: Observed reaches ~58px left of the box, Compiled ~29px
   * right, hence the 14px nudge. */
  .ladder { --lad: min(280px, 100% - 80px); margin: 4px auto 8px; transform: translateX(14px) }
  .ladder .tread { transition: color var(--d-base) ease, opacity var(--d-base) ease, transform 1200ms var(--spring) }
  .ladder .tread:not(.is-on) { opacity: 0; pointer-events: none }
  .tread { letter-spacing: 0.08em }
  .tread .sub { display: none }
  .trust-detail dl { grid-template-columns: minmax(0, 1fr); gap: 10px }
  .trust-steps .tag { display: none }
}
/* Phones: the log opens under the tapped step, so only the open one takes room. */
@media (scripting: enabled) and (max-width: 767px) {
  .rl-panel:not(.is-on) { display: none }
}

/* ---- artifacts: data-st is candidate, verified or revoked (tombstone). */
.art { position: relative; isolation: isolate; display: grid; align-content: start; gap: 8px; min-width: 0; padding: 12px 14px 13px; border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--bg-1); box-shadow: var(--e-1) }
.art-h { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 10px; min-width: 0 }
.art .art-id { min-width: 0; overflow: hidden; font-size: 13px; text-overflow: ellipsis; white-space: nowrap }
.art-s { display: inline-grid; justify-items: end }
.art-s > * { grid-area: 1 / 1 }
.art-t { font-size: 14px; font-weight: 500; line-height: 1.35; letter-spacing: -0.01em }
.art-m { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; font: 400 11px/1.3 var(--font-mono); color: var(--fg-2) }
.art-hash, .art-sig { display: inline-flex; align-items: center; gap: 6px }
/* A hash glyph, never a made-up hash. */
.art-hash i { width: 34px; height: 9px; background: repeating-linear-gradient(90deg, currentColor 0 1px, transparent 1px 3px, currentColor 3px 5px, transparent 5px 6px, currentColor 6px 7px, transparent 7px 10px); opacity: 0.7 }
.stamp { position: relative; display: inline-flex }
.ring { position: absolute; left: -4px; top: -4px; width: calc(100% + 8px); height: calc(100% + 8px); overflow: visible; fill: none; stroke: var(--accent); stroke-width: 1; pointer-events: none }
/* State swaps sequence, never overlap: what leaves goes in 100ms, what arrives waits 100ms. */
.ring rect { x: 0.5px; y: 0.5px; width: calc(100% - 1px); height: calc(100% - 1px); rx: 7px; stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 100ms ease }
.art .t-state-candidate, .art .t-state-revoked { transition: opacity 80ms ease, visibility 0s linear 80ms }
.art .stamp { opacity: 0; visibility: hidden; transform: rotate(-3deg) scale(1.2); transition: opacity 100ms ease, transform 100ms ease, visibility 0s linear 100ms }
.art .t-state-revoked, .art[data-st]:not([data-st="candidate"]) .t-state-candidate { opacity: 0; visibility: hidden }
.art[data-st="candidate"] .t-state-candidate, .art[data-st="revoked"] .t-state-revoked { opacity: 1; visibility: visible; transition: opacity var(--d-base) ease 100ms, visibility 0s linear 100ms }
.art[data-st="verified"] .stamp { opacity: 1; visibility: visible; transform: none; transition: opacity 120ms ease 100ms, transform var(--d-spring) var(--spring) 100ms, visibility 0s linear 100ms }
/* The ring draws around a stamp that has settled. */
.art[data-st="verified"] .ring rect { stroke-dashoffset: 0; transition: stroke-dashoffset 560ms var(--ease-out) 340ms }
.art::before { content: ""; position: absolute; inset: -5px; border: 1px dashed var(--danger); border-radius: calc(var(--r-md) + 4px); opacity: 0; transition: opacity var(--d-slow) ease; pointer-events: none }
.art-b { display: grid } .art-b > * { grid-area: 1 / 1; transition: opacity 100ms ease, transform 100ms ease }
.art-tomb { font: 400 12px/1.4 var(--font-mono); color: var(--fg-2); opacity: 0; transform: translateY(4px) }
.art[data-st="revoked"]::before { opacity: 0.6 }
.art[data-st="revoked"] .art-b .art-t { opacity: 0; transform: translateY(-4px) }
.art[data-st="revoked"] .art-tomb { opacity: 1; transform: none; transition: opacity var(--d-base) ease 100ms, transform var(--d-spring) var(--spring) 100ms }
.tomb { max-width: 360px; margin: 18px 5px 5px }
.ghost { border-style: dashed; background: transparent; box-shadow: none }
.ghost .art-t, .ghost .art-m { color: var(--fg-2) }

/* Pointer light (fine pointers): --mx/--my from app.js. Isolation keeps the wash above each
 * spot's own background (E7 extends .spot to .plan and .term-bar). */
@media (hover: hover) and (pointer: fine) {
  .spot { overflow: hidden; isolation: isolate }
  .spot::after, .lit::before { content: ""; position: absolute; left: -110px; top: -110px; width: 220px; height: 220px; border-radius: 50%; transform: translate(var(--mx, -400px), var(--my, -400px)); pointer-events: none }
  .spot::after { z-index: -1; background: radial-gradient(closest-side, rgba(255, 255, 255, 0.07), transparent); opacity: 0; transition: opacity var(--d-base) ease }
  .lit { position: absolute; inset: 0; z-index: 2; padding: 1px; overflow: hidden; border-radius: inherit; opacity: 0; transition: opacity var(--d-base) ease; pointer-events: none; -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0) }
  .lit::before { background: radial-gradient(closest-side, var(--accent-hover), transparent) }
  .spot:hover::after, .spot:hover > .lit { opacity: 1 }
}

.ff .tag { height: 19px; padding: 0 6px; font-size: 10px }

/* ---- film: each .fx has an in (from --fa over --fd) and an out (from --fb over --fe), as fractions
 * of the film. With scroll timelines native-property keyframes run those ranges of the film's
 * timeline (see the @supports block below). Elsewhere, and in the storyboard, --fi/--fo derive from
 * the playhead --fp (app.js writes them in the rAF fallback) and the rules here read them. */
@property --fp { syntax: "<number>"; inherits: true; initial-value: 0 }
@property --fi { syntax: "<number>"; inherits: true; initial-value: 1 }
@property --fo { syntax: "<number>"; inherits: true; initial-value: 0 }
@keyframes fr-p { from { transform: scaleX(0) } to { transform: none } }
.fx { --fi: clamp(0, (var(--fp) - var(--fa, -1)) / var(--fd, 0.03), 1); --fo: clamp(0, (var(--fp) - var(--fb, 2)) / var(--fe, 0.03), 1); opacity: calc(var(--fi) - var(--fo)) }
.up { transform: translateY(calc((1 - var(--fi)) * 10px - var(--fo) * 6px)) }
.film { padding-top: calc(var(--section-y) * 0.7) }
.film-track { --seg: clamp(190px, 24svh, 250px); position: relative; height: calc(100svh + 6 * var(--seg)) }
.film-stage { --fp: 0.62; position: sticky; top: 0; display: flex; flex-direction: column; justify-content: center; gap: 16px; height: 100svh; padding: 84px 0 20px }
.fs-top { display: flex; align-items: center; justify-content: space-between; gap: 16px }
.fs-t { display: flex; align-items: center; gap: 12px; font-size: 13px; color: var(--fg-2) }
.film-play { padding-left: 12px }
/* The longer label reserves the width, so the toggle never resizes. */
.film-play .lbl { align-items: flex-start }
.film-play .lbl::after { content: "View as steps"; height: 0; overflow: hidden; visibility: hidden }
.ff { --ffh: clamp(340px, 100svh - 350px, 480px); container-type: inline-size; height: var(--ffh); overflow: hidden; border: var(--frame); border-radius: var(--r-xl); background: var(--bg-1); box-shadow: var(--e-3) }
.ff * { transition: none !important }
.film code, .art code { padding: 0; border: 0; background: none }
.ff-in { display: grid; grid-template-rows: minmax(0, 11fr) minmax(0, 9fr); height: 100% }
.ff-term { display: flex; flex-direction: column; min-height: 0; border-bottom: 1px solid var(--line); background: var(--bg-sunken) }
.ff-bar { display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 16px; border-bottom: 1px solid var(--line); font: 400 12px/1 var(--font-mono); color: var(--fg-2) }
.ff-live { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--accent) }
.ff-tt, .sw { display: inline-grid; justify-items: start; min-width: 0 }
.ff-tt { flex: 1 }
.ff-tt > *, .sw > * { grid-area: 1 / 1; min-width: 0; overflow: hidden; text-overflow: ellipsis }
.ff-body, .ff-slot { position: relative; flex: 1; min-height: 0 }
.fb { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: center; padding: 12px 16px; font: 400 11.5px/1.65 var(--font-mono) }
.fl { display: flex; align-items: center; gap: 10px; min-width: 0; white-space: nowrap }
.fl b { flex: none; width: 5ch; font-weight: 400; color: var(--fg-2) }
.fl em { margin-left: auto; padding-left: 12px; font-style: normal; color: var(--fg-2) }
.fl .no { color: var(--danger) }
.fl .ok, .fl.ok { color: var(--accent) }
.fl.dim { padding-left: calc(5ch + 10px); color: var(--fg-2) }
.rx { display: inline-flex; align-items: center; gap: 8px; opacity: 1; clip-path: inset(0 calc((1 - var(--fi)) * 100%) 0 0) }
.mk { color: var(--danger) }
.scan { position: absolute; inset: 0; pointer-events: none }
.scan i { position: absolute; inset: 0; opacity: 1; transform: translateY(calc(var(--fi) * 100%)) }
.scan i::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 36px; border-bottom: 1px solid var(--accent); background: linear-gradient(transparent, var(--accent-soft)) }
.ckg { flex: none; width: 14px; height: 14px; margin-right: -2px; opacity: 1; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round }
.ckg circle { stroke: var(--accent-line); fill: var(--accent-soft) }
.ckg path { stroke: var(--accent); stroke-dasharray: 1; stroke-dashoffset: calc(1 - var(--fi)) }
.tw { position: relative; opacity: 1 }
.ty { clip-path: inset(0 calc((1 - var(--fi)) * 100%) -2px 0) }
/* Finite (WCAG 2.2.2): four blinks, then a steady caret. */
.cr { position: absolute; inset: 0; transform: translateX(calc(var(--fi) * 100%)); animation: ff-caret 1.1s steps(1) 4 }
.cr::before { content: ""; position: absolute; left: 2px; top: 15%; bottom: 15%; width: 7px; background: var(--fg-1) }
@keyframes ff-caret { 50% { opacity: 0 } }
.pop { transform: scale(calc(1 + 0.4 * (1 - var(--fi)) * (1 - var(--fi)) * (1 - var(--fi)))) }
.ff-desk { display: flex; flex-direction: column; min-height: 0; padding: 12px 16px 14px }
.ff-lab { font: 500 11px/1.3 var(--font-mono); letter-spacing: var(--tr-label); text-transform: uppercase; color: var(--fg-2) }
.ff-slot > .art { position: absolute; left: 0; right: 0; top: 50%; max-width: 380px; margin-inline: auto; translate: 0 -50% }
.ff .stamp { opacity: var(--fi); visibility: visible; transform: rotate(calc(-3deg * (1 - var(--fi)))) scale(calc(1 + 0.2 * (1 - var(--fi)) * (1 - var(--fi)) * (1 - var(--fi)))) }
.ff .ring rect { stroke-dashoffset: calc(1 - var(--fi)) }
.ff-res { position: absolute; inset: 0; display: grid; align-content: center; gap: 8px; max-width: 420px; margin-inline: auto; width: 100% }
.ff-res li { position: relative; display: grid; grid-template-columns: 12px minmax(0, 1fr) 56px 30px; align-items: center; gap: 10px; min-height: 38px; padding: 0 12px; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--bg-2); font: 400 12px/1 var(--font-mono) }
.ff-res code { overflow: hidden; text-overflow: ellipsis }
.fan { transform-origin: 0 50%; transform: translateY(calc((1 - var(--fi)) * -18px)) rotate(calc((1 - var(--fi)) * -3deg)) }
.dimout { opacity: calc(var(--fi) - 0.6 * var(--fo)) }
.ff-res .g { opacity: var(--fi) }
.ff-res .g::before { content: ""; position: absolute; inset: -1px; border: 1px solid var(--warn); border-radius: inherit; opacity: var(--fo) }
.rb { height: 4px; overflow: hidden; border-radius: 2px; background: var(--bg-3) }
.rb i { display: block; height: 100%; background: var(--accent); opacity: 1; transform-origin: 0 50%; transform: scaleX(calc(var(--v) * var(--fi))) }
.rk, .rv { color: var(--fg-2); font-variant-numeric: tabular-nums }
@container (min-width: 720px) {
  .ff-in { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); grid-template-rows: minmax(0, 1fr) }
  .ff-term { border-bottom: 0; border-right: 1px solid var(--line) }
  .fb { padding: 24px 40px; font-size: 14px; line-height: 2 }
  .ff-desk { padding: 18px 32px 28px }
  .ff .art { gap: 10px; padding: 16px 18px; border-radius: var(--r-lg) }
  .ff .art-id { font-size: 14px }
  .ff .art-t { font-size: 17px }
  .ff .art-m { font-size: 12px }
  .ff-res { gap: 10px }
  .ff-res li { grid-template-columns: 14px minmax(0, 1fr) 72px 34px; min-height: 48px; padding: 0 16px; font-size: 13px }
}
.fs-foot { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 14px 40px }
.fcap { display: grid; min-width: 0 }
.fcap > li { grid-area: 1 / 1; min-width: 0 }
.fc-t { display: flex; align-items: baseline; gap: 10px; font-size: 17px; font-weight: 500; line-height: 1.3; letter-spacing: -0.01em }
.fc-b { margin-top: 4px; max-width: 41.106em; font-size: 15px; line-height: 1.5; color: var(--fg-1) }
.fcap code, .fb-cap code { display: block; margin-top: 6px; overflow: hidden; font-size: 12px; color: var(--fg-2); white-space: nowrap; text-overflow: ellipsis }
.frail { display: grid; gap: 10px }
.fr-ch { display: flex; gap: 2px }
.fr-ch .btn { isolation: isolate; gap: 6px; padding: 0 10px; color: var(--fg-0); opacity: 1 }
/* Off chapters dim to 0.7, no lower: the 11px accent numbers keep 5.35:1 on the canvas (AA), and on
 * phones the number is the chapter's only visible label. */
.fr-ch button > * { opacity: calc(0.7 + 0.3 * (var(--fi) - var(--fo))) }
.fr-ch button::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background: var(--bg-3); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); opacity: calc(var(--fi) - var(--fo)) }
.fr-bar { display: block; height: 2px; overflow: hidden; border-radius: 1px; background: var(--line) }
.fr-bar i { display: block; height: 100%; background: var(--accent); transform-origin: 0 50%; transform: scaleX(var(--fp)) }
/* Off screen: the caret waits. */
.film.is-off .cr { animation-play-state: paused }
/* The storyboard (app.js): reduced motion, a short screen, or "View as steps". */
.film-board { display: none; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px var(--grid-gap); margin-top: 32px }
.film .film-board * { animation: none }
.film-board .ff { --ffh: 360px }
.fb-cap { margin-top: 14px }
.film.is-steps .film-track { height: auto }
.film.is-steps .film-stage { position: relative; height: auto; padding: 32px 0 0 }
.film.is-steps .film-stage > :not(.fs-top) { display: none }
.film.is-steps .film-board { display: grid }
/* With scroll timelines every .fx runs native-property keyframes over its two ranges of the film's
 * timeline: no registered property re-cascades through every subtree on every frame, only the parts
 * inside their range restyle, and transforms, scales and single fades run on the compositor. An in
 * and its out write the same properties, stacked: the in fills both ways, the out fills forwards only,
 * so it takes over once its range begins (every in ends before its out begins). That measured faster
 * than an out on filter: opacity(), whose extra layers cost more than they save. At every point of
 * its range each keyframe equals the --fi/--fo rule above (the cubic settle is the bezier
 * (1/3, 1, 2/3, 1), exactly 1 - (1 - t)^3). */
@keyframes fx-in { from { opacity: 0 } to { opacity: 1 } }
@keyframes fx-out { from { opacity: 1 } to { opacity: 0 } }
@keyframes fx-in-up { from { opacity: 0; translate: 0 10px } to { opacity: 1; translate: none } }
@keyframes fx-out-up { from { opacity: 1; translate: none } to { opacity: 0; translate: 0 -6px } }
@keyframes fx-in-art { from { opacity: 0; translate: 0 calc(10px - 50%) } to { opacity: 1; translate: 0 -50% } }
@keyframes fx-out-art { from { opacity: 1; translate: 0 -50% } to { opacity: 0; translate: 0 calc(-50% - 6px) } }
@keyframes fx-dim { from { opacity: 1 } to { opacity: 0.4 } }
@keyframes fx-ch-in { from { opacity: 0.7 } to { opacity: 1 } }
@keyframes fx-ch-out { from { opacity: 1 } to { opacity: 0.7 } }
@keyframes fx-wipe { from { clip-path: inset(0 100% 0 0) } to { clip-path: inset(0 0 0 0) } }
@keyframes fx-type { from { clip-path: inset(0 100% -2px 0) } to { clip-path: inset(0 0 -2px 0) } }
@keyframes fx-x { from { transform: none } to { transform: translateX(100%) } }
@keyframes fx-y { from { transform: none } to { transform: translateY(100%) } }
@keyframes fx-draw { from { stroke-dashoffset: 1 } to { stroke-dashoffset: 0 } }
@keyframes fx-bar { from { transform: scaleX(0) } to { transform: scaleX(var(--v)) } }
@keyframes fx-fan { from { opacity: 0; transform: translateY(-18px) rotate(-3deg) } to { opacity: 1; transform: none } }
@keyframes fx-stamp { from { opacity: 0; rotate: -3deg } to { opacity: 1; rotate: none } }
@keyframes fx-stamp-s { from { scale: 1.2 } to { scale: none } }
@keyframes fx-pop { from { transform: scale(1.4) } to { transform: none } }
@supports (animation-timeline: view()) {
  /* Not on phones, which get the storyboard (below); without JS there, the still frame. */
  @media (prefers-reduced-motion: no-preference) and (not ((pointer: coarse) and (max-width: 767px))) {
    .film-track { view-timeline: --film block; view-timeline-inset: 0px }
    /* --fx-in/--fx-out are this .fx's two ranges; its parts without .fx inherit them. */
    .film-stage .fx {
      --fx-in: contain calc(var(--fa, -1) * 100%) contain calc((var(--fa, -1) + var(--fd, 0.03)) * 100%);
      --fx-out: contain calc(var(--fb, 2) * 100%) contain calc((var(--fb, 2) + var(--fe, 0.03)) * 100%);
      animation-name: fx-in, fx-out;
      animation-timing-function: linear;
      animation-fill-mode: both, forwards;
      animation-timeline: --film;
      animation-range: var(--fx-in), var(--fx-out);
    }
    .film-stage .fx.up { transform: none; animation-name: fx-in-up, fx-out-up }
    .film-stage .ff-slot > .art.fx { animation-name: fx-in-art, fx-out-art }
    /* Owners whose parts move, not the owner itself. */
    .film-stage :is(.tw, .ckg, .fr-ch .btn).fx { animation-name: none }
    .film-stage .rx.fx { animation-name: fx-wipe }
    .film-stage .scan i.fx { animation-name: fx-y }
    .film-stage .rb i.fx { animation-name: fx-bar }
    .film-stage .fan.fx { animation-name: fx-fan }
    .film-stage .fan.dimout.fx { animation-name: fx-fan, fx-dim }
    /* Two ins: opacity (linear) and a cubic settle. */
    .film-stage .ff .stamp.fx { transform: none; animation-name: fx-stamp, fx-stamp-s }
    .film-stage .pop.fx { animation-name: fx-in, fx-pop }
    .film-stage :is(.ff .stamp, .pop).fx { animation-timing-function: linear, cubic-bezier(0.3333333, 1, 0.6666667, 1); animation-fill-mode: both; animation-range: var(--fx-in), var(--fx-in) }
    .film-stage :is(.ty, .ckg path, .ring rect, .fr-ch .btn > *), .film-stage .ff-res .g::before, .film-stage .fr-ch .btn::before { animation-timing-function: linear; animation-fill-mode: both, forwards; animation-timeline: --film; animation-range: var(--fx-in), var(--fx-out) }
    .film-stage .ty { animation-name: fx-type }
    .film-stage :is(.ckg path, .ring rect) { animation-name: fx-draw }
    .film-stage .ff-res .g::before { animation-name: fx-in; animation-range: var(--fx-out) }
    .film-stage .fr-ch .btn::before { animation-name: fx-in, fx-out }
    .film-stage .fr-ch .btn > * { animation-name: fx-ch-in, fx-ch-out }
    /* The caret blinks on the clock and rides the typing on the film. */
    .film-stage .cr { animation-name: ff-caret, fx-x; animation-duration: 1.1s, auto; animation-timing-function: steps(1), linear; animation-iteration-count: 4, 1; animation-fill-mode: none, both; animation-timeline: auto, --film; animation-range: normal, var(--fx-in) }
    .film.is-off .film-stage .cr { animation-play-state: paused, running }
    .fr-bar i { animation: fr-p linear both; animation-timeline: --film; animation-range: contain 0% contain 100% }
  }
}
@supports not (animation-timeline: view()) {
  @media (scripting: none) {
    .film-track { height: auto }
    .film-stage { position: relative; height: auto; padding-top: 32px }
  }
}
@media (scripting: none) {
  .film-play, .frail { display: none }
}
/* Reduced motion, a short screen or a phone (app.js filmBoardQuery): always the storyboard, so no
 * toggle. Phones get it because the scrubbed film (some 60 animated parts) misses their frame budget. */
@media (prefers-reduced-motion: reduce), (max-height: 560px), (pointer: coarse) and (max-width: 767px) {
  .film-track { height: auto }
  .film-stage { position: relative; height: auto; padding: 32px 0 0 }
  .film-play, .frail { display: none }
  .film.has-board .film-stage > :not(.fs-top) { display: none }
  .film-board { display: grid }
}

/* ---- privacy modes (pure CSS), seen from above. World units (--u): the stage is 520 wide; things sit
 * at --x/--y/--z. Billboards (.mv-bb) turn back by the world's angles, so text faces the reader. The
 * envelope's x/z and its height ride separate transitions: it hops up onto the cloud pad and drops back
 * the same way. */
.mode-viz { grid-area: 2 / 2; align-self: start; width: 100%; max-width: 520px }
.mv-l { display: grid; grid-template-columns: 270fr 250fr; font: 500 11px/1.3 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-2) }
.mv-l > :last-child { padding-left: 12px }
.mv-stage { position: relative; margin-top: 8px; aspect-ratio: 520 / 172; container-type: inline-size; perspective: 1200px }
.mv-w { --u: calc(100cqw / 520); position: absolute; inset: 0; transform-style: preserve-3d; transform: translateY(calc(-26 * var(--u))) rotateX(-38deg) rotateY(14deg) }
.mv-w > * { position: absolute; left: 0; top: 0; transform-style: preserve-3d; transform-origin: 0 0 }
.mv-w .mv-bb { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
.mv-p { width: calc(var(--w) * var(--u)); height: calc(var(--d) * var(--u)); border: 1px solid var(--line-strong); border-radius: 10px; background: radial-gradient(circle, var(--tex-dot) 0.8px, transparent 1.2px) 0 0 / 8px 8px, var(--bg-2); transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateX(90deg) }
.mv-p::after { content: ""; position: absolute; left: -1px; right: -1px; top: 100%; height: calc(10 * var(--u)); border-radius: 0 0 6px 6px; background: linear-gradient(rgba(255, 255, 255, 0.16), transparent 1px), #141417; transform-origin: 0 0; transform: rotateX(-90deg) }
/* Your own pad rises into place only for Customer VPC. */
.mv-vpc { border-color: var(--accent-line); opacity: 0; transform: translate3d(calc(var(--x) * var(--u)), calc((var(--y) + 26) * var(--u)), calc(var(--z) * var(--u))) rotateX(90deg); transition: transform var(--d-spring) var(--spring), opacity var(--d-base) ease }
.security:has([value=vpc]:checked) .mv-vpc { opacity: 1; transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateX(90deg) }
.mv-b { width: calc(108 * var(--u)); height: calc(78 * var(--u)); border: 1px dashed var(--fg-3); border-radius: 4px; background: linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.01)); transform: translate3d(calc(272 * var(--u)), calc(48 * var(--u)), calc(-54 * var(--u))) rotateY(-90deg) }
.mv-env { --x: 30; --y: 120; --z: 22; transform: translate3d(calc(var(--x) * var(--u)), 0, calc(var(--z) * var(--u))); transition: transform var(--d-spring) var(--ease-in-out) }
.mv-ez { position: absolute; transform-style: preserve-3d; transform: translateY(calc(var(--y) * var(--u))); transition: transform var(--d-spring) cubic-bezier(0.6, 0, 0.8, 0.3) }
/* One plane: the flap and lock are drawn inside the body (separate quads this near the floor sort badly). */
.mv-body { position: absolute; left: 0; top: calc(-31 * var(--u)); width: calc(46 * var(--u)); height: calc(30 * var(--u)); border: 1px solid var(--fg-1); border-radius: 3px; background: var(--bg-2); box-shadow: 0 8px 14px -6px rgba(0, 0, 0, 0.7) }
.mv-body > svg { position: absolute; left: -1px; top: -1px; width: calc(46 * var(--u)); overflow: visible }
.mv-flap { height: calc(20 * var(--u)); fill: var(--bg-2); stroke: var(--fg-1); stroke-width: 1.4; stroke-linejoin: round; transform-origin: 50% 0; transition: transform var(--d-spring) var(--spring) }
.mv-body .mv-lock { left: calc(37 * var(--u)); top: calc(-14 * var(--u)); width: calc(13 * var(--u)); height: calc(17 * var(--u)); fill: var(--info); opacity: 0; transform: scale(0.6); transition: opacity 120ms ease, transform var(--d-spring) var(--spring) }
.mv-sh { fill: none; stroke: var(--info); stroke-width: 1.5; transform: translateY(-3px); transition: transform var(--d-base) var(--ease-out) }
.mv-k i { position: absolute; width: calc(26 * var(--u)); height: calc(9 * var(--u)); border: 1px solid var(--info); border-radius: 2px; background: #15212b; opacity: 0; transform: translate3d(calc(40 * var(--u)), calc(106 * var(--u)), calc(22 * var(--u))); transition: transform var(--d-spring) var(--spring), opacity var(--d-fast) ease }
.security:has([value=relay]:checked) .mv-lock { opacity: 1; transform: none }
.security:has([value=relay]:checked) .mv-sh { transform: none }
.security:has([value=relay]:checked) .mv-env { --x: 362; --y: 98; --z: -28 }
.security:has([value=vpc]:checked) .mv-env { --x: 362; --z: 26 }
.security:has(:is([value=relay], [value=vpc]):checked) .mv-ez { transition-timing-function: cubic-bezier(0.2, 0.9, 0.3, 1) }
.security:has([value=relay]:checked) :is(.mv-env, .mv-ez) { transition-delay: 0.22s }
.security:has([value=sync]:checked) .mv-flap { transform: rotateX(-165deg) }
.security:has([value=sync]:checked) .mv-k i { opacity: 1; transform: translate3d(calc(372 * var(--u)), calc((89 - var(--k) * 10) * var(--u)), calc(-28 * var(--u))); transition-delay: calc(0.12s + var(--k) * 0.08s) }
/* C1, the only default cloud payload, hops across in every mode and rests on the cloud pad. Each mode
 * has its own keyframes name, so choosing a mode replays the crossing. */
.mv-c1 { --x: 294; --y: 98; --z: -28; --c1: translate3d(calc(40 * var(--u)), calc(120 * var(--u)), calc(-28 * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%); --c1m: translate3d(calc(170 * var(--u)), calc(58 * var(--u)), calc(-28 * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
.mv-c1 .tag { height: 20px; padding: 0 6px; background: var(--bg-2) }
.is-in .mv-c1 { animation: mv-c1-a 1100ms var(--ease-in-out) both }
.security:has([value=relay]:checked) .is-in .mv-c1 { animation-name: mv-c1-b }
.security:has([value=sync]:checked) .is-in .mv-c1 { animation-name: mv-c1-c }
.security:has([value=vpc]:checked) .is-in .mv-c1 { animation-name: mv-c1-d }
@keyframes mv-c1-a { 0% { opacity: 0 } 0%, 15% { transform: var(--c1) } 55% { transform: var(--c1m) } }
@keyframes mv-c1-b { 0% { opacity: 0 } 0%, 15% { transform: var(--c1) } 55% { transform: var(--c1m) } }
@keyframes mv-c1-c { 0% { opacity: 0 } 0%, 15% { transform: var(--c1) } 55% { transform: var(--c1m) } }
@keyframes mv-c1-d { 0% { opacity: 0 } 0%, 15% { transform: var(--c1) } 55% { transform: var(--c1m) } }
/* ---- E10 data-flow chips: five classification chips queue on the machine pad (tags sized in cqw,
 * so the queue holds its formation at every width). On first view and each mode change they run
 * once, 1.6s each, staggered from 700ms so the C1 hop lands first: C0/C1 cross to the cloud pad,
 * C2 crosses only with consent (Relay/Sync/VPC), C3 rebounds off the glass, C4 redacts before the
 * glass. Out and back with a hold at the far end, so every run starts clean. Like mv-c1, each
 * path ships one name per mode (a/b/c/d): only a name change revives a finished run (neither
 * var-fed nor literal delay tweaks do). Geometry lives here, not in style attributes, so the VPC
 * destination wins. Without scripting or motion the chips rest queued. */
.mv-chip-c0 { --x: 55; --y: 150; --z: -28; --dx: 345; --dy: 100; --dz: -28 }
.mv-chip-c1 { --x: 115; --y: 150; --z: -28; --dx: 420; --dy: 120; --dz: -28 }
.mv-chip-c2 { --x: 175; --y: 150; --z: -28; --dx: 382; --dy: 108; --dz: -28 }
.mv-chip-c3 { --x: 85; --y: 182; --z: -28; --dx: 215; --dy: 150; --dz: -28 }
.mv-chip-c4 { --x: 145; --y: 182; --z: -28; --dx: 95; --dy: 205; --dz: -28 }
.mv-chip { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
.mv-chip .tag { font-size: max(9px, 2.115cqw); height: max(18px, 4.23cqw); padding: 0 max(5px, 1.35cqw); gap: max(4px, 1.15cqw) }
.mv-chip .tag::before { width: max(5px, 1.35cqw); height: max(5px, 1.35cqw) }
.c4-stack { display: grid; font: 500 max(9px, 2.115cqw)/1.4 var(--font-mono); letter-spacing: 0.06em }
.c4-stack > * { grid-area: 1 / 1 }
.c4-stack .mk { white-space: nowrap; opacity: 0 }
@keyframes mv-chip-cross-a {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  40%, 60% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-cross-b {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  40%, 60% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-cross-c {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  40%, 60% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-cross-d {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  40%, 60% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-bounce-a {
  0%, 62%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  45% { transform: translate3d(calc((var(--x) - 10) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-bounce-b {
  0%, 62%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  45% { transform: translate3d(calc((var(--x) - 10) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-bounce-c {
  0%, 62%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  45% { transform: translate3d(calc((var(--x) - 10) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-bounce-d {
  0%, 62%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  45% { transform: translate3d(calc((var(--x) - 10) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-c4-a {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30%, 70% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-c4-b {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30%, 70% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-c4-c {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30%, 70% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-chip-c4-d {
  0%, 100% { transform: translate3d(calc(var(--x) * var(--u)), calc(var(--y) * var(--u)), calc(var(--z) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
  30%, 70% { transform: translate3d(calc(var(--dx) * var(--u)), calc(var(--dy) * var(--u)), calc(var(--dz) * var(--u))) rotateY(-14deg) rotateX(38deg) translateY(-100%) }
}
@keyframes mv-c4-tag-a { 0%, 15% { opacity: 1 } 30%, 70% { opacity: 0 } 85%, 100% { opacity: 1 } }
@keyframes mv-c4-tag-b { 0%, 15% { opacity: 1 } 30%, 70% { opacity: 0 } 85%, 100% { opacity: 1 } }
@keyframes mv-c4-tag-c { 0%, 15% { opacity: 1 } 30%, 70% { opacity: 0 } 85%, 100% { opacity: 1 } }
@keyframes mv-c4-tag-d { 0%, 15% { opacity: 1 } 30%, 70% { opacity: 0 } 85%, 100% { opacity: 1 } }
@keyframes mv-c4-mk-a { 0%, 15% { opacity: 0 } 30%, 70% { opacity: 1 } 85%, 100% { opacity: 0 } }
@keyframes mv-c4-mk-b { 0%, 15% { opacity: 0 } 30%, 70% { opacity: 1 } 85%, 100% { opacity: 0 } }
@keyframes mv-c4-mk-c { 0%, 15% { opacity: 0 } 30%, 70% { opacity: 1 } 85%, 100% { opacity: 0 } }
@keyframes mv-c4-mk-d { 0%, 15% { opacity: 0 } 30%, 70% { opacity: 1 } 85%, 100% { opacity: 0 } }
@media (prefers-reduced-motion: no-preference) {
  .security:has(input[value="local"]:checked) .boundary.is-in .mv-chip-c0 { animation: mv-chip-cross-a 1.6s linear 700ms both }
  .security:has(input[value="relay"]:checked) .boundary.is-in .mv-chip-c0 { animation: mv-chip-cross-b 1.6s linear 700ms both }
  .security:has(input[value="sync"]:checked) .boundary.is-in .mv-chip-c0 { animation: mv-chip-cross-c 1.6s linear 700ms both }
  .security:has(input[value="vpc"]:checked) .boundary.is-in .mv-chip-c0 { animation: mv-chip-cross-d 1.6s linear 700ms both }
  .security:has(input[value="local"]:checked) .boundary.is-in .mv-chip-c1 { animation: mv-chip-cross-a 1.6s linear 850ms both }
  .security:has(input[value="relay"]:checked) .boundary.is-in .mv-chip-c1 { animation: mv-chip-cross-b 1.6s linear 850ms both }
  .security:has(input[value="sync"]:checked) .boundary.is-in .mv-chip-c1 { animation: mv-chip-cross-c 1.6s linear 850ms both }
  .security:has(input[value="vpc"]:checked) .boundary.is-in .mv-chip-c1 { animation: mv-chip-cross-d 1.6s linear 850ms both }
  /* C2 only travels with consent (in Local-only it rests queued); to your pad in VPC. */
  .security:has(input[value="relay"]:checked) .boundary.is-in .mv-chip-c2 { animation: mv-chip-cross-b 1.6s linear 1000ms both }
  .security:has(input[value="sync"]:checked) .boundary.is-in .mv-chip-c2 { animation: mv-chip-cross-c 1.6s linear 1000ms both }
  .security:has(input[value="vpc"]:checked) .boundary.is-in .mv-chip-c2 { animation: mv-chip-cross-d 1.6s linear 1000ms both }
  .security:has(input[value="vpc"]:checked) .mv-chip-c2 { --dx: 395; --dy: 140; --dz: 30 }
  .security:has(input[value="local"]:checked) .boundary.is-in .mv-chip-c3 { animation: mv-chip-bounce-a 1.6s linear 1150ms both }
  .security:has(input[value="relay"]:checked) .boundary.is-in .mv-chip-c3 { animation: mv-chip-bounce-b 1.6s linear 1150ms both }
  .security:has(input[value="sync"]:checked) .boundary.is-in .mv-chip-c3 { animation: mv-chip-bounce-c 1.6s linear 1150ms both }
  .security:has(input[value="vpc"]:checked) .boundary.is-in .mv-chip-c3 { animation: mv-chip-bounce-d 1.6s linear 1150ms both }
  .security:has(input[value="local"]:checked) .boundary.is-in .mv-chip-c4 { animation: mv-chip-c4-a 1.6s linear 1300ms both }
  .security:has(input[value="relay"]:checked) .boundary.is-in .mv-chip-c4 { animation: mv-chip-c4-b 1.6s linear 1300ms both }
  .security:has(input[value="sync"]:checked) .boundary.is-in .mv-chip-c4 { animation: mv-chip-c4-c 1.6s linear 1300ms both }
  .security:has(input[value="vpc"]:checked) .boundary.is-in .mv-chip-c4 { animation: mv-chip-c4-d 1.6s linear 1300ms both }
  .security:has(input[value="local"]:checked) .boundary.is-in .mv-chip-c4 .tag { animation: mv-c4-tag-a 1.6s linear 1300ms both }
  .security:has(input[value="relay"]:checked) .boundary.is-in .mv-chip-c4 .tag { animation: mv-c4-tag-b 1.6s linear 1300ms both }
  .security:has(input[value="sync"]:checked) .boundary.is-in .mv-chip-c4 .tag { animation: mv-c4-tag-c 1.6s linear 1300ms both }
  .security:has(input[value="vpc"]:checked) .boundary.is-in .mv-chip-c4 .tag { animation: mv-c4-tag-d 1.6s linear 1300ms both }
  .security:has(input[value="local"]:checked) .boundary.is-in .mv-chip-c4 .mk { animation: mv-c4-mk-a 1.6s linear 1300ms both }
  .security:has(input[value="relay"]:checked) .boundary.is-in .mv-chip-c4 .mk { animation: mv-c4-mk-b 1.6s linear 1300ms both }
  .security:has(input[value="sync"]:checked) .boundary.is-in .mv-chip-c4 .mk { animation: mv-c4-mk-c 1.6s linear 1300ms both }
  .security:has(input[value="vpc"]:checked) .boundary.is-in .mv-chip-c4 .mk { animation: mv-c4-mk-d 1.6s linear 1300ms both }
}
/* Each pad's name, at its far end. */
.mv-cap { --x: 422; --y: 97; --z: -28; font: 500 11px/14px var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; color: var(--fg-2) }
.mv-cv { --y: 119; --z: 26; color: var(--accent); opacity: 0; transition: opacity 100ms ease }
.security:has([value=vpc]:checked) .mv-cv { opacity: 1; transition: opacity var(--d-base) ease 160ms }
@container (max-width: 400px) { .mv-cap .mv-long { display: none } }

/* ---- Free meter fill (app.js): each cell lights on its own delay (--cd), in step with the reel */
.meter i { transition: opacity var(--d-base) ease, transform var(--d-spring) var(--spring) }
.meter.is-armed i { transition-delay: var(--cd, 0ms) }
.meter.is-armed i:not(.on) { opacity: 0.2; transform: scaleY(0.5) }

/* ---- Spec numbers: an odometer over the real number (app.js). The real text keeps its box and
 * baseline (and stays what assistive tech, find and copy read); the reel is aria-hidden pseudo text
 * clipped to that one line. Each column is two turns of 0-9 (5% = one line): it waits a line below
 * the window, spins one full turn and lands on its digit. Translate only, once, about 1s. */
.n.is-armed > .n-real { opacity: 0 }
.odo { position: absolute; inset: 0; overflow: hidden; text-align: right; white-space: nowrap; pointer-events: none; -webkit-user-select: none; user-select: none; -webkit-mask: linear-gradient(transparent, #000 14%, #000 86%, transparent); mask: linear-gradient(transparent, #000 14%, #000 86%, transparent) }
/* Inline-level columns: a copied selection reads "10,000 cloud requests", with no line breaks. */
.odo-d, .odo-s { display: inline-block; vertical-align: top }
.odo-d::before { content: "0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9\A 0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9"; display: block; white-space: pre }
.odo-s::before { content: attr(data-c) }
.odo-d { translate: 0 5%; transition: translate 900ms var(--spring) calc(var(--k, 0) * 40ms) }
.n.is-rolling .odo-d { translate: 0 calc((10 + var(--d, 0)) * -5%) }
.odo-s { opacity: 0; transition: opacity var(--d-base) ease 240ms }
.n.is-rolling .odo-s { opacity: 1 }

@media (pointer: coarse) {
  .fr-ch .btn { min-width: 44px }
}
@media (max-width: 1023px) {
  .fs-foot { grid-template-columns: minmax(0, 1fr) }
  .fr-ch { justify-content: space-between }
  .mode-viz { grid-area: auto }
  .film-board { grid-template-columns: repeat(2, minmax(0, 1fr)) }
}
@media (max-width: 767px) {
  /* Phones, 2.5D: a stacked deck (the Wallet gesture) that deals down into a sunken tray. */
  .run-art { grid-template-columns: minmax(0, 1fr); padding-bottom: 36px; transform: none }
  .run-art::before { top: -8px; bottom: 0; left: -8px; right: -8px; height: auto; border-radius: 18px; background: var(--bg-sunken); box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.6); transform: none }
  .run-art::after { top: auto; bottom: 0; left: 0; right: 0; height: 36px; align-items: center; padding: 0 6px; border: 0; background: none; box-shadow: none; transform: none }
  .run-art .art:nth-child(1) { z-index: 3 }
  .run-art .art:nth-child(2) { z-index: 2 }
  .run-art .art[data-pose="deck"] { transform: translateY(calc(var(--i) * -100%)) scale(calc(1 - var(--i) * 0.05)) }
  .run-art .art[data-pose="lift"] { transform: scale(1.03) }
  .film-stage { gap: 12px; padding-bottom: 12px }
  .ff { --ffh: clamp(330px, 100svh - 360px, 520px); border-radius: var(--r-lg) }
  .fc-t { font-size: 16px }
  .fcap code { display: none }
  .fr-ch .nm { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap }
  .film-board { grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: 86%; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none }
  .film-board > li { scroll-snap-align: start }
}
