/* Cineview homepage (/): its acts, on the runtime (/v2/). */

  :root {
    /* Cineview 1.0 homepage palette. Act 1: two staggered headline rows, each completed to the page
       edge by a hairline; Act 2: a scroll zone where the reader's position is the drawing's time. */
    --paper: #fffaf3;
    --paper-deep: #f1dfc9;
    --ink: #352820;
    --ink-soft: #695143;
    --clay: #994f36;
    --clay-deep: #83412c;
    --brass: #b99870;
    --brass-soft: #dbc4a7;
    /* Faint lines on the warm grounds (the sketch, the hairlines); #dbc4a7 fades out on them. */
    --line-soft: #caae8c;
    /* One ground per act (lexicon #41): the scene at eye level names it on the stage. */
    --ground-open: #fbefdd;
    --font-zh: "Noto Serif SC", "Songti SC", "STSong", "SimSun", serif;
    --font-latin: "Fraunces", "Noto Serif SC", Georgia, serif;
    --font-ui: "Inter", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", system-ui, sans-serif;
    --font-code: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    /* Every control shares this radius: half-circle ends now; a value near 12px gives soft rectangles. */
    --radius-control: 999px;
    --u: calc(min(100vw, 177.78vh) / 1920);
    color-scheme: light;
  }

  html,
  body {
    margin: 0;
  }

  /* The current act's ground and the surfaces made from it. A registered colour transitions, so a
     scene change recolours the page in one move (lexicon #41: about .7 s); every surface reads the
     same variable, so none of them changes ahead of the ground. */
  @property --ground {
    syntax: "<color>";
    inherits: true;
    initial-value: #fbefdd;
  }

  @property --ink {
    syntax: "<color>";
    inherits: true;
    initial-value: #352820;
  }

  @property --ink-soft {
    syntax: "<color>";
    inherits: true;
    initial-value: #695143;
  }

  @property --clay {
    syntax: "<color>";
    inherits: true;
    initial-value: #994f36;
  }

  @property --line-soft {
    syntax: "<color>";
    inherits: true;
    initial-value: #caae8c;
  }

  :root {
    --ground: var(--scroll-ground, var(--ground-open));
    --surface: color-mix(in srgb, var(--ground) 40%, var(--paper));
    --surface-deep: color-mix(in srgb, var(--ground), var(--brass) 25%);
    transition:
      --ground 0.7s ease,
      --ink 0.7s ease,
      --ink-soft 0.7s ease,
      --clay 0.7s ease,
      --line-soft 0.7s ease;
  }

  /* Light acts (owner 10-26, option A): no colour per act; the ground follows the scroll, gliding
     from cream to apricot and back over the page (hero.js writes --scroll-ground). Only the cinema
     is its own colour, the dark of the room. The same dark
     ink throughout; Act 9 is the dark of the cinema. The tokens transition with the ground. */
  :root:has(cv-stage[data-cv-theme="time"]),
  :root[data-boot-theme="time"] { --ink: #352820; --ink-soft: #5f4739; --clay: #994f36; --line-soft: #caae8c; }
  :root:has(cv-stage[data-cv-theme="pair"]),
  :root[data-boot-theme="pair"] { --ground: #f9ded2; --ink: #352820; --ink-soft: #5f4739; --clay: #994f36; --line-soft: #caae8c; }
  :root:has(cv-stage[data-cv-theme="method"]),
  :root[data-boot-theme="method"] { --ink: #352820; --ink-soft: #5f4739; --clay: #994f36; --line-soft: #caae8c; }
  :root:has(cv-stage[data-cv-theme="stack"]),
  :root[data-boot-theme="stack"] { --ink: #352820; --ink-soft: #5f4739; --clay: #994f36; --line-soft: #caae8c; }
  :root:has(cv-stage[data-cv-theme="material"]),
  :root[data-boot-theme="material"] { --ink: #352820; --ink-soft: #5f4739; --clay: #994f36; --line-soft: #caae8c; }
  :root:has(cv-stage[data-cv-theme="type"]),
  :root[data-boot-theme="type"] { --ground: #f9e9d3; --ink: #352820; --ink-soft: #5f4739; --clay: #994f36; --line-soft: #caae8c; }
  :root:has(cv-stage[data-cv-theme="extend"]),
  :root[data-boot-theme="extend"] { --ink: #352820; --ink-soft: #5f4739; --clay: #994f36; --line-soft: #caae8c; }
  :root:has(cv-stage[data-cv-theme="night"]),
  :root[data-boot-theme="night"] { --ground: #15100d; --ink: #f3e6d6; --ink-soft: #bfae9c; --clay: #e8a07a; --line-soft: #3a2e26; --surface: color-mix(in srgb, var(--ground), #fff 7%); --surface-deep: color-mix(in srgb, var(--ground), #fff 14%); }

  /* The bar goes clear only once the cinema act has reached it (hero.js: .cinema-under-bar), so the
     act before never scrolls its text through the bar's labels. A clear bar does not catch clicks
     meant for the phone under it: only its own links and buttons take the pointer. */
  :root.cinema-under-bar .bar,
  :root.cinema-under-bar.is-scrolled .bar {
    background: transparent;
    box-shadow: none;
    pointer-events: none;
  }

  :root.cinema-under-bar .bar :is(a, button) {
    pointer-events: auto;
  }

  /* Restoring after a reload: the act's colours at once (no transition), the content hidden until
     the page is back in place, then a short fade in. */
  :root.restoring {
    transition: none;
    background: var(--ground);
  }

  :root.restoring body > * {
    opacity: 0;
  }

  body > * {
    transition: opacity 0.28s ease;
  }

  .bar .brand,
  .bar .nav a,
  .bar .langs button {
    transition: color 0.7s ease;
  }

  @media (prefers-reduced-motion: reduce) {
    :root {
      transition: none;
    }
  }

  body {
    background: var(--ground);
    color: var(--ink);
    font-family: var(--font-ui);
  }

  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  :focus-visible {
    outline: 2px solid var(--clay);
    outline-offset: 4px;
  }

  .sr {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* ---------- Hero ---------- */

  /* The bridge between Act 1 and Act 2: the reel's centre sits on the boundary (the bridge's top
     edge), so its upper half shows under the opening and its lower half here, cut only by the
     screen's right edge. Not a held scene: as tall as it needs. */
  .act-bridge {
    position: relative;
    z-index: 0;
    background: var(--ground);
  }

  /* The opening stays above the bridge, so the reel's upper half sits under its text and buttons. */
  .act-open {
    position: relative;
    z-index: 1;
  }

  :root:has(.act-open) body {
    background: var(--ground);
  }

  .bridge {
    --m: calc(120 * var(--u));
    position: relative;
    display: grid;
    align-items: center;
    min-height: calc(560 * var(--u));
    padding: calc(80 * var(--u)) var(--m);
    overflow-x: clip;
  }

  .reel {
    position: absolute;
    z-index: 0;
    top: calc(-540 * var(--u));
    right: calc(-300 * var(--u));
    width: calc(1080 * var(--u));
    height: calc(1080 * var(--u));
    pointer-events: none;
  }

  .reel svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
  }

  /* Drawn once with the headline (is-drawn), then the colours, then it turns while on screen. */
  .reel-spin {
    transform-origin: 50% 50%;
    animation: reel-turn 20s linear infinite;
    animation-play-state: paused;
  }

  .reel.in-view.is-drawn .reel-spin {
    animation-play-state: running;
  }

  @keyframes reel-turn {
    to { rotate: 360deg; }
  }

  .reel-lines * {
    stroke-dasharray: 1 2;
    stroke-dashoffset: 1.001;
  }

  .reel.is-drawn .reel-lines * {
    stroke-dashoffset: 0;
    transition: stroke-dashoffset 1.6s linear;
  }

  .reel .reel-fills {
    opacity: 0;
  }

  .reel.is-drawn .reel-fills {
    opacity: 1;
    transition: opacity 0.64s linear 0.96s;
  }

  :root:not(.cv-js) .reel .reel-lines *,
  .is-static .reel .reel-lines * {
    stroke-dashoffset: 0;
  }

  :root:not(.cv-js) .reel .reel-fills,
  .is-static .reel .reel-fills {
    opacity: 1;
  }

  .reel-flange { fill: color-mix(in srgb, var(--ground), var(--brass) 14%); }
  .reel-rim { fill: color-mix(in srgb, var(--ground), var(--brass) 30%); }
  .reel-film { fill: color-mix(in srgb, var(--ground), var(--clay) 18%); }
  .reel-hub { fill: color-mix(in srgb, var(--ground), var(--brass) 24%); }
  .reel-bolt,
  .reel-spindle { fill: var(--clay); }
  .reel-hole { fill: var(--ground); }

  .reel-lines {
    fill: none;
    stroke: var(--brass);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .reel-ticks {
    stroke-width: 3;
  }

  .reel-wound {
    stroke: color-mix(in srgb, var(--ground), var(--clay) 34%);
    stroke-width: 1.4;
  }

  .stage {
    z-index: 1;
  }

  .bar {
    --m: calc(120 * var(--u));
  }

  /* The opening is one screen plus the reel's tail: the reel runs past the first screen and shows
     as the page scrolls down; only the screen's right edge cuts it. The stage stays on the first
     screen. */
  .hero {
    --m: calc(120 * var(--u));
    --gap: 0.22em;
    --part: 0;
    --part-dist: calc(154 * var(--u));
    --ext-y: 0em;
    position: relative;
    height: 100vh;
    height: 100svh;
    min-height: 600px;
    overflow: visible;
  }

  /* Fixed over every act. Once the page has scrolled it sits on a paper band, so headings that
     scroll under it never cross the navigation. */
  .bar {
    position: fixed;
    z-index: 10;
    top: 0;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    gap: calc(48 * var(--u));
    padding-block: calc(48 * var(--u)) calc(20 * var(--u));
    padding-inline: max(var(--m), calc((100% - 1920px) / 2 + var(--m)));
    /* No colour transition of its own: the ground's (--ground) moves the band with the page. */
    transition:
      box-shadow 0.3s ease,
      padding 0.3s ease;
  }

  :root.is-scrolled .bar {
    padding-block: calc(24 * var(--u));
    background: var(--ground);
    box-shadow: 0 1px 0 var(--line-soft);
  }

  .brand {
    margin-right: auto;
    color: var(--ink);
    font-family: var(--font-latin);
    font-size: max(20px, calc(34 * var(--u)));
    font-weight: 500;
    letter-spacing: -0.012em;
    line-height: 1;
    text-decoration: none;
  }

  .nav {
    display: flex;
    gap: calc(44 * var(--u));
  }

  .nav a,
  .langs button {
    padding-block: 12px;
    color: var(--ink);
    font: 500 max(14px, calc(17 * var(--u)))/1 var(--font-ui);
    text-decoration: none;
  }

  .nav a:hover {
    color: var(--clay);
  }

  /* Controls are the only rounded shapes, all with half-circle ends. */
  .langs {
    display: flex;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-control);
  }

  .langs button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    height: calc(max(36px, 44 * var(--u)) - 8px);
    padding: 0 calc(16 * var(--u));
    border: 0;
    border-radius: calc(var(--radius-control) - 3px);
    background: transparent;
    color: var(--ink-soft);
    cursor: pointer;
  }

  .langs button:hover {
    color: var(--ink);
  }

  .langs button[aria-pressed="true"] {
    background: var(--surface-deep);
    color: var(--ink);
  }

  /* The block sits centred between the header and the bottom margin, whatever the headline's size. */
  .stage {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(120 * var(--u));
    bottom: calc(72 * var(--u));
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  /* The framework's name as a title card: the name in clay, set in the display serif, with what it
     is beside it; a short clay rule under the pair. No frame, so it reads as a title, not a chip. */
  .kicker {
    display: grid;
    justify-items: center;
    gap: calc(14 * var(--u));
    margin: 0 auto calc(44 * var(--u));
    text-align: center;
  }

  .kicker-line {
    display: flex;
    align-items: baseline;
    gap: calc(20 * var(--u));
  }

  .kicker-name {
    color: var(--clay);
    font: 500 max(22px, calc(44 * var(--u)))/1 var(--font-latin);
    letter-spacing: -0.01em;
  }

  .kicker-what {
    color: var(--ink);
    font: 400 max(17px, calc(32 * var(--u)))/1 var(--font-zh);
    letter-spacing: 0.24em;
  }

  .hero[data-lang="en"] .kicker-what {
    font-family: var(--font-latin);
    font-style: italic;
    letter-spacing: 0.01em;
  }

  .kicker-rule {
    width: calc(72 * var(--u));
    height: max(2px, calc(3 * var(--u)));
    border-radius: 999px;
    background: var(--clay);
  }

  /* --title-scale shrinks a long headline so its longer row fits 1320 design px (set by the page script). */
  .headline {
    margin: 0;
    color: var(--ink);
    font-family: var(--font-zh);
    font-size: calc(216 * var(--u) * var(--title-scale, 1));
    font-weight: 300;
    letter-spacing: 0;
    line-height: 1.18;
  }

  .hero[data-lang="en"] .headline {
    --ext-y: 0.06em;
    font-family: var(--font-latin);
    font-size: calc(188 * var(--u) * var(--title-scale, 1));
    letter-spacing: -0.025em;
  }

  .row {
    display: flex;
    align-items: center;
  }

  .row-1 {
    padding-left: var(--m);
  }

  .row-2 {
    padding-right: var(--m);
  }

  .clip {
    flex: none;
    display: block;
    overflow: hidden;
    padding-block: 0.08em;
    margin-block: -0.08em;
  }

  .row-1 .clip {
    margin-left: calc(var(--part) * var(--part-dist) * -1);
  }

  .row-2 .clip {
    margin-right: calc(var(--part) * var(--part-dist) * -1);
  }

  .words {
    position: relative;
    display: block;
    white-space: nowrap;
    transform-origin: left bottom;
  }

  /* The highlight lives on a decorative copy; the solid ink text underneath stays whole. */
  .glint {
    position: absolute;
    inset: 0;
    color: transparent;
    -webkit-text-fill-color: transparent;
    background-image: linear-gradient(90deg, transparent 0%, var(--clay) 32%, var(--clay) 68%, transparent 100%);
    background-size: 2.4em 100%;
    background-repeat: no-repeat;
    background-position: -9999px 0;
    -webkit-background-clip: text;
    background-clip: text;
    pointer-events: none;
    user-select: none;
  }

  .ext {
    position: relative;
    flex: 1 1 0;
    align-self: stretch;
    min-width: 0;
  }

  .row-1 .ext {
    margin-left: var(--gap);
  }

  .row-2 .ext {
    margin-right: var(--gap);
  }

  .ext-draw {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(50% + var(--ext-y));
    height: max(1px, calc(1.6 * var(--u)));
    border-radius: 999px;
    background: var(--brass);
    transform-origin: left center;
  }

  /* The brush on each line: a thick stroke with a bright tip travels along it, and the ink it leaves
     behind spreads into the paper and fades. The page script draws both on this canvas, which starts
     40 design px before the line (so the ink can soften past its ends) and is sized in measure(). */
  .ink {
    position: absolute;
    left: calc(-40 * var(--u));
    top: calc(50% + var(--ext-y) + max(0.5px, calc(0.8 * var(--u))));
    transform: translateY(-50%);
    pointer-events: none;
  }

  /* Description and buttons share one row, centred on the page under the headline. */
  .lede {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: calc(28 * var(--u)) calc(64 * var(--u));
    margin-top: calc(84 * var(--u));
    padding-inline: var(--m);
  }

  .desc {
    flex: 0 1 auto;
    min-width: 0;
    margin: 0;
    color: var(--ink-soft);
    font-family: var(--font-zh);
    font-size: max(16px, calc(24 * var(--u)));
    font-weight: 400;
    line-height: 1.75;
  }

  .desc .line {
    display: block;
    overflow: hidden;
    white-space: nowrap;
  }

  .desc .line-in {
    display: block;
  }

  .hero[data-lang="en"] .desc {
    font-family: var(--font-latin);
    line-height: 1.6;
  }

  .actions {
    display: flex;
    flex: none;
    flex-wrap: wrap;
    align-items: center;
    gap: max(12px, calc(16 * var(--u)));
  }

  /* Primary: a clay pill with the arrow in its own paper disc at the right end. On hover the
     button deepens, its shadow tightens and the arrow slides forward inside the disc. */
  .primary {
    --size: max(48px, calc(64 * var(--u)));
    --inset: max(5px, calc(6 * var(--u)));
    display: inline-flex;
    align-items: center;
    gap: calc(22 * var(--u));
    height: var(--size);
    padding-left: calc(34 * var(--u));
    padding-right: var(--inset);
    border-radius: var(--radius-control);
    background: var(--clay);
    box-shadow:
      inset 0 1px 0 rgb(255 250 243 / 18%),
      0 1px 2px rgb(53 40 32 / 12%),
      0 10px 24px -12px rgb(153 79 54 / 55%);
    color: var(--paper);
    font: 500 max(15px, calc(18 * var(--u)))/1 var(--font-ui);
    letter-spacing: 0.02em;
    text-decoration: none;
    transition:
      background-color 0.25s ease,
      box-shadow 0.25s ease,
      transform 0.25s ease;
  }

  .primary .arrow {
    position: relative;
    display: grid;
    place-items: center;
    width: calc(var(--size) - 2 * var(--inset));
    aspect-ratio: 1;
    border-radius: 50%;
    background: var(--paper);
    color: var(--clay);
  }

  /* A ring that spreads from the disc once a cycle, when the sound reaches the end of row two's line. */
  .primary .ring {
    position: absolute;
    inset: -1px;
    border: 1.5px solid var(--brass);
    border-radius: 50%;
    opacity: 0;
    pointer-events: none;
  }

  .primary .ring.is-on {
    animation: ring-out 900ms cubic-bezier(0.16, 0.84, 0.24, 1);
  }

  @keyframes ring-out {
    from { opacity: 0.9; transform: scale(1); }
    to { opacity: 0; transform: scale(1.75); }
  }

  .primary .arrow svg {
    width: 42%;
    height: 42%;
    transition: transform 0.35s cubic-bezier(0.16, 0.84, 0.24, 1);
  }

  .primary:hover {
    background: var(--clay-deep);
    box-shadow:
      inset 0 1px 0 rgb(255 250 243 / 14%),
      0 1px 2px rgb(53 40 32 / 14%),
      0 6px 16px -10px rgb(131 65 44 / 60%);
  }

  .primary:hover .arrow svg {
    transform: translateX(22%);
  }

  .primary:active {
    transform: translateY(1px);
  }

  /* Secondary: the same pill drawn with the headline's brass hairline (an SVG outline, so the entry
     can draw it around). Hover fills it with warm paper. */
  .secondary {
    position: relative;
    display: inline-flex;
    align-items: center;
    height: max(48px, calc(64 * var(--u)));
    padding-inline: calc(32 * var(--u));
    border-radius: var(--radius-control);
    background: transparent;
    color: var(--ink);
    font: 500 max(15px, calc(18 * var(--u)))/1 var(--font-ui);
    letter-spacing: 0.02em;
    text-decoration: none;
    transition:
      background-color 0.25s ease,
      border-color 0.25s ease,
      color 0.25s ease,
      transform 0.25s ease;
  }

  .secondary .outline {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
  }

  .secondary .outline rect {
    x: 0.5px;
    y: 0.5px;
    width: calc(100% - 1px);
    height: calc(100% - 1px);
    rx: calc(max(48px, 64 * var(--u)) / 2 - 0.5px);
    fill: none;
    stroke: var(--brass);
    stroke-width: 1px;
    stroke-dasharray: 1 1;
    stroke-dashoffset: 0;
    transition: stroke 0.25s ease;
  }

  .secondary > span {
    position: relative;
  }

  .secondary:hover {
    background: rgb(241 223 201 / 60%);
    color: var(--clay-deep);
  }

  .secondary:hover .outline rect {
    stroke: var(--clay);
  }

  .secondary:active {
    transform: translateY(1px);
  }

  .primary:focus-visible {
    outline-offset: 5px;
  }

  .nav a,
  .langs button {
    transition:
      color 0.2s ease,
      background-color 0.2s ease;
  }

  /* Entry: the v2 rise(line) and draw presets, timed with overlaps. */
  @keyframes rise-line {
    from { transform: translateY(105%) rotate(2.5deg); }
    to { transform: none; }
  }

  @keyframes draw-x {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }

  @keyframes failsafe {
    to { transform: none; opacity: 1; stroke-dashoffset: 0; }
  }

  .pre-entry .hero .words {
    transform: translateY(105%) rotate(2.5deg);
    animation: failsafe 0.01s linear 3s forwards;
  }

  .pre-entry .hero .ext-draw {
    transform: scaleX(0);
    animation: failsafe 0.01s linear 3s forwards;
  }

  .hero.is-playing .row-1 .words {
    animation: rise-line 950ms cubic-bezier(0.19, 0.8, 0.22, 1) 0ms both;
  }

  .hero.is-playing .row-1 .ext-draw {
    animation: draw-x 800ms cubic-bezier(0.37, 0, 0.63, 1) 450ms both;
  }

  .hero.is-playing .row-2 .ext-draw {
    animation: draw-x 800ms cubic-bezier(0.37, 0, 0.63, 1) 650ms both;
  }

  .hero.is-playing .row-2 .words {
    animation: rise-line 950ms cubic-bezier(0.19, 0.8, 0.22, 1) 1050ms both;
  }

  /* The row under the headline follows it in. Text and buttons stay in place and clickable throughout;
     only a short rise, the arrow disc and the outline change. */
  @keyframes rise-in {
    from { transform: translateY(105%); }
    to { transform: none; }
  }

  @keyframes btn-in {
    from { opacity: 0; transform: translateY(calc(14 * var(--u))); }
    to { opacity: 1; transform: none; }
  }

  @keyframes disc-in {
    from { transform: scale(0); }
    to { transform: scale(1); }
  }

  @keyframes arrow-in {
    from { transform: translateX(-70%); opacity: 0; }
    to { transform: none; opacity: 1; }
  }

  @keyframes outline-draw {
    from { stroke-dashoffset: 1; }
    to { stroke-dashoffset: 0; }
  }

  @keyframes label-in {
    from { opacity: 0; }
    to { opacity: 1; }
  }

  .hero.is-playing .kicker {
    animation: kicker-in 700ms ease 0ms both;
  }

  @keyframes kicker-in {
    from { opacity: 0; letter-spacing: 0.3em; }
  }

  .hero.is-playing .desc .line-in {
    animation: rise-in 800ms cubic-bezier(0.19, 0.8, 0.22, 1) both;
  }

  .hero.is-playing #desc0 {
    animation-delay: 1400ms;
  }

  .hero.is-playing #desc1 {
    animation-delay: 1500ms;
  }

  /* Buttons wait for the description: the pill fades up, then its arrow disc and the outline follow. */
  .hero.is-playing .primary {
    animation: btn-in 650ms cubic-bezier(0.19, 0.8, 0.22, 1) 2000ms both;
  }

  .hero.is-playing .primary .arrow {
    animation: disc-in 500ms cubic-bezier(0.34, 1.4, 0.64, 1) 2250ms both;
  }

  .hero.is-playing .primary .arrow svg {
    animation: arrow-in 400ms cubic-bezier(0.16, 0.84, 0.24, 1) 2450ms both;
  }

  .hero.is-playing .secondary .outline rect {
    animation: outline-draw 700ms cubic-bezier(0.37, 0, 0.63, 1) 2150ms both;
  }

  .hero.is-playing .secondary > span {
    animation: label-in 350ms ease 2400ms both;
  }

  /* Before the entry starts (fonts loading) the row already shows its first frame, so nothing flashes. */
  .pre-entry .hero .kicker {
    opacity: 0;
    animation: failsafe 0.01s linear 3s forwards;
  }

  .pre-entry .hero .desc .line-in {
    transform: translateY(105%);
    animation: failsafe 0.01s linear 3s forwards;
  }

  .pre-entry .hero .primary {
    opacity: 0;
    transform: translateY(calc(14 * var(--u)));
    animation: failsafe 0.01s linear 3s forwards;
  }

  .pre-entry .hero .primary .arrow {
    transform: scale(0);
    animation: failsafe 0.01s linear 3s forwards;
  }

  .pre-entry .hero .primary .arrow svg,
  .pre-entry .hero .secondary > span {
    opacity: 0;
    animation: failsafe 0.01s linear 3s forwards;
  }

  .pre-entry .hero .secondary .outline rect {
    stroke-dashoffset: 1;
    animation: failsafe 0.01s linear 3s forwards;
  }

  /* After the entry the rows rest without animations, so switching views never replays it. */
  .hero.is-playing.entry-done .row .words,
  .hero.is-playing.entry-done .row .ext-draw,
  .hero.is-playing.entry-done .desc .line-in,
  .hero.is-playing.entry-done .primary,
  .hero.is-playing.entry-done .primary .arrow,
  .hero.is-playing.entry-done .primary .arrow svg,
  .hero.is-playing.entry-done .secondary .outline rect,
  .hero.is-playing.entry-done .secondary > span {
    animation: none;
  }

  .is-static .hero .kicker,
  .is-static .hero .words,
  .is-static .hero .ext-draw,
  .is-static .hero .desc .line-in,
  .is-static .hero .primary,
  .is-static .hero .primary .arrow,
  .is-static .hero .primary .arrow svg,
  .is-static .hero .secondary .outline rect,
  .is-static .hero .secondary > span {
    animation: none !important;
    transform: none !important;
  }

  .is-static .hero .glint,
  .is-static .hero .ink,
  .is-static .hero .ring {
    display: none;
  }

  @media (prefers-reduced-motion: reduce) {
    .hero .kicker,
    .hero .words,
    .hero .ext-draw,
    .hero .desc .line-in,
    .hero .primary,
    .hero .primary .arrow,
    .hero .primary .arrow svg,
    .hero .secondary .outline rect,
    .hero .secondary > span {
      animation: none !important;
      transform: none !important;
    }

    .hero .glint,
    .hero .ink,
    .hero .ring {
      display: none;
    }

    .hero .primary,
    .hero .primary .arrow svg,
    .hero .secondary > span {
      opacity: 1 !important;
    }

    .hero .secondary .outline rect {
      stroke-dashoffset: 0 !important;
    }

    .primary,
    .primary .arrow svg,
    .secondary {
      transition-duration: 0.01s !important;
    }

    .primary:hover .arrow svg {
      transform: none;
    }
  }

  /* ---------- Act 2: one timeline per scene ---------- */

  /* The zone's shell sticks for one screen while its 2400 ms (2400 px) of scene time pass. The
     copy starts on the page margin (the brand's edge), the drawing's frame and its timeline end on
     the other margin (the language switch's edge), and the copy is centred on the figure. */
  .act-time {
    background: var(--ground);
  }

  .time {
    --m: calc(120 * var(--u));
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 520fr) minmax(0, 1100fr);
    align-items: center;
    gap: calc(80 * var(--u));
    height: 100vh;
    height: 100svh;
    padding: calc(160 * var(--u)) var(--m) calc(80 * var(--u));
    box-sizing: border-box;
  }

  .time-copy {
    display: grid;
    gap: calc(32 * var(--u));
    min-width: 0;
  }

  .time-title {
    margin: 0;
    color: var(--ink);
    font-family: var(--font-zh);
    font-size: calc(88 * var(--u));
    font-weight: 400;
    line-height: 1.2;
    letter-spacing: 0;
    max-width: 5.2em;
  }

  :root[lang="en"] .time-title {
    max-width: 7.5em;
    text-wrap: balance;
    font-family: var(--font-latin);
    font-weight: 300;
    letter-spacing: -0.02em;
  }

  .time-text {
    margin: 0;
    color: var(--ink-soft);
    font-family: var(--font-zh);
    font-size: max(16px, calc(24 * var(--u)));
    line-height: 1.85;
    word-break: keep-all;
  }

  .time-line {
    display: block;
  }

  :root[lang="en"] .time-text {
    font-family: var(--font-latin);
    line-height: 1.6;
    word-break: normal;
    text-wrap: balance;
  }

  /* The drawing and its timeline. On short, wide screens the figure gives up width instead of
     height, still ending on the margin. */
  .time-figure {
    --track-h: max(22px, calc(30 * var(--u)));
    --track-gap: max(6px, calc(8 * var(--u)));
    --tl-gap: max(14px, calc(36 * var(--u)));
    --tl-h: calc(6 * var(--track-h) + 5 * var(--track-gap));
    --copy-h: 0px;
    position: relative;
    justify-self: end;
    width: max(min(100%, 320px), min(100%, calc((100vh - 240 * var(--u) - var(--tl-gap) - var(--tl-h) - var(--copy-h)) * 1022 / 542)));
    width: max(min(100%, 320px), min(100%, calc((100svh - 240 * var(--u) - var(--tl-gap) - var(--tl-h) - var(--copy-h)) * 1022 / 542)));
    min-width: 0;
    margin: 0;
    container-type: inline-size;
  }

  /* The drawing's viewBox (39 39 1022 542) hugs the frame, so the frame's edges are the plate's. */
  .plate {
    position: relative;
    aspect-ratio: 1022 / 542;
  }

  .plate-svg {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
  }

  /* The scene: the frame and its title bar are there from the start; the parts are sketched
     where they will enter, and each part covers its sketch as it does. */
  .plate-base .frame {
    fill: var(--surface);
    stroke: var(--brass);
    stroke-width: 1.6;
  }

  .plate-base .rule {
    fill: none;
    stroke: var(--brass);
    stroke-width: 1.6;
  }

  .sketch {
    fill: none;
    stroke: var(--line-soft);
    stroke-width: 1.2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .plate .fill-paper { fill: var(--paper); }
  .plate .fill-warm { fill: var(--paper-deep); }
  .plate .fill-chrome { fill: var(--surface-deep); }
  .plate .fill-ink { fill: var(--ink); }
  .plate .fill-soft { fill: var(--brass-soft); }
  .plate .fill-clay { fill: var(--clay); }
  .plate .fill-brass { fill: var(--brass); }

  .plate .pill-outline {
    fill: none;
    stroke: var(--brass);
    stroke-width: 1.6;
    stroke-linecap: round;
  }

  .plate .link {
    fill: none;
    stroke: var(--clay);
    stroke-width: 3;
    stroke-linecap: round;
  }

  /* Each part sits on its own bounds, as fractions of the viewBox (39 39 1022 542), so its wipe
     crosses exactly the part over its window. */
  .part {
    position: absolute;
  }

  .part svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
  }

  .part-title { left: 5.577%; top: 32.288%; width: 35.812%; height: 5.535%; }
  .part-text { left: 5.577%; top: 42.251%; width: 32.290%; height: 9.225%; }
  .part-primary { left: 5.479%; top: 57.196%; width: 14.677%; height: 8.487%; }
  .part-secondary { left: 21.331%; top: 57.196%; width: 12.720%; height: 8.487%; }
  .part-picture { left: 54.305%; top: 22.325%; width: 40.117%; height: 65.683%; }
  .part-link { left: 40.607%; top: 34.686%; width: 14.481%; height: 17.528%; }

  /* The timeline: one track per part. A take spans the part's entrance (--at, --len in ms of the
     scene's 2400); the line after it is the time the part stays on screen. Each fill copies its
     take and wipes across it with the part, so it reaches the playhead as the part enters. */
  .timeline {
    --scene: 2400;
    position: relative;
    margin-top: var(--tl-gap);
  }

  .tracks {
    display: grid;
    gap: var(--track-gap);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .track {
    position: relative;
    height: var(--track-h);
  }

  .track::after {
    content: "";
    position: absolute;
    top: 50%;
    left: calc((var(--at) + var(--len)) / var(--scene) * 100%);
    right: 0;
    height: 2px;
    margin-top: -1px;
    border-radius: 999px;
    background: var(--line-soft);
  }

  .take,
  .take-fill {
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(var(--at) / var(--scene) * 100%);
    width: calc(var(--len) / var(--scene) * 100%);
  }

  .take,
  .take-face {
    display: flex;
    align-items: center;
    gap: 0.6em;
    height: 100%;
    padding: 0 max(10px, calc(14 * var(--u)));
    border: 1px solid var(--brass);
    border-radius: var(--radius-control);
    background: transparent;
    color: var(--ink-soft);
    font: 500 max(12px, calc(15 * var(--u)))/1 var(--font-ui);
    white-space: nowrap;
    overflow: hidden;
  }

  .take-face {
    border-color: var(--clay);
    background: var(--clay);
    color: var(--paper);
  }

  .take-time {
    font-weight: 400;
    font-variant-numeric: tabular-nums;
  }

  /* Handoffs. Each `after` is drawn as a rounded elbow: it drops from under the end of the earlier
     take, turns, and runs right into the start of the later one, ending in an arrowhead (--from,
     --to: track rows, 0 at the top; --at: the instant). The elbow sits in the gap left of the later
     take, so it never crosses a take. The lit copy has the later part's after and is wiped in from
     the top, so the arrow fills in clay as that part starts. */
  .joins {
    position: absolute;
    inset: 0;
    pointer-events: none;
  }

  .join {
    --reach: max(16px, calc(22 * var(--u)));
    --y0: calc((var(--from) + 1) * var(--track-h) + var(--from) * var(--track-gap));
    --y1: calc(var(--to) * (var(--track-h) + var(--track-gap)) + var(--track-h) / 2);
    position: absolute;
    top: var(--y0);
    left: calc(var(--at) / var(--scene) * 100% - var(--reach));
    width: var(--reach);
    height: calc(var(--y1) - var(--y0) + 5px);
  }

  .join-line,
  .join-lit {
    position: absolute;
    inset: 0;
  }

  /* The elbow: a left and bottom border with a rounded corner, and the arrowhead at its end. */
  .join-path {
    position: absolute;
    top: 0;
    left: 0;
    right: 3px;
    bottom: 5px;
    border-left: 1.5px solid var(--line-soft);
    border-bottom: 1.5px solid var(--line-soft);
    border-bottom-left-radius: max(8px, calc(12 * var(--u)));
  }

  .join-path::after {
    content: "";
    position: absolute;
    right: -4px;
    bottom: -4.75px;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    border-left: 5px solid var(--line-soft);
  }

  .join-lit .join-path {
    border-color: var(--clay);
  }

  .join-lit .join-path::after {
    border-left-color: var(--clay);
  }

  /* Where the scene is now: across every track, on the same time as the parts. */
  .playhead {
    position: absolute;
    top: calc(-1 * var(--track-gap));
    bottom: calc(-1 * var(--track-gap));
    left: 0;
    width: 100%;
    pointer-events: none;
  }

  .playhead-mark {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -1px;
    width: 2px;
    border-radius: 999px;
    background: var(--ink);
  }

  .playhead-mark::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: max(8px, calc(11 * var(--u)));
    height: max(8px, calc(11 * var(--u)));
    border-radius: 50%;
    background: var(--ink);
    transform: translate(-50%, -50%);
  }

  /* Narrow timelines keep the part's name; the duration goes first (the shortest take, 500 of
     2400, needs about 115px for both). */
  @container (width < 560px) {
    .take-time {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
    }
  }

  /* Without the runtime every part and take shows complete; a playhead at 0 would contradict it. */
  :root:not(.cv-js) .playhead {
    display: none;
  }

  /* ---------- Act 4: work out the design with your AI ---------- */

  .act-method {
    background: var(--ground);
  }

  .method {
    --m: calc(120 * var(--u));
    display: grid;
    grid-template-columns: minmax(0, 860fr) minmax(0, 760fr);
    align-items: center;
    gap: calc(100 * var(--u));
    padding: calc(190 * var(--u)) var(--m) calc(170 * var(--u));
  }

  /* The sketch behind the act: faint lines, under everything, cut by the act's edges. */
  .method {
    position: relative;
    overflow: clip;
  }

  .method-sketch {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
  }

  .method-sketch-svg {
    display: block;
    width: 100%;
    height: 100%;
    fill: none;
    stroke: color-mix(in srgb, var(--ground), var(--brass) 45%);
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .method-copy,
  .cards {
    position: relative;
    z-index: 1;
  }

  .method-copy {
    display: grid;
    gap: calc(30 * var(--u));
    min-width: 0;
  }

  .method-title {
    margin: 0;
    color: var(--ink);
    font: 400 calc(76 * var(--u))/1.2 var(--font-zh);
  }

  .method-text {
    max-width: 30em;
    text-wrap: pretty;
    margin: 0;
    color: var(--ink-soft);
    font: 400 max(16px, calc(22 * var(--u)))/1.85 var(--font-zh);
  }

  :root[lang="en"] .method-title {
    font-family: var(--font-latin);
    font-weight: 300;
    letter-spacing: -0.02em;
  }

  :root[lang="en"] .method-text,
  :root[lang="en"] .card-text {
    font-family: var(--font-latin);
    line-height: 1.6;
  }

  /* The brief: real text the reader can copy, in a quiet panel. */
  .brief {
    display: grid;
    gap: calc(18 * var(--u));
    margin-top: calc(8 * var(--u));
    padding: calc(28 * var(--u)) calc(32 * var(--u));
    border: max(1px, calc(1.4 * var(--u))) solid var(--line-soft);
    border-radius: calc(20 * var(--u));
    background: var(--surface);
  }

  .brief-label {
    margin: 0;
    color: var(--clay);
    font: 500 max(13px, calc(16 * var(--u)))/1.2 var(--font-ui);
    letter-spacing: 0.06em;
  }

  .brief-text {
    margin: 0;
    color: var(--ink);
    font: 400 max(13px, calc(17 * var(--u)))/1.8 var(--font-code);
    white-space: pre-wrap;
  }

  .brief-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(16 * var(--u)) calc(20 * var(--u));
  }

  .brief-copy {
    border: 0;
    padding-right: calc(32 * var(--u));
    cursor: pointer;
  }

  .brief-status {
    color: var(--ink-soft);
    font: 400 max(13px, calc(16 * var(--u)))/1.2 var(--font-ui);
  }

  /* Typewriter (hero.js): the text not typed yet keeps its place but is invisible, so nothing
     reflows; a caret follows the typed part. While a card waits its turn it shows three dots. */
  .type-rest {
    color: transparent;
  }

  .is-typing .type-shown::after {
    content: "";
    display: inline-block;
    width: 0.08em;
    height: 1em;
    margin-left: 0.04em;
    vertical-align: -0.12em;
    background: var(--clay);
    animation: caret 0.9s steps(1) infinite;
  }

  @keyframes caret {
    50% { opacity: 0; }
  }

  .card-text.is-waiting {
    position: relative;
  }

  .card-text.is-waiting .type-rest {
    visibility: hidden;
  }

  .card-text.is-waiting::before {
    content: "";
    position: absolute;
    top: 0.35em;
    left: 0;
    width: 2.4em;
    height: 0.5em;
    background:
      radial-gradient(circle closest-side, var(--clay) 90%, transparent) 0 50% / 0.5em 0.5em no-repeat,
      radial-gradient(circle closest-side, var(--clay) 90%, transparent) 0.9em 50% / 0.5em 0.5em no-repeat,
      radial-gradient(circle closest-side, var(--clay) 90%, transparent) 1.8em 50% / 0.5em 0.5em no-repeat;
    animation: dots 1.2s ease-in-out infinite;
  }

  @keyframes dots {
    0%, 100% { opacity: 0.35; }
    50% { opacity: 1; }
  }

  /* Three cards of an example discussion; each overlaps the one before as it enters. */
  .cards {
    display: grid;
    gap: calc(22 * var(--u));
    min-width: 0;
  }

  .cards-note {
    margin: 0;
    color: var(--ink-soft);
    font: 500 max(13px, calc(15 * var(--u)))/1.2 var(--font-ui);
    letter-spacing: 0.06em;
  }

  .card-b {
    margin-left: calc(48 * var(--u));
  }

  .card-c {
    margin-left: calc(96 * var(--u));
  }

  .card-body {
    display: grid;
    gap: calc(12 * var(--u));
    padding: calc(24 * var(--u)) calc(28 * var(--u));
    border: max(1px, calc(1.4 * var(--u))) solid var(--line-soft);
    border-radius: calc(18 * var(--u));
    background: var(--surface);
    box-shadow: 0 calc(18 * var(--u)) calc(40 * var(--u)) calc(-24 * var(--u)) rgb(53 40 32 / 22%);
  }

  .card-who {
    display: flex;
    align-items: center;
    gap: calc(10 * var(--u));
    margin: 0;
    color: var(--clay);
    font: 500 max(12px, calc(15 * var(--u)))/1.2 var(--font-ui);
  }

  .card-mark {
    width: max(14px, calc(20 * var(--u)));
    height: max(14px, calc(20 * var(--u)));
    fill: none;
    stroke: var(--clay);
    stroke-width: 1.6;
    stroke-linejoin: round;
  }

  .card-mark path {
    fill: var(--clay);
    stroke: none;
  }

  .card-text {
    margin: 0;
    text-wrap: pretty;
    color: var(--ink);
    font: 400 max(15px, calc(21 * var(--u)))/1.7 var(--font-zh);
  }

  /* ---------- Act 5: stack them, then cover them; read across ---------- */

  .act-cards,
  .act-row {
    background: var(--ground);
  }

  /* The held deck fills more of its screen: this act's design unit is 8% larger (margins keep the
     page's unit, so its edges line up with the other acts). */
  .cards-act {
    --m: calc(120 * min(100vw, 177.78vh) / 1920);
    --u: calc(min(100vw, 177.78vh) / 1920 * 1.08);
    display: grid;
    grid-template-columns: minmax(0, 620fr) minmax(0, 900fr);
    align-items: center;
    gap: calc(140 * var(--u));
    height: 100vh;
    height: 100svh;
    /* The fixed bar covers the top: more padding there centres the content in what is visible. */
    padding: calc(150 * var(--u)) var(--m) calc(60 * var(--u));
  }

  .cards-copy {
    display: grid;
    gap: calc(30 * var(--u));
  }

  .cards-title,
  .row-title {
    margin: 0;
    color: var(--ink);
    font: 400 calc(76 * var(--u))/1.2 var(--font-zh);
  }

  .cards-text,
  .row-text {
    margin: 0;
    color: var(--ink-soft);
    font: 400 max(16px, calc(22 * var(--u)))/1.85 var(--font-zh);
    text-wrap: pretty;
  }

  :root[lang="en"] .cards-title,
  :root[lang="en"] .row-title {
    font-family: var(--font-latin);
    font-weight: 300;
    letter-spacing: -0.02em;
  }

  :root[lang="en"] .cards-text,
  :root[lang="en"] .row-text,
  :root[lang="en"] .deck-text {
    font-family: var(--font-latin);
    line-height: 1.6;
  }

  /* The deck: each card lands 64 px below the last (step), so the titles above stay readable. */
  /* The deck: content-tall cards, narrower than the column, on its right edge. */
  .deck {
    align-self: center;
    justify-self: end;
    width: min(100%, calc(900 * var(--u)));
  }

  .deck-card {
    position: relative;
    overflow: hidden;
    display: grid;
    align-content: start;
    gap: calc(16 * var(--u));
    padding: calc(24 * var(--u)) calc(36 * var(--u)) calc(26 * var(--u));
    border: max(1px, calc(1.4 * var(--u))) solid var(--line-soft);
    border-radius: calc(22 * var(--u));
    background: var(--surface);
    box-shadow: 0 calc(20 * var(--u)) calc(44 * var(--u)) calc(-26 * var(--u)) rgb(53 40 32 / 24%);
  }

  .deck-head {
    display: flex;
    align-items: center;
    gap: calc(16 * var(--u));
    min-height: max(28px, calc(30 * var(--u)));
    margin: 0;
    color: var(--ink);
    font: 500 max(16px, calc(24 * var(--u)))/1.2 var(--font-zh);
  }

  .deck-num {
    display: grid;
    flex: none;
    place-items: center;
    width: max(24px, calc(30 * var(--u)));
    height: max(24px, calc(30 * var(--u)));
    border-radius: 50%;
    background: var(--clay);
    color: var(--paper);
    font: 600 max(12px, calc(15 * var(--u)))/1 var(--font-latin);
  }

  .deck-text {
    margin: calc(24 * var(--u)) 0 0;
    color: var(--ink-soft);
    font: 400 max(15px, calc(24 * var(--u)))/1.65 var(--font-zh);
  }

  .deck-tag {
    margin-left: auto;
    padding: calc(6 * var(--u)) calc(14 * var(--u));
    border: max(1px, calc(1.2 * var(--u))) solid var(--line-soft);
    border-radius: 999px;
    color: var(--ink-soft);
    font: 500 max(11px, calc(14 * var(--u)))/1 var(--font-ui);
    letter-spacing: 0.06em;
  }

  .deck-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) calc(230 * var(--u));
    align-items: stretch;
    gap: calc(30 * var(--u));
    margin-top: calc(8 * var(--u));
  }

  /* The picture of each step, in a thumbnail tile that fills the body's height. */
  .deck-thumb {
    display: grid;
    place-items: center;
    padding: calc(16 * var(--u));
    border-radius: calc(14 * var(--u));
    background: color-mix(in srgb, var(--ground), var(--brass) 14%);
  }

  /* Each step's example: a line of the brief, the scene plan as a table, a line of the build. */
  .deck-sample {
    display: grid;
    gap: calc(8 * var(--u));
    margin-top: calc(4 * var(--u));
    padding: calc(14 * var(--u)) calc(18 * var(--u));
    border-radius: calc(12 * var(--u));
    background: color-mix(in srgb, var(--ground), var(--brass) 12%);
  }

  .deck-sample-label {
    color: var(--ink-soft);
    font: 500 max(11px, calc(13 * var(--u)))/1 var(--font-ui);
    letter-spacing: 0.06em;
  }

  .deck-quote {
    margin: 0;
    color: var(--ink);
    font: 400 max(14px, calc(22 * var(--u)))/1.5 var(--font-zh);
  }

  .deck-table {
    border-collapse: collapse;
    color: var(--ink);
    font: 400 max(13px, calc(19 * var(--u)))/1.6 var(--font-zh);
  }

  .deck-table td + td {
    padding-left: calc(24 * var(--u));
    color: var(--ink-soft);
    font-family: var(--font-code);
  }

  .deck-code {
    margin: 0;
    color: var(--ink);
    font: 400 max(12px, calc(18 * var(--u)))/1.6 var(--font-code);
    white-space: pre-wrap;
  }

  /* The three steps under the text, as the deck's legend. */
  /* 从一个想法，/ 变成一页设计: each half whole, so the title breaks only after the comma. */
  .row-title .nb {
    display: inline-block;
    white-space: nowrap;
  }

  .cards-title .nb {
    display: inline-block;
    white-space: nowrap;
  }

  .cards-flow {
    display: flex;
    align-items: center;
    gap: calc(14 * var(--u));
    margin: calc(26 * var(--u)) 0 0;
    padding: 0;
    list-style: none;
  }

  .cards-flow li {
    display: flex;
    align-items: center;
    gap: calc(10 * var(--u));
    color: var(--ink);
    font: 500 max(14px, calc(20 * var(--u)))/1 var(--font-ui);
  }

  .cards-flow li + li::before {
    content: "";
    width: calc(46 * var(--u));
    height: max(1px, calc(1.6 * var(--u)));
    margin-right: calc(4 * var(--u));
    background: var(--line-soft);
  }

  /* Each step's number is outlined; it fills in as its card lands (the zone is 1300 long: the deck
     enters over its first 1000, the last 300 hold the finished deck). */
  .cards-flow li {
    position: relative;
  }

  .cards-flow b {
    display: grid;
    place-items: center;
    width: max(24px, calc(34 * var(--u)));
    height: max(24px, calc(34 * var(--u)));
    border: max(1.5px, calc(2 * var(--u))) solid var(--clay);
    border-radius: 50%;
    color: var(--clay);
    font: 500 max(12px, calc(16 * var(--u)))/1 var(--font-latin);
  }

  .flow-lit {
    position: absolute;
    left: 0;
    top: 50%;
    translate: 0 -50%;
  }

  .flow-lit b {
    background: var(--clay);
    color: var(--paper);
  }

  /* Past the connector (46 u), its margin (4 u) and the row's gap (10 u). */
  .cards-flow li + li .flow-lit {
    left: calc(60 * var(--u));
  }

  @keyframes flow-1 {
    from, to { opacity: 1; }
  }

  @keyframes flow-2 {
    0%, 24% { opacity: 0; }
    27%, 100% { opacity: 1; }
  }

  @keyframes flow-3 {
    0%, 49% { opacity: 0; }
    52%, 100% { opacity: 1; }
  }

  .deck-out {
    margin: calc(6 * var(--u)) 0 0;
    padding-top: calc(14 * var(--u));
    border-top: max(1px, calc(1.2 * var(--u))) solid var(--line-soft);
    color: var(--clay);
    font: 500 max(12px, calc(16 * var(--u)))/1.3 var(--font-ui);
  }

  .deck-body .deck-text {
    margin: 0;
  }

  .deck-points {
    display: grid;
    gap: calc(8 * var(--u));
    margin: calc(14 * var(--u)) 0 0;
    padding: 0;
    list-style: none;
    color: var(--ink);
    font: 400 max(14px, calc(21 * var(--u)))/1.5 var(--font-zh);
  }

  .deck-points li {
    display: flex;
    gap: calc(12 * var(--u));
  }

  .deck-points li::before {
    content: "";
    flex: none;
    width: calc(8 * var(--u));
    height: calc(8 * var(--u));
    margin-top: 0.55em;
    border-radius: 50%;
    background: var(--clay);
  }

  .deck-art {
    width: 100%;
    height: auto;
    fill: var(--surface-deep);
    stroke: var(--clay);
    stroke-width: 1.4;
    stroke-linecap: round;
  }

  :root[lang="en"] .deck-points {
    font-family: var(--font-latin);
  }

  /* The row: a lead block, then the panels; the scene moves the row left by its overflow. */
  .act-row {
    --m: calc(120 * var(--u));
  }

  .row-lead {
    display: grid;
    align-content: center;
    gap: calc(28 * var(--u));
    flex: none;
    width: calc(640 * var(--u));
    margin-left: var(--m);
    margin-right: calc(80 * var(--u));
  }

  .row-panel {
    display: grid;
    align-content: center;
    justify-items: start;
    gap: calc(18 * var(--u));
    flex: none;
    align-self: center;
    align-content: start;
    width: calc(520 * var(--u));
    height: auto;
    margin-right: calc(40 * var(--u));
    padding: calc(40 * var(--u));
    border: max(1px, calc(1.4 * var(--u))) solid var(--line-soft);
    border-radius: calc(24 * var(--u));
    background: var(--surface);
  }

  .row-panel:last-child {
    margin-right: var(--m);
  }

  .row-icon {
    width: 100%;
    height: auto;
    fill: var(--surface-deep);
    stroke: var(--brass);
    stroke-width: 1.4;
  }

  .row-name {
    margin: 0;
    color: var(--ink);
    font: 400 max(18px, calc(34 * var(--u)))/1.2 var(--font-zh);
  }

  /* Each page type as a small storyboard: three shots with their names, the pace of the three
     (segment widths), and the effects that suit it. */
  .rp-board {
    display: flex;
    align-items: flex-start;
    gap: calc(6 * var(--u));
    width: 100%;
    margin-bottom: calc(4 * var(--u));
  }

  .rp-shot {
    flex: 1;
    display: grid;
    gap: calc(8 * var(--u));
    margin: 0;
    min-width: 0;
  }

  .rp-shot svg {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    padding: calc(6 * var(--u));
    border-radius: calc(10 * var(--u));
    background: color-mix(in srgb, var(--ground), var(--brass) 16%);
    fill: color-mix(in srgb, var(--ground), var(--clay) 30%);
    stroke: var(--clay);
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .rp-shot figcaption {
    color: var(--ink-soft);
    font: 500 max(11px, calc(14 * var(--u)))/1.3 var(--font-ui);
    text-align: center;
  }

  .rp-arrow {
    flex: none;
    width: calc(14 * var(--u));
    height: calc(14 * var(--u));
    margin-top: calc(34 * var(--u));
    border-top: max(1.5px, calc(2 * var(--u))) solid var(--clay);
    border-right: max(1.5px, calc(2 * var(--u))) solid var(--clay);
    rotate: 45deg;
    scale: 0.6;
  }

  .rp-meta {
    display: grid;
    grid-template-columns: calc(84 * var(--u)) 1fr;
    align-items: center;
    gap: calc(12 * var(--u));
    width: 100%;
    margin: 0;
  }

  .rp-label {
    color: var(--clay);
    font: 500 max(11px, calc(14 * var(--u)))/1 var(--font-ui);
  }

  .rp-pace {
    display: flex;
    gap: calc(4 * var(--u));
    height: calc(8 * var(--u));
  }

  .rp-pace i {
    border-radius: 999px;
    background: color-mix(in srgb, var(--ground), var(--clay) 45%);
  }

  .rp-pace i:nth-child(2) {
    background: var(--clay);
  }

  .rp-fx {
    display: flex;
    flex-wrap: wrap;
    gap: calc(8 * var(--u));
    margin: 0;
  }

  .rp-fx code {
    padding: calc(5 * var(--u)) calc(12 * var(--u));
    border: max(1px, calc(1.2 * var(--u))) solid var(--line-soft);
    border-radius: 999px;
    color: var(--ink-soft);
    font: 400 max(11px, calc(14 * var(--u)))/1 var(--font-code);
  }


  /* A sample opening line for the page type, set like a quote. */
  .rp-line {
    margin: 0;
    padding-left: calc(14 * var(--u));
    border-left: max(2px, calc(3 * var(--u))) solid var(--clay);
    color: var(--ink);
    font: 400 max(14px, calc(19 * var(--u)))/1.5 var(--font-zh);
  }

  :root[lang="en"] .rp-line {
    font-family: var(--font-latin);
    font-style: italic;
  }

  .row-desc {
    margin: 0;
    color: var(--ink-soft);
    font: 400 max(14px, calc(20 * var(--u)))/1.7 var(--font-zh);
  }

  .row-use {
    display: flex;
    flex-wrap: wrap;
    gap: calc(10 * var(--u));
    margin: auto 0 0;
    padding-top: calc(18 * var(--u));
    border-top: max(1px, calc(1.2 * var(--u))) solid var(--line-soft);
    color: var(--ink-soft);
    font: 500 max(12px, calc(15 * var(--u)))/1.4 var(--font-ui);
  }

  .row-use span:first-child {
    color: var(--clay);
  }

  :root[lang="en"] .row-desc {
    font-family: var(--font-latin);
  }

  :root[lang="en"] .row-name {
    font-family: var(--font-latin);
  }

  /* ---------- Act 6: the same frames, three treatments ---------- */

  .act-material {
    background: var(--ground);
  }

  .material {
    --m: calc(120 * var(--u));
    display: grid;
    grid-template-rows: auto 1fr auto;
    gap: calc(40 * var(--u));
    height: 100vh;
    height: 100svh;
    padding: calc(140 * var(--u)) var(--m) calc(70 * var(--u));
  }

  .material-copy {
    display: grid;
    grid-template-columns: minmax(0, 700fr) minmax(0, 900fr);
    align-items: end;
    gap: calc(80 * var(--u));
  }

  .material-title {
    max-width: none;
    margin: 0;
    color: var(--ink);
    font: 400 calc(76 * var(--u))/1.2 var(--font-zh);
  }

  .material-text {
    margin: 0;
    color: var(--ink-soft);
    font: 400 max(16px, calc(22 * var(--u)))/1.85 var(--font-zh);
    text-wrap: pretty;
  }

  :root[lang="en"] .material-title {
    max-width: none;
    text-wrap: balance;
    font-family: var(--font-latin);
    font-weight: 300;
    letter-spacing: -0.02em;
  }

  :root[lang="en"] .material-text {
    font-family: var(--font-latin);
    line-height: 1.6;
  }

  /* The strip: a warm film base (dark amber-brown, lighter at the edges, like stock against a
     light table) with rounded sprocket holes in two rows and the edge print between them. The
     holes are cut through (mask), so the act's ground shows in them. */
  .strip {
    position: relative;
    align-self: center;
    justify-self: center;
    padding: calc(52 * var(--u)) calc(34 * var(--u));
    border-radius: calc(8 * var(--u));
    background:
      linear-gradient(180deg, rgb(255 222 180 / 10%), transparent 18%, transparent 82%, rgb(255 222 180 / 10%)),
      #2b1d15;
    box-shadow: 0 calc(24 * var(--u)) calc(60 * var(--u)) calc(-30 * var(--u)) rgb(43 29 21 / 45%);
  }

  /* The sprocket holes, in the act's ground colour (drawn, not cut: a mask would re-composite the
     whole strip on every frame of its animation). */
  .strip-holes {
    position: absolute;
    left: 0;
    right: 0;
    height: calc(14 * var(--u));
    background: var(--ground);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='14'%3E%3Crect x='8' y='1' width='18' height='12' rx='3' fill='white'/%3E%3C/svg%3E") 0 0 / calc(34 * var(--u)) 100% space no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='14'%3E%3Crect x='8' y='1' width='18' height='12' rx='3' fill='white'/%3E%3C/svg%3E") 0 0 / calc(34 * var(--u)) 100% space no-repeat;
    pointer-events: none;
  }

  .strip-holes-top {
    top: calc(16 * var(--u));
  }

  .strip-holes-bottom {
    bottom: calc(16 * var(--u));
  }

  /* Edge print: the stock's name and frame numbers, as printed on real film. */
  .strip::before,
  .strip::after {
    position: absolute;
    left: calc(34 * var(--u));
    right: calc(34 * var(--u));
    color: rgb(233 185 120 / 62%);
    font: 500 max(9px, calc(12 * var(--u)))/1 var(--font-code);
    letter-spacing: 0.24em;
    white-space: nowrap;
    overflow: hidden;
  }

  .strip::before {
    content: "CINEVIEW 2.0   ▸ 12   12A   ▸ 13   13A   ▸ 14   14A   ▸ 15   15A   ▸ 16";
    top: calc(36 * var(--u));
  }

  .strip::after {
    content: "CINEVIEW STOCK 400T   ◂ SAFETY FILM   ◂ SCENE 06   ◂ TAKE 1";
    bottom: calc(34 * var(--u));
    text-align: right;
  }

  .strip-frames {
    display: flex;
    gap: calc(22 * var(--u));
  }

  .strip-frame {
    position: relative;
    width: calc((100svh - 580 * var(--u)) * 9 / 16);
    max-width: calc(250 * var(--u));
    aspect-ratio: 9 / 16;
    border-radius: calc(3 * var(--u));
    overflow: hidden;
    background: #120c09;
    box-shadow: 0 0 0 max(1px, calc(1.2 * var(--u))) rgb(233 185 120 / 18%);
  }

  .strip-frame--hero {
    z-index: 2;
    overflow: visible;
  }

  .strip-focus,
  .strip-print,
  .strip-play {
    position: absolute;
    inset: 0;
  }

  .strip-frame img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: calc(6 * var(--u));
  }

  .strip-video {
    position: absolute;
    inset: 0;
    border-radius: calc(6 * var(--u));
    overflow: hidden;
  }

  .strip-video::part(video) {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* Three steps under the strip; each lights over its window (keyframes on the scene, 0-100%). */
  .steps {
    display: flex;
    justify-content: center;
    gap: calc(28 * var(--u));
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .step {
    position: relative;
  }

  .step-face {
    display: inline-flex;
    align-items: center;
    gap: calc(12 * var(--u));
    padding: calc(10 * var(--u)) calc(22 * var(--u));
    border: max(1px, calc(1.4 * var(--u))) solid var(--line-soft);
    border-radius: 999px;
    color: var(--ink-soft);
    font: 500 max(13px, calc(17 * var(--u)))/1 var(--font-ui);
  }

  .step-face b {
    color: var(--clay);
    font: 500 max(14px, calc(19 * var(--u)))/1 var(--font-latin);
  }

  .step-face code {
    font: 400 max(12px, calc(15 * var(--u)))/1 var(--font-code);
  }

  .step-lit {
    position: absolute;
    inset: 0;
  }

  .step-on {
    border-color: var(--clay);
    background: var(--clay);
    color: var(--paper);
  }

  .step-on b {
    color: var(--paper);
  }

  @keyframes step-1 {
    0%, 24% { opacity: 1; }
    27%, 100% { opacity: 0; }
  }

  @keyframes step-2 {
    0%, 27% { opacity: 0; }
    30%, 50% { opacity: 1; }
    53%, 100% { opacity: 0; }
  }

  @keyframes step-3 {
    0%, 54% { opacity: 0; }
    57%, 100% { opacity: 1; }
  }

  /* ---------- Act 8: your own animation, on the same timeline ---------- */

  .act-extend {
    background: var(--ground);
  }

  .extend {
    --m: calc(120 * var(--u));
    display: grid;
    grid-template-columns: minmax(0, 700fr) minmax(0, 820fr);
    align-items: center;
    gap: calc(100 * var(--u));
    height: 100vh;
    height: 100svh;
    padding: calc(130 * var(--u)) var(--m) calc(70 * var(--u));
  }

  .extend-copy {
    display: grid;
    gap: calc(30 * var(--u));
  }

  /* 自己的动画，/ 接上同一条时间线: each half stays whole, so the line breaks only after the comma. */
  .extend-title .nb {
    display: inline-block;
    white-space: nowrap;
  }

  .extend-title {
    max-width: none;
    margin: 0;
    color: var(--ink);
    font: 400 calc(76 * var(--u))/1.2 var(--font-zh);
  }

  .extend-text {
    margin: 0;
    color: var(--ink-soft);
    font: 400 max(16px, calc(22 * var(--u)))/1.85 var(--font-zh);
    text-wrap: pretty;
  }

  :root[lang="en"] .extend-title {
    max-width: none;
    text-wrap: balance;
    font-family: var(--font-latin);
    font-weight: 300;
    letter-spacing: -0.02em;
  }

  :root[lang="en"] .extend-text {
    font-family: var(--font-latin);
    line-height: 1.6;
  }

  /* The code card: the few lines that drive the canvas. */
  .extend-code {
    margin: calc(6 * var(--u)) 0 0;
    padding: calc(26 * var(--u)) calc(30 * var(--u));
    border: max(1px, calc(1.4 * var(--u))) solid var(--line-soft);
    border-radius: calc(18 * var(--u));
    background: var(--surface);
    color: var(--ink);
    font: 400 max(13px, calc(18 * var(--u)))/1.75 var(--font-code);
    white-space: pre-wrap;
  }

  .extend-code .c-k { color: var(--clay); }
  .extend-code .c-f { color: var(--ink); font-weight: 600; }
  .extend-code .c-s { color: var(--ink-soft); }
  .extend-code .c-c { color: var(--ink-soft); font-style: italic; }

  /* ---------- Act 8: a projector made of pixels ---------- */
  .extend-stage {
    position: relative;
    justify-self: end;
    width: min(100%, calc(100svh - 220 * var(--u)));
    aspect-ratio: 1;
    margin: 0;
  }

  .pj-view {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    cursor: grab;
    touch-action: pan-y;
    user-select: none;
    outline: none;
  }

  .pj-view:active {
    cursor: grabbing;
  }

  .pj-view:focus-visible {
    box-shadow: 0 0 0 2px var(--clay);
    border-radius: calc(24 * var(--u));
  }

  /* The particles are drawn on one canvas the size of the act, under its text (hero.js, WebGL);
     the square on the right is where the projector assembles and where it is dragged. */
  .extend {
    position: relative;
  }

  .pj-canvas {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
  }

  .extend-copy,
  .extend-stage {
    position: relative;
    z-index: 1;
  }

  .pj-floor {
    position: absolute;
    left: 50%;
    top: 50%;
    width: calc(760 * var(--u));
    height: calc(150 * var(--u));
    margin: calc(140 * var(--u)) 0 0 calc(-380 * var(--u));
  }

  .pj-shadow {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgb(53 40 32 / 28%), transparent);
  }


  /* ---------- Act 9: lights down, the film begins ---------- */

  .act-cinema {
    background: var(--ground);
  }

  /* One big phone in the middle; when the drag reaches its last scene it shrinks to the left and
     the summary comes in on the right (v1 Scene5Cinema). */
  .cinema {
    --m: calc(120 * var(--u));
    display: grid;
    place-items: center;
    height: 100vh;
    height: 100svh;
    padding: 10px var(--m) 8px;
  }


  .end-title {
    margin: 0;
    color: var(--ink);
    font: 400 calc(76 * var(--u))/1.2 var(--font-zh);
  }

  .end-text {
    margin: 0;
    color: var(--ink-soft);
    font: 400 max(16px, calc(22 * var(--u)))/1.85 var(--font-zh);
    text-wrap: pretty;
  }

  :root[lang="en"] .end-title {
    text-wrap: balance;
    font-family: var(--font-latin);
    font-weight: 300;
    letter-spacing: -0.02em;
  }

  :root[lang="en"] .end-text {
    font-family: var(--font-latin);
    line-height: 1.6;
  }


  /* The closing buttons sit on the dark ground of the last act. The main one is a light pill (the
     night ink) around a deep clay disc, with a faint clay glow; hover brightens it instead of
     deepening it. Fixed colours, so the pill does not flip with the theme as the act scrolls away. */
  .primary.on-dark {
    background: #f3e6d6;
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / 45%),
      0 14px 32px -18px rgb(232 160 122 / 50%);
    color: #1d140f;
  }

  .primary.on-dark .arrow {
    background: var(--clay-deep);
    color: #f3e6d6;
  }

  .primary.on-dark:hover {
    background: var(--paper);
    box-shadow:
      inset 0 1px 0 rgb(255 255 255 / 55%),
      0 14px 36px -16px rgb(232 160 122 / 65%);
  }

  .secondary.on-dark {
    color: var(--ink);
  }

  /* No draw-on here; the dash pattern only left a notch where the outline starts. */
  .secondary.on-dark .outline rect {
    stroke: var(--brass);
    stroke-dasharray: none;
  }

  /* Hover lights the pill faintly and turns the label and the outline light clay. */
  .secondary.on-dark:hover {
    background: rgb(243 230 214 / 8%);
    color: var(--clay);
  }

  .secondary.on-dark:hover .outline rect {
    stroke: var(--clay);
  }

  /* The device: v1's phone (Scene5Cinema.css .phone-mockup), nearly the screen's height, 332:720,
     with a thin amber edge and a warm glow behind it, the one lit thing in the dark room. */
  .device {
    position: relative;
    text-align: center;
  }

  .device-body {
    position: relative;
    margin: 0 auto;
    height: calc(100vh - 64px);
    height: calc(100svh - 64px);
    aspect-ratio: 332 / 720;
    padding: 2.6%;
    border: 1px solid rgb(225 164 91 / 42%);
    border-radius: 13% / 6%;
    background: #0b0a0c;
    box-shadow:
      0 0 16px 3px rgb(225 164 91 / 24%),
      0 30px 90px -30px rgb(225 164 91 / 30%);
  }

  .device-body::before {
    content: "";
    position: absolute;
    inset: -18%;
    z-index: -1;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgb(225 164 91 / 16%), transparent 72%);
    pointer-events: none;
  }

  .device-screen {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 11% / 5%;
    background: #060507;
  }

  .device-hint {
    margin: 10px 0 0;
    color: var(--ink-soft);
    font: 500 max(13px, calc(15 * var(--u)))/1.2 var(--font-ui);
    letter-spacing: 0.06em;
  }

  /* v1 Scene5Cinema: once the last drag scene is reached, the phone moves left (1.1 s) and the
     summary column comes in: title 1.12 s, subtitle 1.42 s, story 1.8 s, ruler 2.2 s. Leaving the
     last scene reverses it. The column sits beside the phone, over the same stage. */
  .cinema {
    position: relative;
  }

  .device {
    transform-origin: 50% 50%;
    transition:
      translate 1.1s cubic-bezier(0.16, 1, 0.3, 1),
      scale 1.1s cubic-bezier(0.16, 1, 0.3, 1);
  }

  .cinema {
    --phone-w: calc((100svh - 64px) * 332 / 720);
    --phone-k: 0.86;
    --split-gap: calc(110 * var(--u));
    --col-w: calc(600 * var(--u));
    --group-w: calc(var(--phone-w) * var(--phone-k) + var(--split-gap) + var(--col-w));
  }

  .is-finished .device {
    translate: calc(var(--phone-w) * var(--phone-k) / 2 - var(--group-w) / 2) 0;
    scale: var(--phone-k);
  }

  .device-summary {
    position: absolute;
    top: 50%;
    left: calc(50% - var(--group-w) / 2 + var(--phone-w) * var(--phone-k) + var(--split-gap));
    display: grid;
    gap: calc(20 * var(--u));
    width: var(--col-w);
    translate: 0 -50%;
    pointer-events: none;
  }

  .is-finished .device-summary {
    pointer-events: auto;
  }

  .device-summary > * {
    margin: 0;
    opacity: 0;
    translate: 0 calc(18 * var(--u));
    transition:
      opacity 0.6s ease,
      translate 0.8s cubic-bezier(0.16, 1, 0.3, 1);
  }

  .is-finished .device-summary > * {
    opacity: 1;
    translate: 0 0;
  }

  .is-finished .summary-title { transition-delay: 1.12s; }
  .is-finished .summary-sub { transition-delay: 1.42s; }
  .is-finished .summary-end { transition-delay: 2.1s; }

  .summary-title {
    margin: 0;
    color: var(--ink);
    font: 400 calc(64 * var(--u))/1.2 var(--font-zh);
    text-wrap: balance;
  }

  .summary-lead {
    color: var(--ink);
    font: 400 max(16px, calc(22 * var(--u)))/1.8 var(--font-zh);
  }


  .is-finished .summary-lead { transition-delay: 1.6s; }

  .summary-sub {
    color: #d8a24a;
    font: 500 max(13px, calc(16 * var(--u)))/1.2 var(--font-ui);
    letter-spacing: 0.14em;
  }




  /* The closing, under the act's copy: a rule, the call to start, and the three ways in. */
  .summary-end {
    display: grid;
    justify-items: start;
    gap: calc(14 * var(--u));
    margin-top: calc(18 * var(--u));
    padding-top: calc(30 * var(--u));
    border-top: max(1px, calc(1.4 * var(--u))) solid var(--line-soft);
  }

  .summary-end .end-title {
    font-size: calc(40 * var(--u));
  }

  .summary-end .end-text {
    font-size: max(15px, calc(19 * var(--u)));
  }

  .summary-end .end-actions {
    justify-content: flex-start;
    margin-top: calc(6 * var(--u));
  }

  :root[lang="en"] .summary-title,
  :root[lang="en"] .summary-lead {
    font-family: var(--font-latin);
  }

  @media (prefers-reduced-motion: reduce) {
    .device,
    .device-summary > * {
      transition: none;
    }
  }

  /* Narrow screens: no room beside the phone, so when the copy comes in the phone shrinks to the
     top and the copy takes the width below it, in fixed sizes (u is too small there). */
  @media (max-width: 760px) {
    .cinema {
      --phone-k: 0.4;
      --phone-top: 66px;
    }

    .is-finished .device {
      translate: 0 calc(-50svh + (100svh - 64px) * var(--phone-k) / 2 + var(--phone-top));
    }

    .device-summary {
      top: calc(var(--phone-top) + (100svh - 64px) * var(--phone-k) + 22px);
      left: 20px;
      right: 20px;
      width: auto;
      gap: 12px;
      translate: none;
    }

    .summary-title {
      font-size: 26px;
    }

    .summary-sub {
      font-size: 12px;
    }

    .summary-lead {
      font-size: 15px;
      line-height: 1.7;
    }

    .summary-end {
      gap: 10px;
      margin-top: 6px;
      padding-top: 16px;
    }

    .summary-end .end-title {
      font-size: 21px;
    }

    .summary-end .end-text {
      font-size: 14px;
    }

    .summary-end .end-actions {
      gap: 10px;
    }
  }

  @media (max-width: 760px) and (max-height: 720px) {
    .cinema {
      --phone-k: 0.3;
    }

    .summary-lead {
      display: none;
    }
  }

  /* The end: a short closing screen with the ways in. */
  .end {
    --m: calc(120 * var(--u));
    display: grid;
    align-content: center;
    justify-items: center;
    gap: calc(30 * var(--u));
    min-height: 100vh;
    min-height: 100svh;
    padding: calc(120 * var(--u)) var(--m);
    text-align: center;
  }

  .end-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: calc(18 * var(--u));
    margin-top: calc(12 * var(--u));
  }

  /* Tablets in portrait: the copy above the drawing, both on the page margins. */
  @media (max-width: 960px) and (orientation: portrait) {
    .time {
      grid-template-columns: 1fr;
      align-content: center;
      gap: 40px;
    }

    /* Full width when the screen is tall enough; otherwise as wide as the height left under the copy. */
    .time-figure {
      --copy-h: 300px;
      justify-self: start;
    }

    .method {
      grid-template-columns: 1fr;
      gap: 40px;
    }

    .extend {
      grid-template-columns: 1fr;
      align-content: center;
      gap: 32px;
      height: auto;
      min-height: 100svh;
    }

    .extend-stage {
      justify-self: center;
      width: min(100%, 60svh);
    }
  }

  /* ---------- Narrow screens: keep the composition legible; mobile design is out of scope ---------- */

  @media (max-width: 760px) {
    .hero {
      --m: 20px;
      min-height: 560px;
    }

    .bar {
      --m: 20px;
      gap: 14px;
      padding-block: 14px;
    }

    :root.is-scrolled .bar {
      padding-block: 10px;
    }

    .nav {
      display: none;
    }

    .stage {
      top: 96px;
      bottom: 40px;
    }

    .headline {
      font-size: calc(15vw * var(--title-scale, 1));
    }

    .hero[data-lang="en"] .headline {
      font-size: calc(11.4vw * var(--title-scale, 1));
    }

    .lede {
      margin-top: 36px;
      padding-left: var(--m) !important;
    }

    .lede {
      justify-content: flex-start;
    }

    .reel {
      right: -120px;
      bottom: calc(var(--reel-tail) - 200px);
      width: 440px;
      height: 440px;
    }

    .desc .line {
      white-space: normal;
    }

    .actions {
      gap: 20px;
      margin-top: 24px;
    }

    .primary {
      padding-left: 22px;
      gap: 14px;
    }

    .secondary {
      padding-inline: 20px;
    }

    .time {
      grid-template-columns: 1fr;
      align-content: center;
      gap: 28px;
      padding: 88px 20px 40px;
    }

    .time-title,
    :root[lang="en"] .time-title {
      max-width: none;
      font-size: 34px;
    }

    .time-figure {
      --copy-h: 290px;
      justify-self: start;
    }

    .method {
      grid-template-columns: 1fr;
      gap: 28px;
      padding: 88px 20px 48px;
    }

    .method-title {
      font-size: 32px;
    }

    .cards-act,
    .material {
      grid-template-columns: 1fr;
      align-content: center;
      gap: 24px;
      padding: 88px 20px 40px;
    }

    .material-copy {
      grid-template-columns: 1fr;
      gap: 14px;
    }

    .cards-title,
    .row-title,
    .material-title {
      max-width: none;
      font-size: 32px;
    }

    .method-sketch {
      display: none;
    }

    .deck {
      justify-self: stretch;
      width: 100%;
    }

    .deck-card {
      padding: 14px 16px 16px;
    }

    .deck-body {
      grid-template-columns: 1fr;
    }

    .deck-thumb {
      display: none;
    }

    .row-lead {
      width: 300px;
      margin-left: 20px;
    }

    .row-panel {
      width: 250px;
      height: auto;
      gap: 12px;
      margin-right: 14px;
      padding: 20px;
      border-radius: 16px;
    }

    .row-panel:last-child {
      margin-right: 20px;
    }

    .row-name {
      font-size: 20px;
    }

    .row-desc {
      font-size: 14px;
    }

    .row-use {
      font-size: 12px;
    }

    .rp-meta {
      grid-template-columns: 52px 1fr;
    }



    /* On a phone the reel is a faint watermark. */
    .act-bridge .reel {
      opacity: 0.32;
    }

    .extend {
      padding: 88px 20px 40px;
    }

    .extend-title {
      font-size: 32px;
    }

    .extend-title .nb {
      white-space: normal;
    }

    .extend-code {
      font-size: 12px;
    }

    .card-b,
    .card-c {
      margin-left: 0;
    }
  }
