/* Scroll film: layout and lettering.
   The runtime (film.js) puts these on every live scene:
     --u   screens scrolled since the scene began      --p    0 to 1 across the whole scene
     --in  0 to 1 while the scene arrives              --out  0 to 1 while the next one arrives
   and on every beat: the classes "on" / "past", and --t, 0 to 1 across the beat.
   Scenes are written by the templates in ../templates.ts; each gets the class t-<template>. */

@property --s { syntax: '<percentage>'; inherits: true; initial-value: 50%; }

:root {
  --pink: #ff5cab;
  --hot: #ff2d8a;
  --blush: #ffd3e6;
  --ink: #160611;
  --veil: 8, 4, 14;
  /* The accent chrome, top to bottom: sky, horizon, floor. A look changes these. */
  --a1: #ffe6f2; --a2: #ff9dcb; --a3: #ff3a93; --a4: #6d0740; --a5: #c4237b; --a6: #ffaad3; --a7: #ffd3e8; --a-edge: #5b0a37;
  --wide: "Anybody", "Arial Black", "Helvetica Neue", Impact, sans-serif;
  --serif: "Instrument Serif", Didot, "Bodoni 72", Georgia, serif;
  --script: "Pinyon Script", "Snell Roundhand", cursive;
  --tape: "VT323", "Andale Mono", Menlo, monospace;
  --snap: cubic-bezier(.2, 1.5, .35, 1);
  --glide: cubic-bezier(.16, 1, .3, 1);
}

/* Looks: one word in the cut swaps the palette for the whole film. */
.look-noir {
  --pink: #ff3b3b; --hot: #e01212; --blush: #f1ece4; --veil: 6, 6, 6;
  --a1: #fff1ee; --a2: #ff8f84; --a3: #e3201c; --a4: #3d0503; --a5: #9c1512; --a6: #ff9c92; --a7: #ffe0db; --a-edge: #3d0503;
}
/* Blood: the red of noir with the pictures left in their own colours. */
.look-blood {
  --pink: #ff3b3b; --hot: #e01212; --blush: #f1ece4; --ink: #120404; --veil: 10, 2, 2;
  --a1: #fff1ee; --a2: #ff8f84; --a3: #e3201c; --a4: #3d0503; --a5: #9c1512; --a6: #ff9c92; --a7: #ffe0db; --a-edge: #3d0503;
}
.look-acid {
  --pink: #c8ff3d; --hot: #9dff00; --blush: #efffc4; --ink: #0c1400; --veil: 4, 8, 2;
  --a1: #f7ffe0; --a2: #d6ff6b; --a3: #8fe800; --a4: #1c3300; --a5: #5c9a00; --a6: #dcff8a; --a7: #f1ffcf; --a-edge: #1c3300;
}
.look-noir #grain { opacity: calc(.24 * var(--f-grain-amount, 1)); }
.look-noir .plate > img, .look-noir .plate > video { filter: grayscale(.85) contrast(1.12); }

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Nothing on screen but the film: no scrollbar, no selection. */
html { background: #000; scrollbar-width: none; }
html::-webkit-scrollbar { display: none; }
body {
  background: #000;
  color: #fff;
  font-family: var(--wide);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  cursor: default;
  -webkit-tap-highlight-color: transparent;
  /* A tap turns the page; two quick ones must not zoom it. */
  touch-action: manipulation;
}

#film { position: fixed; inset: 0; overflow: hidden; background: #000; }

/* ------------------------------------------------------------ scenes and how they arrive */

.scene {
  --in: 1; --out: 0; --u: 0; --p: 0; --rise: 0; --leave: 0;
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #000;
  display: none;
  transform: translate3d(0, calc(var(--rise) * (1 - var(--in)) * 100% - var(--leave) * var(--out) * 100%), 0);
}
.scene.live, .scene.warm, #film.measuring .scene { display: block; }
/* A scene that is loading ahead of the playhead waits underneath the one on screen. Parking it
   on top and hiding it makes Chrome treat the clip below as covered, and the clip goes black. */
.scene.warm { z-index: 0 !important; }
#film.measuring .scene { visibility: hidden; transform: none !important; }
#film.measuring * { transition: none !important; animation: none !important; }

.scene[data-in="push"], .scene[data-in="cover"] { --rise: 1; }
.scene[data-out="push"], .scene[data-out="reveal"] { --leave: 1; }
.scene[data-in="reveal"] { clip-path: inset(calc((1 - var(--in)) * 100%) 0 0 0); }
.scene[data-in="iris"] { clip-path: circle(calc(var(--in) * 130%) at 50% 50%); }
.scene[data-in="wipe"] { clip-path: polygon(0 0, calc(var(--in) * 135%) 0, calc(var(--in) * 135% - 35%) 100%, 0 100%); }
.scene[data-in="fade"] { opacity: var(--in); }
.scene[data-in="zoom"] { opacity: min(1, var(--in) * 2.5); }
.scene[data-in="zoom"] > .plate { scale: calc(1.28 - var(--in) * .28); }
/* A cut that rhymes: two pictures share a shape, and the film goes through it. It happens
   in two movements. First the new picture shows only inside a disc that lies exactly over the
   shape in the old picture, and that disc travels and grows until the new picture's own shape
   is where and as big as it really is. Then the disc opens to the whole screen. All the while
   the old picture pushes in toward the shape. The runtime works out where the two shapes are
   on this screen (--ax --ay for the old one, --bx --by --br for the new, --k for how much
   smaller the old one is). Without them it is an iris from the middle. */
.scene[data-in="match"] {
  --a: min(1, calc(var(--in) / .55));
  --e: calc(var(--a) * var(--a) * (3 - 2 * var(--a)));
  --b: max(0, calc((var(--in) - .45) / .55));
  --size: calc(var(--k, 1) + (1 - var(--k, 1)) * var(--e));
  --dx: calc((var(--ax, 0px) - var(--bx, 0px)) * (1 - var(--e)));
  --dy: calc((var(--ay, 0px) - var(--by, 0px)) * (1 - var(--e)));
  clip-path: circle(calc(var(--br, 12vmin) * var(--size) + var(--b) * var(--b) * 175vmax) at calc(var(--bx, 50%) + var(--dx)) calc(var(--by, 50%) + var(--dy)));
}
.scene[data-in="match"] > .plate { transform-origin: var(--bx, 50%) var(--by, 50%); transform: translate3d(var(--dx), var(--dy), 0) scale(var(--size)); }
.scene[data-out="match"] > .plate { transform-origin: var(--mx, 50%) var(--my, 50%); scale: calc(1 + var(--out) * var(--f-match-push, .9)); }
/* Words from two scenes in one disc are unreadable: the old ones go as the cut starts, and the
   new ones come once the disc is opening. */
.scene[data-out="match"] > :not(.plate) { filter: opacity(calc(1 - min(1, var(--out) * 4))); }
.scene[data-in="match"] > :not(.plate) { filter: opacity(min(1, calc(var(--b) * 2))); }
/* A four-point sparkle that opens until it has swallowed the screen. */
.scene[data-in="star"] {
  --r: calc(var(--in) * var(--in) * 330vmax);
  --w: calc(var(--r) * .13);
  clip-path: polygon(
    50% calc(50% - var(--r)), calc(50% + var(--w)) calc(50% - var(--w)),
    calc(50% + var(--r)) 50%, calc(50% + var(--w)) calc(50% + var(--w)),
    50% calc(50% + var(--r)), calc(50% - var(--w)) calc(50% + var(--w)),
    calc(50% - var(--r)) 50%, calc(50% - var(--w)) calc(50% - var(--w)));
}
/* Six bars that open like a blind. */
.scene[data-in="blinds"] {
  --h: calc(var(--in) * 16.8%);
  clip-path: polygon(
    0 0, 100% 0, 100% var(--h), 0 var(--h),
    0 16.667%, 100% 16.667%, 100% calc(16.667% + var(--h)), 0 calc(16.667% + var(--h)),
    0 33.333%, 100% 33.333%, 100% calc(33.333% + var(--h)), 0 calc(33.333% + var(--h)),
    0 50%, 100% 50%, 100% calc(50% + var(--h)), 0 calc(50% + var(--h)),
    0 66.667%, 100% 66.667%, 100% calc(66.667% + var(--h)), 0 calc(66.667% + var(--h)),
    0 83.333%, 100% 83.333%, 100% calc(83.333% + var(--h)), 0 calc(83.333% + var(--h)));
}

/* The scene underneath sinks into the dark while another one covers it. */
.scene[data-out="cover"]::after, .scene[data-out="wipe"]::after, .scene[data-out="iris"]::after,
.scene[data-out="star"]::after, .scene[data-out="blinds"]::after {
  content: "";
  position: absolute; inset: 0; z-index: 80;
  background: #000;
  opacity: calc(var(--out) * .6);
  pointer-events: none;
}

.scene[data-in="flash"]::before {
  content: "";
  position: absolute; inset: 0; z-index: 90;
  background: #fff;
  opacity: 0;
  pointer-events: none;
}
.scene[data-in="flash"].live::before { animation: flash .45s ease-out both; }
@keyframes flash { from { opacity: .95; } to { opacity: 0; } }

/* ------------------------------------------------------------ pictures */

.plate { position: absolute; inset: 0; overflow: hidden; }
.plate > img, .plate > video {
  position: absolute; inset: 0;
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
}
.plate[data-at] { visibility: hidden; }
.plate[data-at].on { visibility: visible; }

/* A slow push-out on every still, so nothing ever sits dead. */
.plate.kb > * { transform: scale(calc(1 + (.1 - var(--p) * .07) * var(--f-drift-amount, 1))); will-change: transform; }
.plate.kb[data-at] > * { transform: scale(calc(1 + (.16 - var(--t, 0) * .1) * var(--f-drift-amount, 1))); }
.off-drift .plate.kb:not(.tight) > * { transform: none; }
/* Pushed in on the face, for a hit. */
.plate.tight > * { transform: scale(calc(1.5 - var(--p) * .12)); transform-origin: var(--ox, 50%) var(--oy, 50%); }
/* --dim is set on a picture that is dark already, so it is darkened less. */
.plate.dim > * { filter: brightness(var(--dim, .5)) saturate(1.15); }
.plate.dim.deep > * { filter: brightness(calc(var(--dim, .5) * .6)) saturate(.5); }

/* A shot that has not been made yet: a card in a colour of its own, carrying its description.
   It lets a story be read in its real format before any picture exists. */
.rough {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: 12vmin;
  background:
    radial-gradient(90% 70% at 30% 25%, hsl(var(--hue) 55% 42% / .9), hsl(var(--hue) 55% 42% / 0) 70%),
    radial-gradient(80% 80% at 80% 85%, hsl(calc(var(--hue) + 50) 60% 30% / .9), hsl(calc(var(--hue) + 50) 60% 30% / 0) 70%),
    hsl(calc(var(--hue) - 30) 35% 12%);
}
.rough p {
  max-width: 30em;
  color: rgba(255, 255, 255, .88);
  font: 400 clamp(15px, 1.9vw, 28px)/1.34 var(--tape);
  letter-spacing: .04em;
  text-align: center;
  text-wrap: balance;
}
.rough b { display: block; margin-bottom: .6em; color: rgba(255, 255, 255, .9); font-weight: 400; letter-spacing: .3em; text-transform: uppercase; }
/* Whether the shot will move or hold: a film read as cards is judged on that too. */
.rough .kind {
  display: inline-block; margin-right: .7em; padding: .08em .5em .04em;
  border: 1px solid currentColor; border-radius: .2em;
  font-style: normal; letter-spacing: .2em;
}
.rough.clip .kind { border-color: var(--hot); background: var(--hot); color: #000; }
/* A card that will be a clip is never quite still. */
.rough.clip::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(105deg, rgba(255, 255, 255, 0) 42%, rgba(255, 255, 255, .08) 50%, rgba(255, 255, 255, 0) 58%);
  background-size: 260% 100%;
  animation: sheen 2.6s linear infinite;
  pointer-events: none;
}
@keyframes sheen { from { background-position: 130% 0; } to { background-position: -130% 0; } }
/* Inside the letters of a title a card's words would only be noise: they wait until the camera is through. */
.t-title .rough p { opacity: var(--z, 0); }
.rough.tall { position: relative; inset: auto; width: 100%; align-items: start; padding-top: 30vh; }
.duet .plate > .rough { inset: 0; width: 100%; height: 100%; transform: none !important; }
/* In a split screen each card keeps its words on its own side of the seam. */
.duet .side-a > .rough { padding: 12vmin 56vw 12vmin 7vw; }
.duet .side-b > .rough { padding: 12vmin 6vw 12vmin 60vw; }
.duet .rough p { text-align: left; }

.shade { position: absolute; inset: 0; pointer-events: none; }
.shade.b { background: linear-gradient(180deg, rgba(5, 3, 9, 0) 48%, rgba(5, 3, 9, .74) 100%); }

/* Two-colour treatment: shadows take one colour, highlights the other. */
.duo > img, .duo > video { filter: grayscale(1) contrast(1.35) brightness(1.08); }
.duo::before, .duo::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.duo::before { background: var(--lo); mix-blend-mode: lighten; }
.duo::after { background: var(--hi); mix-blend-mode: multiply; }
.duo.blue { --lo: #101a55; --hi: #a8e8ff; }
.duo.rose { --lo: #2b0638; --hi: #ff7dc0; }
.duo.gold { --lo: #4b0e34; --hi: #ffd668; }
.duo.lime { --lo: #06210f; --hi: #c8ff3d; }
.duo.mono { --lo: #0b0b0b; --hi: #f1ece4; }

/* ------------------------------------------------------------ lettering */

.label {
  font: 700 clamp(10px, .95vw, 16px)/1.25 var(--wide);
  font-stretch: 118%;
  letter-spacing: .26em;
  text-transform: uppercase;
}
.serif { font-family: var(--serif); font-style: italic; font-weight: 400; letter-spacing: -.012em; line-height: .98; }
.script { font-family: var(--script); font-weight: 400; line-height: 1.05; }
.osd {
  font-family: var(--tape);
  font-weight: 400;
  font-size: clamp(16px, 1.9vw, 34px);
  line-height: 1;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-shadow: .05em 0 rgba(255, 40, 110, .6), -.05em 0 rgba(60, 220, 255, .6), 0 0 .45em rgba(255, 255, 255, .35);
}

/* A fitted line is sized by the runtime to span its wall exactly. */
.fit { display: block; width: max-content; white-space: nowrap; }
.wall {
  position: absolute; left: 50%; top: 50%;
  width: 95vw;
  display: flex; flex-direction: column; align-items: center;
  gap: 1vw;
  transform: translate(-50%, -50%) scaleY(var(--sy, 1));
  pointer-events: none;
}
.wall .fit {
  font-family: var(--wide);
  font-weight: 900;
  font-stretch: 150%;
  line-height: .74;
  letter-spacing: -.04em;
  text-transform: uppercase;
  text-box: trim-both cap alphabetic;
}
.wall.cond { gap: 1.3vw; }
.wall.cond .fit { font-stretch: 56%; font-weight: 850; letter-spacing: -.012em; }
.wall[data-at] { visibility: hidden; }
.wall[data-at].on { visibility: visible; }
.wall.word { width: 94vw; }
/* The last word of a word cut starts squat and grows as the reader scrolls. */
.wall.grow { transform: translate(-50%, -50%) scaleY(calc(var(--sy, 1) * (.42 + min(var(--t, 0), .42) * 1.38))); }

.wall.hit.on { animation: hit .2s var(--snap) both; }
@keyframes hit { from { scale: 1.3; } to { scale: 1; } }

.wall.lines .fit { opacity: 0; transition: transform .5s var(--glide), opacity .1s; }
.wall.lines .fit:nth-child(odd) { transform: translateX(-14vw); }
.wall.lines .fit:nth-child(even) { transform: translateX(14vw); }
.wall.lines.on .fit { opacity: 1; transform: none; }
.wall.lines.on .fit:nth-child(2) { transition-delay: .06s; }
.wall.lines.on .fit:nth-child(3) { transition-delay: .12s; }
.wall.lines.on .fit:nth-child(4) { transition-delay: .18s; }
.wall.lines.on .fit:nth-child(5) { transition-delay: .24s; }

/* ---- lettering styles ---- */

.diff { mix-blend-mode: difference; color: #fff; }
.solid { color: #fff; text-shadow: 0 .03em .22em rgba(0, 0, 0, .5); }
.outline { color: transparent; -webkit-text-stroke: max(2px, .018em) #fff; }
/* The feature "lettering": giant lines keep the shape of letters. A condensed line is set less narrow
   (the runtime also stretches it less), words stand apart, and an outlined letter has a smoked body
   and a heavier edge, so that it reads as a letter and not as a tangle of lines. */
:root:not(.off-lettering) .wall .fit { word-spacing: .14em; }
:root:not(.off-lettering) .wall.cond .fit { font-stretch: 74%; letter-spacing: 0; }
:root:not(.off-lettering) .outline { color: rgba(14, 8, 18, .4); -webkit-text-stroke: max(2.5px, .028em) #fff; letter-spacing: .012em; }

/* Chrome: sky above a hard horizon, a warm floor below it. */
.chrome {
  color: transparent;
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(180deg, #fff 0%, #f1f7ff 12%, #a9c0e8 33%, #46598f 47%, #0c1636 50.5%, #76506c 53%, #f0bcd5 70%, #fff 87%, #dde7fb 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-stroke: .011em rgba(255, 255, 255, .9);
  filter: drop-shadow(0 .014em 0 var(--edge, #1b1030)) drop-shadow(0 .014em 0 var(--edge, #1b1030)) drop-shadow(0 .05em .07em rgba(0, 0, 0, .55));
}
.chrome.pink {
  --edge: var(--a-edge);
  background-image: linear-gradient(180deg, #fff 0%, var(--a1) 12%, var(--a2) 32%, var(--a3) 46.5%, var(--a4) 50.5%, var(--a5) 53%, var(--a6) 70%, #fff 87%, var(--a7) 100%);
}
/* Red chrome, whatever the look. */
.chrome.red {
  --edge: #3d0503;
  background-image: linear-gradient(180deg, #fff 0%, #fff1ee 12%, #ff8f84 32%, #e3201c 46.5%, #3d0503 50.5%, #9c1512 53%, #ff9c92 70%, #fff 87%, #ffe0db 100%);
}
.chrome.ice {
  --edge: #06254f;
  background-image: linear-gradient(180deg, #fff 0%, #e9fbff 12%, #8fdcff 32%, #2a8fd6 46.5%, #06254f 50.5%, #3f6f9a 53%, #b9ecff 70%, #fff 87%, #d6f3ff 100%);
}
.chrome.peach {
  --edge: #5a160f;
  background-image: linear-gradient(180deg, #fff 0%, #fff0e2 12%, #ffc08f 32%, #ff7a52 46.5%, #6a1a12 50.5%, #c2503a 53%, #ffc7a6 70%, #fff 87%, #ffe1cf 100%);
}
.chrome.khaki {
  --edge: #23220a;
  background-image: linear-gradient(180deg, #fff 0%, #fbf9e1 12%, #d9d48c 32%, #8d8a3f 46.5%, #23220a 50.5%, #6f6a36 53%, #e3deaa 70%, #fff 87%, #efecc9 100%);
}
.chrome.gold {
  --edge: #3a1f02;
  background-image: linear-gradient(180deg, #fff 0%, #fff6d6 12%, #ffd35c 32%, #d98a00 46.5%, #3a1f02 50.5%, #9a5a06 53%, #ffd978 70%, #fff 87%, #ffeebd 100%);
}

/* A tube of light. */
.neon {
  color: #fff;
  -webkit-text-stroke: 0;
  text-shadow: 0 0 .04em #fff, 0 0 .1em var(--pink), 0 0 .26em var(--hot), 0 0 .6em var(--hot);
  animation: flicker 3.2s infinite;
}
@keyframes flicker { 0%, 91%, 94%, 100% { opacity: 1; } 92% { opacity: .55; } 93% { opacity: .9; } }
.wall .fit.neon { font-weight: 300; font-stretch: 125%; letter-spacing: -.01em; }

/* Clear crystal: almost nothing inside, bright edges, a streak of light across. */
.glass {
  color: transparent;
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(115deg, rgba(255, 255, 255, .5) 0%, rgba(255, 255, 255, .06) 22%, rgba(255, 255, 255, .34) 40%, rgba(255, 255, 255, .04) 58%, rgba(255, 255, 255, .6) 78%, rgba(255, 255, 255, .1) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-stroke: .014em rgba(255, 255, 255, .95);
  filter: drop-shadow(0 0 .03em rgba(255, 255, 255, .8)) drop-shadow(0 .04em .12em rgba(0, 0, 0, .35));
}

/* A die-cut sticker. */
.sticker {
  color: #fff;
  -webkit-text-stroke: .05em var(--ink);
  paint-order: stroke fill;
  filter: drop-shadow(.035em .045em 0 var(--hot)) drop-shadow(0 .05em .1em rgba(0, 0, 0, .4));
}

/* A fashion cover. */
.wall .fit.vogue {
  font-family: "Bodoni Moda", Didot, "Bodoni 72", serif;
  font-style: italic;
  font-weight: 800;
  font-stretch: 100%;
  letter-spacing: -.045em;
  text-transform: none;
  text-box: trim-both cap alphabetic;
  line-height: .8;
  color: #fff;
  text-shadow: 0 .02em .18em rgba(0, 0, 0, .45);
}

/* Fat rounded letters with a gel shine. */
.wall .fit.bubbletype {
  font-family: "Bagel Fat One", "Arial Rounded MT Bold", var(--wide);
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: -.02em;
  color: transparent;
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(180deg, #fff 0%, var(--a1) 18%, var(--a2) 44%, var(--a3) 58%, var(--a2) 80%, var(--a1) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-stroke: .02em rgba(255, 255, 255, .85);
  filter: drop-shadow(0 .03em 0 var(--a-edge)) drop-shadow(0 .07em .1em rgba(0, 0, 0, .45));
}

.wall .fit.gothic {
  font-family: "Pirata One", "Luminari", serif;
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: -.01em;
  text-transform: none;
  text-box: trim-both cap alphabetic;
  color: #fff;
  text-shadow: 0 .02em 0 var(--hot), 0 .05em .2em rgba(0, 0, 0, .5);
}

.wall .fit.marker {
  font-family: "Permanent Marker", "Marker Felt", cursive;
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: 0;
  rotate: -3deg;
  color: #fff;
  text-shadow: .02em .03em 0 var(--hot);
}

.wall .fit.lcd, .wall.lcd .fit {
  font-family: var(--tape);
  font-weight: 400;
  font-stretch: 100%;
  letter-spacing: 0;
  color: var(--blush);
  text-shadow: 0 0 .04em #fff, 0 0 .14em var(--hot), 0 0 .5em var(--hot);
}

.spark {
  position: absolute;
  width: var(--z, 3vw); height: var(--z, 3vw);
  min-width: 14px; min-height: 14px;
  fill: #fff;
  filter: drop-shadow(0 0 .35vw #fff) drop-shadow(0 0 1vw var(--pink));
  transform: scale(0);
  pointer-events: none;
}
/* A sparkle glints three times as its lettering arrives and then goes: one that never stops
   takes the eye off the picture. */
.scene.live .spark { animation: twinkle 2.6s var(--d, 0s) 3 ease-in-out both; }
.scene [data-at]:not(.on) .spark { animation: none; }
@keyframes twinkle {
  0%, 100% { transform: scale(0) rotate(0deg); }
  50% { transform: scale(.8) rotate(45deg); }
}
.glints { position: absolute; inset: 0; visibility: hidden; }
.glints.on { visibility: visible; }

/* ------------------------------------------------------------ beats */

.fx { opacity: 0; transition: opacity .28s ease, transform .55s var(--glide); }
.fx.on { opacity: 1; }
.fx.rise:not(.on) { transform: translate3d(0, 2.4vh, 0); }
.fx.rise.past { transform: translate3d(0, -2.4vh, 0); }
.fx.slam:not(.on) { transform: scale(1.5); }
.fx.slam.on { transition: opacity .08s linear, transform .26s var(--snap); }
.fx.slam.past { transform: scale(.92); transition: opacity .1s linear, transform .1s; }
.fx.pop:not(.on) { transform: translate3d(0, .6em, 0) scale(.6); }
.fx.pop.on { transition: opacity .1s linear, transform .32s var(--snap); }

[data-split] .w {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, .45em, 0) rotate(2deg);
  transition: opacity .3s, transform .6s var(--glide);
  transition-delay: calc(var(--i) * 36ms);
}
.on[data-split] .w, .on [data-split] .w { opacity: 1; transform: none; }

.narr {
  position: absolute; left: 4.5vw; bottom: 9vh;
  max-width: 64vw;
  text-wrap: balance;
  font-size: clamp(28px, 4.7vw, 96px);
  text-shadow: 0 .05em .35em rgba(0, 0, 0, .6);
}
.narr.high { bottom: 25vh; }
.narr.centre { left: 50%; bottom: 7vh; width: 86vw; max-width: none; translate: -50% 0; text-align: center; font-size: clamp(24px, 3.6vw, 72px); }
/* A second line of narration goes in a top corner, on the side the face is not. */
.narr.two { bottom: auto; top: 2.2vw; max-width: 56vw; }
.narr.two.to-left { left: 2.6vw; }
.narr.two.to-right { left: auto; right: 2.6vw; text-align: right; }
.voice { position: absolute; left: 50%; top: 3.5vh; translate: -50% 0; padding: .5em 1.2em .45em; border-radius: 2em; background: #000; z-index: 3; }

/* ------------------------------------------------------------ speech */

.bubble, .line {
  --tint: 255, 150, 202;
  position: absolute;
  max-width: min(36vw, 22em);
  padding: .5em .95em .6em;
  border-radius: 1.3em;
  font: 640 clamp(17px, 2.15vw, 38px)/1.1 var(--wide);
  font-stretch: 84%;
  letter-spacing: -.012em;
  color: var(--ink);
  background:
    radial-gradient(130% 95% at 50% -32%, rgba(255, 255, 255, .92) 0 46%, rgba(255, 255, 255, 0) 47%),
    linear-gradient(180deg, rgba(var(--tint), .97), rgba(var(--tint), .88));
  box-shadow:
    inset 0 0 0 .08em rgba(255, 255, 255, .75),
    inset 0 -.5em .7em rgba(255, 255, 255, .42),
    0 .3em 1.1em rgba(0, 0, 0, .42),
    0 0 0 .05em rgba(0, 0, 0, .28);
  z-index: 6;
}
/* The name tag. */
.bubble::before, .line::before {
  content: attr(data-name);
  position: absolute; left: 1.05em; top: -1.5em;
  padding: .3em .8em .28em;
  border-radius: 2em;
  background: #000;
  color: #fff;
  font: 800 .4em/1 var(--wide);
  font-stretch: 125%;
  letter-spacing: .22em;
  text-transform: uppercase;
  white-space: nowrap;
}
/* The tail. */
.bubble::after, .line::after {
  content: "";
  position: absolute; left: 1.5em; bottom: -.34em;
  width: .9em; height: .9em;
  background: rgb(var(--tint));
  border-radius: 0 0 .2em 0;
  transform: rotate(45deg) skew(12deg, 12deg);
  box-shadow: .06em .06em 0 rgba(0, 0, 0, .22);
  z-index: -1;
}
/* Heard through a mask or a wall. */
.mumble { font-style: italic; letter-spacing: .05em; color: rgba(22, 6, 17, .6); }

/* A bubble on its own goes to the side the face is not. */
.bubble.to-left { left: 9vw; top: 45vh; transform-origin: 0 100%; }
.bubble.to-right { right: 9vw; top: 45vh; transform-origin: 100% 100%; }
.bubble.to-right::before, .line[data-seat="right"]::before { left: auto; right: 1.05em; }
.bubble.to-right::after, .line[data-seat="right"]::after { left: auto; right: 1.5em; }
.bubble.low { left: 6vw; top: auto; bottom: 9vh; transform-origin: 0 100%; }
.bubble.centre { left: 50%; top: 44vh; translate: -50% 0; max-width: 70vw; transform-origin: 50% 100%; font-size: clamp(20px, 2.9vw, 52px); }

.talk { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
.line {
  bottom: 9.5vh;
  opacity: 0;
  transform: translate3d(0, .6em, 0) scale(.6);
  transition: opacity .1s linear, transform .32s var(--snap), filter .3s;
}
.line.on { opacity: 1; transform: none; }
.line.on.cool { transform: scale(.84); filter: saturate(.55) brightness(.9); opacity: .88; }
.line.past { opacity: 0; transform: translate3d(0, -1.1em, 0) scale(.8); transition: opacity .16s linear, transform .3s ease-in; }
/* Each voice keeps its seat for the whole scene. */
.line[data-seat="left"] { left: 4vw; transform-origin: 0 100%; }
.line[data-seat="right"] { right: 4vw; transform-origin: 100% 100%; }
.line[data-seat="mid"] { left: 50%; bottom: 31vh; translate: -50% 0; transform-origin: 50% 100%; }
.line[data-seat="top"] { left: 50%; bottom: auto; top: 16vh; translate: -50% 0; transform-origin: 50% 0; }
/* A stage direction is not spoken, so it gets no bubble. */
.line.stage {
  left: 50%; bottom: auto; top: 27vh; translate: -50% 0;
  max-width: 80vw;
  padding: 0;
  background: none; box-shadow: none;
  color: #fff;
  font: italic 400 clamp(26px, 4.4vw, 88px)/1 var(--serif);
  text-align: center;
  text-shadow: 0 .05em .35em rgba(0, 0, 0, .6);
  white-space: nowrap;
}
.line.stage::before, .line.stage::after { content: none; }

/* ------------------------------------------------------------ how a line is said */

/* A line can carry a delivery (data-as), and the bubble performs it the way a letterer would.
   Some deliveries are drawn as an outline stretched behind the words, in the speaker's colour;
   the plain bubble underneath is then switched off. */
:root:not(.off-deliveries) .shape {
  position: absolute; left: -.8em; top: -.62em;
  width: calc(100% + 1.6em); height: calc(100% + 1.24em);
  overflow: visible;
  z-index: -1;
  fill: rgb(var(--tint)); stroke: #000; stroke-width: 3px; stroke-linejoin: round; paint-order: stroke;
  filter: drop-shadow(0 .28em .5em rgba(0, 0, 0, .45));
}
:root:not(.off-deliveries) .shape * { vector-effect: non-scaling-stroke; }

/* Shouted: a burst, heavy capitals, a quick shake, and no tail. */
:root:not(.off-deliveries) .bubble[data-as="shout"], :root:not(.off-deliveries) .line[data-as="shout"] {
  padding: .72em 1.3em .78em;
  background: none; box-shadow: none;
  font-weight: 900; font-stretch: 104%; font-size: clamp(20px, 2.6vw, 46px);
  letter-spacing: -.01em; text-transform: uppercase;
  rotate: -2deg;
}
:root:not(.off-deliveries) .bubble[data-as="shout"]::after, :root:not(.off-deliveries) .line[data-as="shout"]::after { display: none; }
:root:not(.off-deliveries) .bubble[data-as="shout"].on, :root:not(.off-deliveries) .line[data-as="shout"].on { animation: shake .36s linear; }
@keyframes shake {
  0% { rotate: -7deg; scale: 1.2; }
  35% { rotate: 2deg; scale: 1; }
  65% { rotate: -4deg; }
  100% { rotate: -2deg; }
}
/* It lands on the picture too: one short jolt. */
:root:not(.off-deliveries) .scene.jolt .plate > * { animation: jolt .36s var(--glide); }
@keyframes jolt {
  0% { scale: 1.07; translate: .5vw -.4vh; }
  35% { translate: -.4vw .3vh; }
  70% { translate: .2vw 0; }
  100% { scale: 1; translate: 0 0; }
}

/* Screamed: the words leave the bubble and take the screen for one beat. */
:root:not(.off-deliveries) .line.scream[data-seat] {
  left: 0; right: 0; top: 0; bottom: 0;
  max-width: none; padding: 0;
  background: none; box-shadow: none; border-radius: 0;
  translate: none; transform-origin: 50% 50%;
}
:root:not(.off-deliveries) .line.scream::before, :root:not(.off-deliveries) .line.scream::after { display: none; }
:root:not(.off-deliveries) .line.scream.on.cool { opacity: 0; transform: scale(1.12); transition: opacity .12s linear, transform .2s ease-in; }

/* Whispered: small, in a dashed outline you can see through. */
:root:not(.off-deliveries) .bubble[data-as="whisper"], :root:not(.off-deliveries) .line[data-as="whisper"] {
  background: rgba(var(--tint), .18); box-shadow: none;
  outline: .1em dashed rgba(var(--tint), .95); outline-offset: -.05em;
  color: #fff; font-weight: 500; font-stretch: 92%; font-size: clamp(14px, 1.6vw, 28px);
  letter-spacing: .05em;
  text-shadow: 0 .05em .3em rgba(0, 0, 0, .75);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
:root:not(.off-deliveries) .bubble[data-as="whisper"]::after, :root:not(.off-deliveries) .line[data-as="whisper"]::after { display: none; }

/* Muttered: smaller still, drained of colour, with no tail because it is for nobody. */
:root:not(.off-deliveries) .bubble[data-as="mutter"], :root:not(.off-deliveries) .line[data-as="mutter"] {
  padding: .4em .8em .45em;
  color: rgba(22, 6, 17, .72);
  font-style: italic; font-weight: 500; font-size: clamp(13px, 1.45vw, 25px);
  filter: saturate(.3);
}
:root:not(.off-deliveries) .bubble[data-as="mutter"]::after, :root:not(.off-deliveries) .line[data-as="mutter"]::after { display: none; }

/* Thought: a cloud, in the serif, trailing two dots. */
:root:not(.off-deliveries) .bubble[data-as="think"], :root:not(.off-deliveries) .line[data-as="think"] {
  padding: .85em 1.35em .9em;
  background: none; box-shadow: none;
  font: italic 400 clamp(20px, 2.5vw, 44px)/1.02 var(--serif);
  letter-spacing: 0;
}
:root:not(.off-deliveries) [data-as="think"] .shape { stroke: none; }
:root:not(.off-deliveries) .bubble[data-as="think"]::after, :root:not(.off-deliveries) .line[data-as="think"]::after {
  bottom: -1.25em; width: .46em; height: .46em;
  border-radius: 50%;
  transform: none;
  box-shadow: -.5em .55em 0 -.08em rgb(var(--tint));
}

/* Deadpan: a plain box with a hard shadow, and no bounce when it arrives. */
:root:not(.off-deliveries) .bubble[data-as="flat"], :root:not(.off-deliveries) .line[data-as="flat"] {
  border-radius: .16em;
  background: rgb(var(--tint));
  box-shadow: 0 0 0 .07em #000, .22em .22em 0 #000;
  font-stretch: 78%; letter-spacing: 0;
}
:root:not(.off-deliveries) .line[data-as="flat"] { transform: none; transition: opacity .01s linear; }
:root:not(.off-deliveries) .bubble[data-as="flat"]::after, :root:not(.off-deliveries) .line[data-as="flat"]::after { border-radius: 0; box-shadow: none; }

/* Cut off: the box is sliced where the sentence stops. */
:root:not(.off-deliveries) .bubble[data-as="cut"], :root:not(.off-deliveries) .line[data-as="cut"] { padding-right: 1.6em; background: none; box-shadow: none; }
:root:not(.off-deliveries) [data-as="cut"] .shape { left: -.1em; top: -.06em; width: calc(100% + .2em); height: calc(100% + .12em); stroke-width: 2px; }
:root:not(.off-deliveries) [data-as="cut"] .shape line { stroke: #fff; stroke-width: 5px; }

/* Trailing away: the last word goes. */
:root:not(.off-deliveries) .fade {
  display: inline-block;
  letter-spacing: .1em;
  -webkit-mask-image: linear-gradient(90deg, #000 5%, rgba(0, 0, 0, .06)); mask-image: linear-gradient(90deg, #000 5%, rgba(0, 0, 0, .06));
}

/* With deliveries switched off every line is a plain bubble, a scream included. */
.off-deliveries .shape { display: none; }
.off-deliveries .line.scream .wall { position: static; display: block; width: auto; transform: none; }
.off-deliveries .line.scream .fit { all: unset !important; }
:root.off-jolt .scene.jolt .plate > * { animation: none !important; }

/* ------------------------------------------------------------ cold-open */

.slate { position: absolute; inset: 0; opacity: calc(1 - var(--u) * 4); pointer-events: none; }
/* On an ordinary panel the heading stays up; only the opening one fades as the reader starts to scroll. */
.slate.held { opacity: 1; }
.slate .tl { position: absolute; left: 2.4vw; top: 2.4vw; }
.slate .tr { position: absolute; right: 2.4vw; top: 2.4vw; }
.ready .slate .tl { white-space: nowrap; animation: typed 1s steps(20) .4s both; }
@keyframes typed { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 -5% 0 0); } }

/* Before any scrolling, only the heading and the top of the first line show above the bottom edge. */
.law {
  --line: clamp(34px, 6.3vw, 124px);
  position: absolute; left: 4.5vw; bottom: 9vh;
  width: min(66vw, 1200px);
  transform: translate3d(0, calc((1 - min(1, var(--u) * 2.5)) * (100% + 9vh - var(--line) * .62 - 1.8vh - 16px)), 0);
}
.law .label { margin-bottom: 1.8vh; color: var(--blush); }
.law .serif { font-size: var(--line); text-shadow: 0 .05em .35em rgba(0, 0, 0, .55); }

.aside { position: absolute; left: 50%; top: 50%; translate: -50% -50%; white-space: nowrap; }
.aside .script {
  font-size: clamp(30px, 10.4vw, 210px);
  text-shadow: 0 0 .22em var(--pink), 0 0 .6em var(--hot), 0 .03em .1em rgba(0, 0, 0, .5);
}

/* Blood: narration is lettered like a film's own titles, heavy and narrow, in capitals, and the
   line that twists it is the same family pulled wide and slanted, with its red printed off-register.
   Fine italics and script lettering do not hold over dark footage that moves. */
.look-blood .serif {
  font-family: "Anybody", "Helvetica Neue", Arial, sans-serif; font-style: normal; font-weight: 800; font-stretch: 70%;
  text-transform: uppercase; letter-spacing: .012em; line-height: 1.04;
}
.look-blood .narr { font-size: clamp(24px, 3.3vw, 70px); text-shadow: 0 .05em .25em rgba(0, 0, 0, .9), 0 0 .5em rgba(0, 0, 0, .55); }
.look-blood .law { --line: clamp(27px, 4.6vw, 96px); }
.look-blood .aside .script {
  font-family: "Anybody", "Helvetica Neue", Arial, sans-serif; font-style: italic; font-weight: 900; font-stretch: 125%;
  text-transform: uppercase; letter-spacing: -.012em; line-height: .92;
  font-size: clamp(30px, 6.2vw, 128px); color: var(--blush);
  text-shadow: .04em .04em 0 var(--hot), 0 0 .45em rgba(0, 0, 0, .7);
}

/* ------------------------------------------------------------ long-panel and strip */

.strip { position: absolute; left: 0; top: 0; width: 100%; will-change: transform; }
.strip > img { display: block; width: 100%; height: auto; }
.strip .cap {
  position: absolute;
  font-size: clamp(30px, 5.6vw, 112px);
  text-shadow: 0 .05em .4em rgba(0, 0, 0, .65);
  opacity: 0;
  transform: translate3d(0, 1em, 0);
  transition: opacity .5s, transform .9s var(--glide);
}
.strip .cap.on { opacity: 1; transform: none; }
.strip .cap.to-right { right: 5.5vw; text-align: right; }
.strip .cap.to-left { left: 5vw; }
.strip .who { position: absolute; left: 2.5vw; right: 2.5vw; }
.strip .who .label { position: absolute; left: .2vw; bottom: calc(100% + 1.4vw + var(--lift, 0px)); text-shadow: 0 0 .8em rgba(0, 0, 0, .8); }
.strip .who .fit { opacity: 0; }
.strip .who.on .fit { animation: land .34s var(--snap) both; }
.strip .bubble {
  top: auto;
  max-width: 46vw;
  font-size: clamp(18px, 2.7vw, 48px);
  opacity: 0;
  transform: translate3d(0, .6em, 0) scale(.6);
  transition: opacity .1s linear, transform .32s var(--snap);
}
.strip .bubble.on { opacity: 1; transform: none; }
.strip .bubble.to-left { left: 5%; }
.strip .bubble.to-right { right: 5%; }

/* ------------------------------------------------------------ title */

.knock, .edge { position: absolute; inset: 0; display: grid; place-items: center; }
.knock { background: #000; mix-blend-mode: multiply; }
.knock .fit { color: #fff; }
.edge .fit {
  color: transparent;
  -webkit-text-stroke: max(1.5px, .01em) rgba(255, 255, 255, .92);
  filter: drop-shadow(0 0 .03em var(--pink));
}
.edge { opacity: calc(1 - var(--z, 0) * 5); }
.lockup.wall { position: relative; left: auto; top: auto; width: 93vw; gap: 1.5vw; transform: none; scale: calc(.9 + min(var(--u), .5) * .2); }
.t-title .plate video { filter: brightness(1.2) saturate(1.15); }
.t-title .tagline {
  position: absolute; left: 50%; bottom: 6vh;
  translate: -50% 0;
  white-space: nowrap;
  font-size: clamp(30px, 4.8vw, 96px);
  text-shadow: 0 0 .3em var(--pink);
  opacity: calc(min(1, var(--u) * 3 - 1) - var(--z, 0) * 5);
}
.t-title .s1 { opacity: calc(1 - var(--z, 0) * 5); }
.t-title .shade { opacity: var(--z, 0); }

/* ------------------------------------------------------------ character */

.roll .card { position: absolute; inset: 0; }
.roll .card.fx { transition: none; }
.head { position: absolute; left: 2.5vw; right: 2.5vw; bottom: 3.4vh; display: flex; flex-direction: column; gap: 1.5vw; }
.roll.half .head { right: auto; width: 58vw; }
.head .wall, .who .wall { position: relative; left: auto; top: auto; width: 100%; transform: scaleY(var(--sy, 1)); transform-origin: 50% 100%; }
/* A name stretched tall grows upward, so its heading rides up with it. */
.head .label { text-shadow: 0 0 .8em rgba(0, 0, 0, .7); transform: translate3d(0, calc(var(--lift, 0px) * -1), 0); }
.card.on .head .fit, .roll.live .card:not(.fx) .head .fit { animation: land .34s var(--snap) both; }
@keyframes land { from { transform: scale(1.35) translate3d(0, 12%, 0); opacity: 0; } to { transform: none; opacity: 1; } }

.roll .role {
  position: absolute; right: 2.6vw; top: 2.6vw;
  font: 900 clamp(18px, 2.7vw, 56px)/.92 var(--wide);
  font-stretch: 150%;
  letter-spacing: -.03em;
  text-align: right;
  text-transform: uppercase;
  text-shadow: 0 .04em .3em rgba(0, 0, 0, .5);
}
.roll .figure {
  position: absolute; left: 3vw; top: 4vh;
  font: 900 clamp(70px, 15vw, 300px)/.8 var(--wide);
  font-stretch: 56%;
  letter-spacing: -.01em;
  text-shadow: 0 .03em .25em rgba(0, 0, 0, .45);
}
.roll .surname {
  position: absolute; right: 1vw; bottom: calc(var(--tall, 12vw) - .5em);
  font-size: clamp(54px, 10.5vw, 220px);
  rotate: -7deg;
  text-shadow: 0 0 .16em var(--hot), 0 0 .5em var(--hot), 0 .02em .06em rgba(0, 0, 0, .7);
  clip-path: inset(-20% 100% -20% -10%);
  transition: clip-path .7s .28s ease-out;
}
.roll .card.on .surname, .roll.live .card:not(.fx) .surname { clip-path: inset(-20% -10% -20% -10%); }
.roll .narr { bottom: 8vh; }
.roll .narr.two { bottom: auto; }

/* ------------------------------------------------------------ stat */

.t-stat .stat { position: absolute; left: 3.6vw; bottom: 6vh; display: flex; flex-direction: column-reverse; align-items: flex-start; gap: 1.8vw; transform-origin: 0 100%; }
.stat .n {
  display: block;
  font: 900 clamp(120px, 31vw, 640px)/.74 var(--wide);
  font-stretch: 150%;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.05em;
  text-box: trim-both cap alphabetic;
}
.stat .label { font-size: clamp(14px, 2vw, 38px); letter-spacing: .14em; text-shadow: 0 0 .8em rgba(0, 0, 0, .8); }

/* ------------------------------------------------------------ the camcorder */

.osd-frame { position: absolute; inset: 0; pointer-events: none; z-index: 30; }
.osd-frame .osd { position: absolute; font-size: clamp(18px, 2.4vw, 44px); }
.osd-frame .rec { left: 4.6vw; top: 5vh; display: flex; align-items: center; gap: .4em; }
.osd-frame .rec i { width: .5em; height: .5em; border-radius: 50%; background: #ff2a3c; box-shadow: 0 0 .5em #ff2a3c; animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.osd-frame .sp { left: 4.6vw; top: calc(5vh + 1.25em); }
.osd-frame .tc { right: 4.6vw; top: 5vh; }
.osd-frame .date { right: 4.6vw; bottom: 5vh; text-align: right; line-height: 1.05; color: #ffb347; text-shadow: 0 0 .4em rgba(255, 140, 40, .7); }
.corner { position: absolute; width: 3.2vw; height: 3.2vw; border: 0 solid rgba(255, 255, 255, .85); }
.corner.a { left: 2.2vw; top: 2.2vw; border-left-width: 2px; border-top-width: 2px; }
.corner.b { right: 2.2vw; top: 2.2vw; border-right-width: 2px; border-top-width: 2px; }
.corner.c { left: 2.2vw; bottom: 2.2vw; border-left-width: 2px; border-bottom-width: 2px; }
.corner.d { right: 2.2vw; bottom: 2.2vw; border-right-width: 2px; border-bottom-width: 2px; }
.tape > .plate > * { filter: saturate(1.3) contrast(1.08); }
.tape::after {
  content: "";
  position: absolute; inset: 0; z-index: 20;
  background: repeating-linear-gradient(180deg, rgba(0, 0, 0, 0) 0 2px, rgba(0, 0, 0, .2) 2px 3px);
  pointer-events: none;
}
.reel { position: absolute; left: 4.6vw; bottom: 5vh; font-size: clamp(18px, 2.4vw, 44px); z-index: 30; }
/* Narration keeps clear of the camcorder's own lettering in the corners. */
.tape .narr.two { top: calc(5vh + 7.4vw); }
.tape .narr.two.to-left { left: 4.6vw; }
.tape .narr.two.to-right { right: 4.6vw; }
/* With a name card along the bottom, the date stamp moves up out of its way. */
.roll.tape .osd-frame .date { bottom: auto; top: calc(5vh + 1.35em); }

/* ------------------------------------------------------------ ad */

.band { position: absolute; left: 0; right: 0; height: 8vh; display: flex; align-items: center; overflow: hidden; background: var(--hot); color: #000; z-index: 3; }
.band.top { top: 0; }
.band.bottom { bottom: 0; background: #fff; }
.run {
  display: flex; gap: 2.6vw;
  white-space: nowrap;
  font: 900 4.3vh/1 var(--wide);
  font-stretch: 150%;
  letter-spacing: -.03em;
  text-transform: uppercase;
  transform: translate3d(calc(var(--u) * -36vw - 4vw), 0, 0);
}
.run.back { transform: translate3d(calc(var(--u) * 36vw - 130vw), 0, 0); }
.run span::after { content: "✦"; margin-left: 2.6vw; }
.t-ad .intro { position: absolute; left: 2.6vw; top: calc(8vh + 2.6vh); text-shadow: 0 0 .8em rgba(0, 0, 0, .8); }
.burst { position: absolute; right: 7vw; top: 17vh; width: 17vw; height: 17vw; display: grid; place-items: center; rotate: 12deg; z-index: 2; }
.burst, .burst::before, .burst::after { background: #ffc54a; }
.burst::before, .burst::after { content: ""; position: absolute; inset: 0; }
.burst::before { rotate: 30deg; }
.burst::after { rotate: 60deg; }
.burst span { position: relative; z-index: 1; color: #000; font: 900 3.1vw/.88 var(--wide); font-stretch: 130%; letter-spacing: -.03em; text-align: center; text-transform: uppercase; }
.quote { position: absolute; left: 4.5vw; bottom: calc(8vh + 5vh); width: 66vw; font-size: clamp(34px, 6.6vw, 132px); text-shadow: 0 .05em .35em rgba(0, 0, 0, .7); }

/* ------------------------------------------------------------ duet: two faces, one seam */

.duet { --s: 50%; transition: --s .5s cubic-bezier(.3, 1.25, .4, 1); }
.duet[data-spk="left"] { --s: 62%; }
.duet[data-spk="right"] { --s: 38%; }
.duet .side-b { clip-path: polygon(calc(var(--s) + 6%) 0, 100% 0, 100% 100%, calc(var(--s) - 6%) 100%); }
.duet .seam {
  position: absolute; inset: 0; z-index: 3;
  background: linear-gradient(180deg, #fff, var(--pink));
  clip-path: polygon(calc(var(--s) + 6% - .22vw) 0, calc(var(--s) + 6% + .22vw) 0, calc(var(--s) - 6% + .22vw) 100%, calc(var(--s) - 6% - .22vw) 100%);
}
/* Each picture is sized to more than cover its side and slid so its face sits in the middle
   of whatever room it has. --face and --ratio come from the cut; --x is where the face goes. */
.duet .plate > * {
  inset: auto; left: 0; top: 50%;
  width: var(--w); height: calc(var(--w) / var(--ratio));
  object-fit: fill;
  transform: translate3d(calc(var(--x) - var(--face) * var(--w)), -50%, 0);
  transition: transform .6s cubic-bezier(.3, 1.2, .4, 1), filter .4s;
}
.duet .side-a > * { --w: max(calc(100vh * var(--ratio)), 126vw); --x: 31vw; }
.duet .side-b > * { --w: max(calc(112vh * var(--ratio)), 78vw); --x: 68vw; }
.duet[data-spk="right"] .side-a > * { --x: 19vw; filter: brightness(.62) saturate(.8); }
.duet[data-spk="left"] .side-b > * { --x: 83vw; filter: brightness(.62) saturate(.8); }

/* ------------------------------------------------------------ number-card */

.numcard .digit {
  position: absolute; left: 3vw; top: 50%;
  translate: 0 -50%;
  font: 900 clamp(220px, 108vh, 1400px)/.74 var(--wide);
  font-stretch: 56%;
  letter-spacing: -.03em;
  text-box: trim-both cap alphabetic;
  z-index: 2;
}
.numcard .wall.word { left: auto; right: 3vw; width: 66vw; align-items: flex-end; transform: translate(0, -50%) scaleY(var(--sy, 1)); mix-blend-mode: difference; color: #fff; z-index: 2; }
.numcard .wall.word .fit { font-stretch: 78%; letter-spacing: -.025em; }
.numcard.live .wall.word { animation: hit .22s var(--snap) both; }
/* The word can turn into another one on a given line. */
.numcard .wall.then, .numcard.turned .wall.was { visibility: hidden; }
.numcard.turned .wall.then { visibility: visible; }

/* ------------------------------------------------------------ chapter, sfx, rules, clock */

.eptitle { position: absolute; inset: 0; visibility: hidden; background: rgba(var(--veil), .55); z-index: 25; }
.eptitle.on { visibility: visible; }
.eptitle.on .wall { animation: hit .24s var(--snap) both; }
.eptitle .wall { width: 92vw; gap: 2.2vw; }
.eptitle .label { position: absolute; left: 50%; top: 5vh; translate: -50% 0; font-size: clamp(12px, 1.5vw, 26px); }

.sfx {
  position: absolute; left: 3vw; top: 6vh;
  font: italic 900 clamp(90px, 24vw, 480px)/.8 var(--wide);
  font-stretch: 62%;
  letter-spacing: -.02em;
  text-transform: uppercase;
  color: transparent;
  -webkit-text-stroke: max(2px, .014em) #fff;
  rotate: -7deg;
  transform-origin: 0 50%;
  filter: drop-shadow(0 0 .06em rgba(255, 255, 255, .7));
}
.t-sfx.live .sfx { animation: spray .5s var(--glide) both; }
@keyframes spray { from { transform: scale(.4) translate3d(-10%, 0, 0); opacity: 0; } to { transform: none; opacity: 1; } }
.t-sfx .bubble { top: auto; bottom: 10vh; right: 6vw; }

.wall.stack { mix-blend-mode: difference; color: #fff; transition: opacity .3s; }
.stack .fit { opacity: 0; transform: translate3d(0, 60%, 0) scaleY(1.6); transform-origin: 50% 100%; transition: opacity .06s linear, transform .3s var(--snap); }
.stack .fit.on { opacity: 1; transform: none; }
.stack .fit.o { color: transparent; -webkit-text-stroke: max(2px, .02em) #fff; }
.hush .wall.stack { opacity: .2; }

.t-clock .label { position: absolute; left: 50%; top: 7vh; translate: -50% 0; color: var(--pink); font-size: clamp(12px, 1.5vw, 26px); }
.wall.lcd { width: 88vw; top: 46%; }
.wall.lcd .fit i { font-style: normal; animation: blink 1s steps(1) infinite; }

/* ------------------------------------------------------------ end-card, photo, rewind */

.wall.logo { top: 36%; width: 62vw; gap: 1vw; }
.t-end-card .tagline { position: absolute; left: 50%; top: 59%; translate: -50% 0; white-space: nowrap; font-size: clamp(28px, 4.4vw, 88px); text-shadow: 0 0 .3em var(--pink); }
.t-end-card .plate.dim > * { filter: brightness(calc(var(--dim, .5) * .76)) saturate(.9); }
.billing {
  position: absolute; left: 7vw; right: 7vw; bottom: 12vh;
  font: 700 clamp(17px, 2.5vw, 50px)/1.08 var(--wide);
  font-stretch: 50%;
  letter-spacing: .05em;
  text-align: center;
  text-transform: uppercase;
  color: #efe6f1;
}
.billing small { margin: 0 .55em 0 1.2em; font-size: .46em; letter-spacing: .14em; vertical-align: .34em; opacity: .8; }
.billing i { margin: 0 .2em; font-style: normal; opacity: .55; }
.cont { position: absolute; left: 50%; bottom: 5vh; translate: -50% 0; white-space: nowrap; color: var(--blush); }

.photo { position: absolute; inset: 0; z-index: 35; visibility: hidden; box-shadow: inset 0 0 0 2.4vw #fff, inset 0 0 0 calc(2.4vw + 2px) rgba(0, 0, 0, .35); }
.photo.on { visibility: visible; }
.photo::before { content: ""; position: absolute; inset: 0; background: #fff; opacity: 0; }
.photo.on::before { animation: flash .7s ease-out both; }
.stamp {
  position: absolute; left: 50%; bottom: 13vh;
  font-size: clamp(54px, 11vw, 230px);
  white-space: nowrap;
  translate: -50% 0; rotate: -5deg;
  text-shadow: 0 0 .18em var(--hot), 0 0 .5em var(--hot), 0 .02em .06em rgba(0, 0, 0, .7);
  clip-path: inset(-30% 100% -30% -5%);
  transition: clip-path .9s .35s ease-out;
}
.photo.on .stamp { clip-path: inset(-30% -5% -30% -5%); }

.t-rewind .plate > * { filter: saturate(.25) contrast(1.4) brightness(.8); animation: jitter .12s steps(2) infinite; }
@keyframes jitter { 50% { translate: .6vw 0; } }
.bars {
  position: absolute; inset: -20% 0; z-index: 10;
  background: repeating-linear-gradient(180deg, rgba(255, 255, 255, 0) 0 6vh, rgba(255, 255, 255, .5) 6vh 6.5vh, rgba(0, 0, 0, .45) 6.5vh 9vh, rgba(255, 255, 255, 0) 9vh 17vh);
  animation: roll .28s linear infinite;
}
@keyframes roll { to { transform: translate3d(0, -17vh, 0); } }
.rew { position: absolute; left: 50%; top: 50%; translate: -50% -50%; font-size: clamp(60px, 13vw, 260px); white-space: nowrap; z-index: 30; }

/* ------------------------------------------------------------ depth */

/* A scene with depth shows its picture twice: whole, at the back, and again as a cut-out of the
   subject, in front of the lettering, the 3D and the layers. The back goes a little out of
   focus and the subject sits a touch closer, which is what a lens would do. Anything to be
   read stays in front of both. */
.plate.over { z-index: 5; pointer-events: none; }
.off-depth .plate.over { display: none; }
:root:not(.off-depth) .scene.deep > .plate:not(.over) > * { filter: blur(var(--f-depth-blur, 2.5px)) brightness(.94); }
:root:not(.off-depth) .scene.deep > .plate.dim:not(.over) > * { filter: blur(calc(var(--f-depth-blur, 2.5px) * 1.2)) brightness(var(--dim, .5)) saturate(1.15); }
:root:not(.off-depth) .scene.deep > .plate.over > img { scale: 1.03; }
:root:not(.off-depth) .scene.deep .narr, :root:not(.off-depth) .scene.deep .label, :root:not(.off-depth) .scene.deep .voice, :root:not(.off-depth) .scene.deep .bubble { z-index: 6; }
/* On an upright screen a figure fills most of the frame and would hide the words, so there
   big lettering stays in front as it always did. Weather and focus keep their depth. */
@media (max-aspect-ratio: 1/1) {
  .scene.deep:has(> .wall, > .three:not(.under)) > .plate.over { display: none; }
  .scene.deep:has(> .wall, > .three:not(.under)) > .plate:not(.over):not(.dim) > * { filter: none; }
  .scene.deep:has(> .wall, > .three:not(.under)) > .plate.dim:not(.over) > * { filter: brightness(var(--dim, .5)) saturate(1.15); }
}
/* Behind somebody, weather can be weather: it no longer has to keep clear of the face. */
:root:not(.off-depth) .scene.deep .layer.air { -webkit-mask-image: none; mask-image: none; }
:root:not(.off-depth) .scene.deep.live .layer.air { animation: none; opacity: .85; }

/* ------------------------------------------------------------ page */

/* A comic page. Every cell is the size of the screen and clips itself to its own shape, so the
   gutters are simply what is left between them. A cell carries its box (--x --y --w --h) and
   its middle (--cx --cy) for whatever sits inside it. */
.page { position: absolute; inset: 0; background: #000; transform-origin: var(--zx, 50%) var(--zy, 50%); transition: transform .75s var(--glide); }
.page.paper-white { background: #f4efe9; }
.page.paper-pink { background: var(--hot); }
.cell { position: absolute; inset: 0; }
.cell.fx { transition: opacity .12s linear, transform .5s var(--glide); }
.cell.fx:not(.on) { transform: translate3d(var(--dx, 0), var(--dy, 0), 0) scale(1.05); }
/* The keyline round a panel: the same shape a little larger, underneath it. */
.rim { position: absolute; inset: 0; background: #fff; }
.paper-white .rim { background: #000; }
.pic { position: absolute; inset: 0; }
.pic > img, .pic > .rough { position: absolute; object-fit: cover; }
.pic > .rough { padding: 2.4vmin; }
.pic > .rough p { font-size: clamp(10px, 1vw, 15px); }
/* A panel of lettering instead of a picture. */
.pic.word { background: radial-gradient(120% 120% at 30% 20%, var(--a5), var(--a4) 55%, #000); }
.pic.word .wall { left: var(--cx); top: var(--cy); width: calc(var(--w) * .82); }
/* The one panel allowed to break the grid: larger, tilted, on top, with a hard shadow. */
.cell.pop { z-index: 3; filter: drop-shadow(.7vmin .7vmin 0 rgba(0, 0, 0, .9)); }
.cell.pop .pic, .cell.pop .rim { transform-origin: var(--cx) var(--cy); transform: rotate(-1.6deg) scale(1.07); }
.cell.pop .rim { background: var(--hot); }
.cell.pop.fx.on { animation: thump .3s var(--snap); }
@keyframes thump { from { scale: 1.1; } to { scale: 1; } }
/* A caption box, and a line of speech, kept inside their panel. */
.cell .note {
  position: absolute; left: calc(var(--x) + 1.6vw); top: calc(var(--y) + 2.4vh);
  max-width: calc(var(--w) - 3.2vw);
  padding: .32em .6em .4em;
  background: #000; color: #fff;
  font: italic 400 clamp(15px, 1.75vw, 32px)/1.04 var(--serif);
  z-index: 2;
}
.paper-white .cell .note { background: #fff; color: #000; }
.cell .bubble.in {
  left: calc(var(--x) + 1.8vw); top: auto; bottom: calc(100% - var(--y) - var(--h) + 4.5vh);
  max-width: calc(var(--w) - 4vw);
  font-size: clamp(14px, 1.6vw, 28px);
}
/* Diving into a panel: the whole page grows about the point that brings it to the middle. */
.scene.diving .page { transform: scale(var(--zoom, 1)); }
/* What was written on the page stays behind: only the picture is dived into. */
.cell .note, .cell .bubble.in { transition: opacity .2s linear; }
.scene.diving .cell .note, .scene.diving .cell .bubble.in { opacity: 0; }

/* ------------------------------------------------------------ layers */

/* Something happening in a scene that belongs to its story. A layer sits behind the lettering
   and the speech (the build writes it straight after the picture), and what it scatters thins
   out around the face (--fx, --fy), so nothing crosses what the reader is looking at. It
   arrives with the scene and then falls back: anything that keeps moving over a picture
   competes with it. Each bit of a particle layer carries --x (where), --k (how big), --e (how
   long one loop takes), --o (how far into the loop it starts) and --r (a turn). */
.layer { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.layer > i { position: absolute; left: var(--x); top: 0; display: block; }
.layer.air {
  opacity: calc(var(--f-layers-strength, 1) * var(--f-layers-rest, .38));
  -webkit-mask-image: radial-gradient(ellipse 46% 42% at var(--fx, 50%) var(--fy, 42%), rgba(0, 0, 0, 0) 32%, rgba(0, 0, 0, .5) 64%, #000 100%);
  mask-image: radial-gradient(ellipse 46% 42% at var(--fx, 50%) var(--fy, 42%), rgba(0, 0, 0, 0) 32%, rgba(0, 0, 0, .5) 64%, #000 100%);
}
.scene.live .layer.air { animation: settle 3.4s ease-out both; }
@keyframes settle { 0%, 30% { opacity: var(--f-layers-strength, 1); } 100% { opacity: calc(var(--f-layers-strength, 1) * var(--f-layers-rest, .38)); } }
/* A story can do without them. What tells the story, such as a clock running down, stays. */
.off-layers .layer.air, .off-layers .layer.light { display: none; }
.off-objects .three.under { display: none; }
.off-sparkles .spark { display: none; }
.l-sparkles .spark { opacity: .8; }

.l-embers > i {
  top: auto; bottom: -3vh;
  width: calc(var(--k) * .6vmin); height: calc(var(--k) * .6vmin);
  border-radius: 50%;
  background: #ffe2b0;
  box-shadow: 0 0 .9vmin .28vmin rgba(255, 130, 40, .7);
  animation: rise var(--e) linear var(--o) infinite;
}
@keyframes rise {
  0% { transform: translate3d(0, 0, 0); opacity: 0; }
  10% { opacity: 1; }
  80% { opacity: .9; }
  100% { transform: translate3d(calc(var(--k) * 7vw - 6vw), -112vh, 0); opacity: 0; }
}

.l-spray > i {
  top: -12vh;
  width: max(1px, .1vmin); height: calc(var(--k) * 6vh);
  background: linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, .55));
  rotate: 24deg;
  animation: streak calc(var(--e) * .14) linear var(--o) infinite;
}
@keyframes streak { to { translate: -48vw 126vh; } }

.l-rain > i {
  top: -14vh;
  width: 1px; height: calc(var(--k) * 9vh);
  background: linear-gradient(rgba(220, 235, 255, 0), rgba(220, 235, 255, .45));
  rotate: 8deg;
  animation: streak-down calc(var(--e) * .1) linear var(--o) infinite;
}
@keyframes streak-down { to { translate: -16vw 128vh; } }

.l-confetti > i {
  top: -5vh;
  width: calc(var(--k) * 1.1vmin); height: calc(var(--k) * 1.9vmin);
  background: hsl(calc(var(--r) * 1deg) 95% 64%);
  animation: tumble var(--e) linear var(--o) infinite;
}
.l-petals > i {
  top: -5vh;
  width: calc(var(--k) * 2.1vmin); height: calc(var(--k) * 1.4vmin);
  border-radius: 80% 12% 80% 12%;
  background: linear-gradient(135deg, #fff, var(--a2));
  opacity: .8;
  animation: tumble calc(var(--e) * 1.6) linear var(--o) infinite;
}
.l-ash > i {
  top: -4vh;
  width: calc(var(--k) * .55vmin); height: calc(var(--k) * .55vmin);
  border-radius: 50%;
  background: rgba(255, 255, 255, .78);
  filter: blur(.5px);
  animation: tumble calc(var(--e) * 2.3) linear var(--o) infinite;
}
@keyframes tumble {
  from { transform: translate3d(0, 0, 0) rotateX(0deg) rotateZ(calc(var(--r) * 1deg)); }
  to { transform: translate3d(calc(var(--k) * 11vw - 9vw), 114vh, 0) rotateX(900deg) rotateZ(calc(var(--r) * 1deg + 420deg)); }
}

.l-bubbles > i {
  top: auto; bottom: -9vh;
  width: calc(var(--k) * 3.2vmin); height: calc(var(--k) * 3.2vmin);
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, .7);
  background: radial-gradient(circle at 30% 28%, rgba(255, 255, 255, .75) 0 8%, rgba(255, 255, 255, .1) 30%, rgba(255, 255, 255, 0) 62%);
  animation: rise calc(var(--e) * 1.3) ease-in var(--o) infinite;
}

.l-haze::before, .l-haze::after {
  content: "";
  position: absolute; inset: -20% -60%;
  background:
    radial-gradient(40% 30% at 30% 62%, rgba(255, 255, 255, .26), rgba(255, 255, 255, 0) 70%),
    radial-gradient(46% 28% at 72% 40%, rgba(255, 255, 255, .18), rgba(255, 255, 255, 0) 70%);
  filter: blur(30px);
  animation: drift 26s linear infinite alternate;
}
.l-haze::after { animation-duration: 38s; animation-direction: alternate-reverse; opacity: .8; }
@keyframes drift { from { transform: translate3d(-12%, 0, 0); } to { transform: translate3d(12%, 0, 0); } }

.l-leak::before {
  content: "";
  position: absolute; inset: -30%;
  background:
    radial-gradient(35% 60% at 18% 30%, rgba(255, 150, 60, .55), rgba(255, 150, 60, 0) 70%),
    radial-gradient(30% 50% at 86% 72%, rgba(255, 80, 160, .42), rgba(255, 80, 160, 0) 70%);
  mix-blend-mode: screen;
  animation: drift 13s ease-in-out infinite alternate;
}

.l-flashes > i {
  top: calc(var(--k) * 56vh - 22vh);
  width: 46vmin; height: 46vmin;
  margin-left: -23vmin;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, .6), rgba(255, 255, 255, 0) 62%);
  mix-blend-mode: screen;
  opacity: 0;
  animation: bulb calc(var(--e) * .55) linear var(--o) infinite;
}
@keyframes bulb {
  0%, 91%, 100% { opacity: 0; transform: scale(.4); }
  93% { opacity: 1; transform: scale(1); }
  96% { opacity: .2; }
}

.l-pulse { box-shadow: inset 0 0 20vmin 3vmin rgba(255, 20, 60, .42); animation: beat 1.05s ease-in-out infinite; z-index: 3; }
@keyframes beat { 0%, 100% { opacity: .25; } 12% { opacity: 1; } 24% { opacity: .45; } 36% { opacity: .9; } }

.l-speed {
  background: repeating-conic-gradient(from 0deg at 50% 46%, rgba(255, 255, 255, 0) 0deg 2.2deg, rgba(255, 255, 255, .4) 2.4deg 2.8deg, rgba(255, 255, 255, 0) 3deg 5.6deg);
  -webkit-mask-image: radial-gradient(circle at 50% 46%, transparent 24%, #000 66%);
  mask-image: radial-gradient(circle at 50% 46%, transparent 24%, #000 66%);
  mix-blend-mode: screen;
}
/* The lines shake for the length of the hit and then hold still. */
.scene.live .l-speed { animation: pump .32s steps(2) 5; }
@keyframes pump { 50% { transform: scale(1.05) rotate(.9deg); } }

.l-halftone {
  background-image: radial-gradient(circle, rgba(0, 0, 0, .6) 27%, rgba(0, 0, 0, 0) 30%);
  background-size: 1vmin 1vmin;
  mix-blend-mode: multiply;
  opacity: .5;
}

.l-glitch {
  background: repeating-linear-gradient(180deg, rgba(0, 0, 0, 0) 0 7vh, rgba(0, 255, 255, .22) 7vh 7.7vh, rgba(0, 0, 0, 0) 7.7vh 19vh, rgba(255, 0, 90, .22) 19vh 19.6vh);
  mix-blend-mode: screen;
  opacity: 0;
}
/* The signal fails three times as the scene arrives, then holds. */
.scene.live .l-glitch { animation: jolt 2.3s steps(1) 3; }
@keyframes jolt {
  0%, 85%, 100% { transform: none; opacity: 0; }
  87% { transform: translate3d(1.2vw, -3vh, 0); opacity: 1; }
  90% { transform: translate3d(-.8vw, 5vh, 0); opacity: .8; }
  93% { transform: translate3d(.4vw, -1vh, 0); opacity: 1; }
}

.l-countdown {
  inset: auto 3vw 4.5vh auto;
  overflow: visible;
  font-size: clamp(22px, 3.4vw, 64px);
  color: var(--blush);
  text-shadow: 0 0 .06em #fff, 0 0 .2em var(--hot), 0 0 .6em var(--hot);
  z-index: 7;
}
.l-stamp { display: grid; place-items: center; z-index: 5; }
.l-stamp span {
  padding: .12em .34em .1em;
  border: .09em solid currentColor;
  border-radius: .12em;
  color: #ff3048;
  font: 900 clamp(48px, 13vw, 260px)/1 var(--wide);
  font-stretch: 62%;
  letter-spacing: .02em;
  text-transform: uppercase;
  rotate: -11deg;
  mix-blend-mode: hard-light;
  opacity: .88;
  -webkit-mask-image: repeating-radial-gradient(circle at 30% 40%, #000 0 1.1px, rgba(0, 0, 0, .55) 1.1px 2.3px);
  mask-image: repeating-radial-gradient(circle at 30% 40%, #000 0 1.1px, rgba(0, 0, 0, .55) 1.1px 2.3px);
}
.l-ticker { inset: auto 0 0 0; height: 5.4vh; display: flex; align-items: center; background: #000; color: #fff; z-index: 5; }
.l-ticker .run { font-size: 2.5vh; gap: 2vw; letter-spacing: .04em; transform: translate3d(calc(var(--u) * -30vw - 4vw), 0, 0); }
.l-ticker .run span::after { margin-left: 2vw; color: var(--pink); }

/* ------------------------------------------------------------ the reader's own time */

/* A deadline that is really running, in the corner of every screen after the one that set it. */
#deadline {
  position: fixed; right: 3vw; bottom: 4.5vh; z-index: 940;
  margin: 0;
  font-size: clamp(22px, 3.4vw, 64px);
  color: var(--blush);
  text-shadow: 0 0 .06em #fff, 0 0 .2em var(--hot), 0 0 .6em var(--hot);
  opacity: 0;
  transition: opacity .3s;
  pointer-events: none;
}
#deadline.on { opacity: 1; }
/* Each second that turns over shows: the figures flare and settle. Two names for one move, so it can be played again at once. */
@keyframes tick-a { from { filter: brightness(1.9); scale: 1.05; } to { filter: none; scale: 1; } }
@keyframes tick-b { from { filter: brightness(1.9); scale: 1.05; } to { filter: none; scale: 1; } }
#deadline.tick-a, [data-rt="deadline"].tick-a { animation: tick-a .5s ease-out; }
#deadline.tick-b, [data-rt="deadline"].tick-b { animation: tick-b .5s ease-out; }
#deadline { transform-origin: 100% 100%; font-variant-numeric: tabular-nums; }
/* Out of time: it flashes for a few seconds and then holds, so it does not nag for the rest of the film. */
#deadline.out { color: #ff3048; animation: blink 1s steps(1) 6; }
.until { font-variant-numeric: tabular-nums; }
/* A small white square at the top of the screen while a picture or a clip on it is still arriving. */
#loading {
  position: fixed; z-index: 945;
  top: calc(env(safe-area-inset-top, 0px) + 14px); left: 50%;
  width: var(--f-loading-size, 6px); height: var(--f-loading-size, 6px);
  margin-left: calc(var(--f-loading-size, 6px) / -2);
  background: #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .35);
  opacity: 0;
  pointer-events: none;
}
#loading.on { animation: loading .9s ease-in-out infinite alternate; }
@keyframes loading { from { opacity: 1; } to { opacity: .25; } }
.off-loading #loading { display: none; }
/* A scroll turns one page: the page itself does not follow the finger, so nothing rests half way. */
:root:not(.off-paging):not(.gated) body { touch-action: none; }
/* The words step aside: one more step after giant lettering over a clip, with the clip seen whole. */
.scene.clear .wall, .scene.clear .label.voice, .scene.clear .glints { opacity: 0 !important; transition: opacity .2s linear; }
.scene.clear .plate.dim > * { filter: none; transition: filter .3s linear; }
/* A film read late at night can close in a little at the edges. */
#night {
  position: fixed; inset: 0; z-index: 999;
  background: radial-gradient(120% 95% at 50% 42%, rgba(4, 6, 30, 0) 38%, rgba(4, 6, 30, .82) 100%);
  opacity: calc(var(--night, 0) * var(--f-realtime-night, 0));
  pointer-events: none;
}
.off-realtime #deadline, .off-realtime #night { display: none; }

/* ------------------------------------------------------------ 3D */

/* The 3D stage draws into one canvas, which is moved into whichever scene needs it. */
.three { position: absolute; inset: 0; pointer-events: none; z-index: 4; }
/* Objects in the air are a layer like any other: behind the lettering and the speech. */
.three.under { z-index: auto; }
/* Except under inverting letters, which would turn gold coins navy: there they stay in front. */
.scene:has(.wall.diff) .three.under { z-index: 4; }
.three canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* The flat version of a 3D scene shows only where 3D cannot be drawn. */
.gl:not(.off-three) .flat { display: none !important; }
.off-three .three:not(.under) { display: none; }
/* Behind pictures hung in space, the same picture out of focus, so the dark between them has colour. */
.gl .plate.backdrop > * { filter: blur(42px) brightness(.5) saturate(1.5); scale: 1.25; }

/* ------------------------------------------------------------ the front desk */

/* Sound is only allowed to begin on a tap, so the film waits here for one. */
html.gated, html.gated body { overflow: hidden; }
html.gated .scene:first-child > :not(.plate) { visibility: hidden; }
#gate {
  position: fixed; inset: 0; z-index: 900;
  display: grid; place-items: center;
  padding: max(6vh, env(safe-area-inset-top)) 7vw max(6vh, env(safe-area-inset-bottom));
  background: radial-gradient(120% 90% at 50% 45%, rgba(var(--veil), .5), rgba(var(--veil), .88));
  text-align: center;
  touch-action: none;
  transition: opacity .7s ease, visibility 0s linear .7s;
}
html:not(.gated) #gate { opacity: 0; visibility: hidden; pointer-events: none; }
.desk { display: flex; flex-direction: column; align-items: center; gap: 2.8vh; max-width: min(86vw, 820px); }
.desk .label { color: var(--blush); font-size: clamp(11px, 1.05vw, 16px); }
.desk .serif { font-size: clamp(32px, 4.8vw, 80px); text-wrap: balance; text-shadow: 0 .05em .4em rgba(0, 0, 0, .6); }
#checkin, #quiet { -webkit-appearance: none; appearance: none; border: 0; cursor: pointer; }
#checkin {
  display: flex; align-items: center; gap: .55em;
  margin-top: 1.6vh;
  padding: .62em 1.3em .66em 1.1em;
  border-radius: 2em;
  color: var(--ink);
  font: 900 clamp(22px, 2.6vw, 44px)/1 var(--wide);
  font-stretch: 135%;
  letter-spacing: -.02em;
  text-transform: uppercase;
  background:
    radial-gradient(130% 95% at 50% -32%, rgba(255, 255, 255, .92) 0 46%, rgba(255, 255, 255, 0) 47%),
    linear-gradient(180deg, var(--a2), var(--a6));
  box-shadow:
    inset 0 0 0 .07em rgba(255, 255, 255, .8),
    inset 0 -.45em .7em rgba(255, 255, 255, .42),
    0 .3em 1.2em var(--hot),
    0 0 0 .05em rgba(0, 0, 0, .28);
  transition: transform .18s var(--snap);
}
#checkin:hover { transform: scale(1.05); }
#checkin:active { transform: scale(.96); }
#checkin svg { width: 1.15em; height: 1.15em; fill: currentColor; }
#quiet {
  padding: .9em 1.2em;
  background: none;
  color: #fff;
  font: 700 clamp(11px, 1vw, 15px)/1 var(--wide);
  font-stretch: 118%;
  letter-spacing: .22em;
  text-transform: uppercase;
  text-decoration: underline;
  text-underline-offset: .4em;
  opacity: .75;
}
#quiet:hover { opacity: 1; }

/* Sound and full screen: out of the picture until the mouse moves or the screen is tapped. */
#controls {
  position: fixed; z-index: 950;
  right: max(14px, env(safe-area-inset-right)); top: max(14px, env(safe-area-inset-top));
  display: flex; gap: 10px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s;
}
html.controls:not(.gated) #controls { opacity: 1; pointer-events: auto; }
#controls button {
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border: 0; border-radius: 50%;
  color: #fff;
  background: rgba(14, 8, 20, .45);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  backdrop-filter: blur(10px) saturate(1.4);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .38);
  cursor: pointer;
}
#controls svg { width: 22px; height: 22px; fill: currentColor; }
/* The paywall. On the front desk of a locked episode the two ways in give place to the offer; on the end card of
   the last free episode the way on becomes it. One line says how much is behind it, the button names the price,
   and a small line says how it is paid. */
.walling #checkin, .walling #quiet, .walled #checkin, .walled #quiet { display: none; }
.offer { display: grid; justify-items: center; gap: 1.5vh; margin-top: .6vh; }
.offer .more, .cont .more { display: block; font: italic 400 clamp(22px, 2.3vw, 38px)/1.05 var(--serif); letter-spacing: -.01em; text-transform: none; color: #fff; text-shadow: 0 .05em .4em rgba(0, 0, 0, .6); }
.offer .how, .cont .how { display: block; font: 700 clamp(9px, .82vw, 13px)/1.2 var(--wide); font-stretch: 116%; letter-spacing: .2em; text-transform: uppercase; color: #fff; opacity: .78; }
.cont .more { margin-bottom: 1.5vh; }
.cont .how { margin-top: 1.6vh; }
#unlock, .cont a.onward.buy {
  -webkit-appearance: none; appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: .6em; max-width: 100%; box-sizing: border-box;
  padding: 1.05em 1.5em 1em 1.25em; border: 0; border-radius: 999px;
  background: linear-gradient(180deg, #fff 0%, #ffd9ec 18%, var(--pink) 100%); color: #14060d;
  font: 800 clamp(13px, 1.25vw, 19px)/1.25 var(--wide); font-stretch: 112%; letter-spacing: .07em; text-transform: uppercase;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, .9), 0 0 26px var(--pink); animation: onward 1.8s ease-in-out infinite;
}
#unlock svg, .cont a.onward.buy svg { flex: none; width: 1.25em; height: 1.25em; }
.cont a.onward.buy::after { content: none; }
#unlock.wait, .cont a.onward.wait { opacity: .6; pointer-events: none; animation: none; }
.cont a.onward.nudge { animation: nudge .36s ease, onward 1.8s ease-in-out .36s infinite; }
@keyframes nudge { 30% { transform: scale(1.09); } 60% { transform: scale(.98); } }
/* An end card that carries the offer gives it the room of the cast's names, and nothing on it says "tap". */
.scene.leads:has(.onward.buy) .billing { display: none; }
html:has(.t-end-card.live .onward.buy) #tapper { opacity: 0; }

/* The way home, on a film that stands on a shelf: always there, small, in the top left corner. A scene heading
   typed in that corner starts to its right. */
#menu {
  position: fixed; z-index: 960;
  left: max(12px, env(safe-area-inset-left)); top: max(12px, env(safe-area-inset-top));
  display: grid; place-items: center;
  width: 40px; height: 40px; padding: 0;
  border: 0; border-radius: 50%;
  color: #fff;
  background: rgba(14, 8, 20, .42);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  backdrop-filter: blur(10px) saturate(1.4);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .38);
  cursor: pointer;
  transition: opacity .3s, scale .3s;
  -webkit-tap-highlight-color: transparent;
}
#menu svg { width: 21px; height: 21px; }
#menu.away { opacity: 0; scale: .8; pointer-events: none; }
#menu:active { scale: .92; }
html[data-shelf] .slate .tl { left: calc(max(12px, env(safe-area-inset-left)) + 50px); top: calc(max(12px, env(safe-area-inset-top)) + 12px); }
#snd .waves { display: none; }
html.sound #snd .waves { display: inline; }
html.sound #snd .muted { display: none; }
html.no-full #full { display: none; }
/* On a shelf, the end card's last line is the way to the next episode. */
.cont a { color: inherit; text-decoration: none; border-bottom: 1px solid currentColor; padding-bottom: .15em; pointer-events: auto; }
/* The way on to the next episode is the one thing to press on an end card: a button, in the film's pink, that calls.
   Such a card shows its names and its button as it arrives, with no step between them. */
.scene.leads .fx { opacity: 1; transform: none; }
/* High enough to stand clear of a friend in the corner and of what he says there. */
.scene.leads .billing { bottom: max(28.5vh, calc(env(safe-area-inset-bottom) + 25.5vh)); }
.scene.leads .cont { left: 4vw; right: 4vw; bottom: max(21vh, calc(env(safe-area-inset-bottom) + 18vh)); translate: none; white-space: normal; text-align: center; }
.cont a.onward {
  display: inline-block; max-width: 100%; box-sizing: border-box; padding: 1.05em 1.6em 1em; border: 0; border-radius: 999px;
  background: linear-gradient(180deg, #fff 0%, #ffd9ec 18%, var(--pink) 100%); color: #14060d;
  font: 800 clamp(13px, 1.25vw, 19px)/1.25 var(--wide); font-stretch: 112%; letter-spacing: .07em; text-transform: uppercase;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, .9), 0 0 26px var(--pink); animation: onward 1.8s ease-in-out infinite;
}
.cont a.onward::after { content: ""; display: inline-block; margin-left: .7em; vertical-align: -.05em; border-left: .72em solid currentColor; border-top: .44em solid transparent; border-bottom: .44em solid transparent; }
@keyframes onward { 50% { box-shadow: 0 0 0 2px #fff, 0 0 44px 6px var(--pink); transform: scale(1.03); } }
html:not(.controls):not(.gated) body { cursor: none; }

/* Some lettering is cut one way for wide screens and another for upright ones. */
@media (max-aspect-ratio: 1/1) { .land { display: none !important; } }
@media (min-aspect-ratio: 1001/1000) { .port { display: none !important; } }

/* ------------------------------------------------------------ over everything */

/* Film grain, so stills and clips sit in the same picture. */
.off-grain #grain { display: none; }
#grain {
  position: fixed; inset: 0; z-index: 1000;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 1.1 -.1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: calc(.16 * var(--f-grain-amount, 1));
  mix-blend-mode: overlay;
  pointer-events: none;
  animation: grain .7s steps(1) infinite;
}
@keyframes grain {
  0% { background-position: 0 0; }
  14% { background-position: -37px 52px; }
  28% { background-position: 61px -18px; }
  42% { background-position: -84px -73px; }
  57% { background-position: 23px 97px; }
  71% { background-position: 112px 41px; }
  85% { background-position: -51px -109px; }
}
/* A phone composites the grain as a plain veil: blending the whole screen is too heavy for it. */
@media (pointer: coarse) {
  #grain { mix-blend-mode: normal; opacity: calc(.07 * var(--f-grain-amount, 1)); }
}

#hud { position: fixed; left: 8px; top: 8px; z-index: 1002; padding: 4px 8px; background: rgba(0, 0, 0, .7); color: #7dffb0; font: 12px/1.3 Menlo, monospace; pointer-events: none; }

/* ------------------------------------------------------------ upright screens */

@media (max-aspect-ratio: 1/1) {
  .strip > img { width: max(100%, 92vh); margin-left: calc((100% - max(100%, 92vh)) / 2); }
  .narr, .law { max-width: none; width: 90vw; }
  /* A phone's own bars cover the bottom of the page until the reader has scrolled: words sit clear of them. */
  .narr, .law { bottom: 17vh; }
  .narr.centre { bottom: 15vh; }
  .roll .narr:not(.two) { bottom: 17vh; }
  /* So the opening line cannot wait half below the edge: its heading is there, and the line comes up into place. */
  .law { transform: none; }
  .law .serif { opacity: min(1, calc(var(--u) * 2.5)); translate: 0 calc((1 - min(1, var(--u) * 2.5)) * 40%); }
  .narr.two, .narr.two.to-left, .narr.two.to-right { left: 5vw; right: 5vw; top: 17vh; width: auto; max-width: none; text-align: left; }
  .aside { white-space: normal; width: 96vw; text-align: center; }
  .aside .script { font-size: 16.5vw; line-height: .98; }
  .look-blood .aside .script { font-size: 9.6vw; line-height: .94; }
  .bubble, .line { max-width: 78vw; font-size: clamp(17px, 4.6vw, 30px); }
  .bubble.to-left, .bubble.to-right { left: 6vw; right: auto; top: 60vh; transform-origin: 0 100%; }
  .bubble.to-right::before { left: 1.05em; right: auto; }
  .bubble.to-right::after { left: 1.5em; right: auto; }
  /* On an impact the reaction stays low and to the right; without this the rule above gives it a top as well as a bottom and it stretches. */
  .t-sfx .bubble.to-right { top: auto; bottom: 12vh; left: auto; right: 6vw; transform-origin: 100% 100%; }
  .t-sfx .bubble.to-right::before { left: auto; right: 1.05em; }
  .t-sfx .bubble.to-right::after { left: auto; right: 1.5em; }
  .bubble.centre { font-size: clamp(17px, 4.6vw, 30px); max-width: 82vw; }
  .rough p { font-size: clamp(15px, 4.2vw, 24px); }
  .pic > .rough p { font-size: clamp(10px, 2.6vw, 14px); }
  .cell .note { left: calc(var(--x) + 3vw); top: calc(var(--y) + 1.6vh); max-width: calc(var(--w) - 6vw); font-size: clamp(14px, 3.9vw, 24px); }
  .cell .bubble.in { left: calc(var(--x) + 3vw); bottom: calc(100% - var(--y) - var(--h) + 2.6vh); max-width: calc(var(--w) - 6vw); font-size: clamp(13px, 3.7vw, 22px); }
  :root:not(.off-deliveries) .bubble[data-as="shout"], :root:not(.off-deliveries) .line[data-as="shout"] { font-size: clamp(19px, 5.5vw, 36px); }
  :root:not(.off-deliveries) .bubble[data-as="think"], :root:not(.off-deliveries) .line[data-as="think"] { font-size: clamp(19px, 5.3vw, 34px); }
  :root:not(.off-deliveries) .bubble[data-as="whisper"], :root:not(.off-deliveries) .line[data-as="whisper"] { font-size: clamp(14px, 3.8vw, 24px); }
  :root:not(.off-deliveries) .bubble[data-as="mutter"], :root:not(.off-deliveries) .line[data-as="mutter"] { font-size: clamp(13px, 3.5vw, 22px); }
  .strip .bubble { max-width: 80vw; font-size: clamp(17px, 4.6vw, 30px); top: auto; }
  .strip .bubble.to-right { left: auto; right: 5%; }
  .wall.cond .fit { font-stretch: 50%; }
  .wall.stack { gap: 1.6vw; }
  .lockup.port .fit { font-stretch: 62%; letter-spacing: -.02em; }
  .lockup.wall { gap: 3vw; }
  .t-title .tagline, .t-end-card .tagline { font-size: 8vw; }
  .wall.logo { width: 84vw; }
  .roll.half .head { right: 2.5vw; width: auto; }
  .roll .surname { font-size: 19vw; }
  .stat .n { font-size: 33vw; transform: scaleY(1.55); transform-origin: 0 100%; }
  .stat .label { margin-bottom: 13.5vw; }
  .osd-frame .osd, .reel { font-size: 5vw; }
  .osd-frame .date { bottom: auto; top: calc(5vh + 1.35em); }
  .burst { width: 30vw; height: 30vw; top: 13vh; }
  .burst span { font-size: 5.6vw; }
  .quote { width: 90vw; font-size: 11vw; }
  .billing { font-size: 5.4vw; }

  /* Dialogue: the seam turns to run across, one face above the other. */
  .duet .side-b { clip-path: polygon(0 calc(var(--s) + 4%), 100% calc(var(--s) - 4%), 100% 100%, 0 100%); }
  .duet .seam { clip-path: polygon(0 calc(var(--s) + 4% - .25vh), 100% calc(var(--s) - 4% - .25vh), 100% calc(var(--s) - 4% + .25vh), 0 calc(var(--s) + 4% + .25vh)); }
  .duet .plate > * { --x: 50vw; transition: transform .6s cubic-bezier(.3, 1.2, .4, 1), filter .4s; }
  .duet .side-a > .rough { padding: 9vh 8vw 58vh; }
  .duet .side-b > .rough { padding: 52vh 8vw 24vh; }
  .duet .side-a > * { --w: max(calc(75vh * var(--ratio)), 100vw); top: 0; transform: translate3d(calc(50vw - var(--face) * var(--w)), 0, 0); }
  .duet .side-b > * { --w: max(calc(72vh * var(--ratio)), 100vw); top: auto; bottom: 0; transform: translate3d(calc(50vw - var(--face) * var(--w)), 0, 0); }
  .duet[data-spk="right"] .side-a > * { transform: translate3d(calc(50vw - var(--face) * var(--w)), -7vh, 0); }
  .duet[data-spk="left"] .side-b > * { transform: translate3d(calc(50vw - var(--face) * var(--w)), 7vh, 0); }
  .line[data-seat="left"] { bottom: auto; top: 12vh; transform-origin: 0 0; }
  /* With more than two voices over one shot, they stack up from the bottom instead. */
  .t-talk .line[data-seat="left"] { top: auto; bottom: 20vh; transform-origin: 0 100%; }
  .t-talk .line[data-seat="mid"] { bottom: 31vh; }
  .numcard .digit { top: 5vh; translate: none; font-size: 52vh; }
  .numcard .wall.word { top: 68%; width: 92vw; }
  .numcard .line[data-seat="left"] { top: auto; bottom: 22vh; transform-origin: 0 100%; }
}

/* ------------------------------------------------------------ games */

/* A scene that is played (game.js). The game draws on a canvas over the scene's own picture, which
   shows through until the game has begun. With the feature off the scene is a plain panel. */
.game { position: absolute; inset: 0; pointer-events: none; }
.game canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity .3s linear; }
.t-game.playing .game canvas, .t-game.played .game canvas { opacity: 1; }
.off-games .game, .off-games .t-game .hud { display: none; }
.t-game.playing { touch-action: none; }
/* A way out, for a reader who would rather read: a button in the top corner from the first second. */
.game .pass {
  position: absolute; left: max(12px, env(safe-area-inset-left)); top: max(calc(env(safe-area-inset-top, 0px) + 8px), 4.6vh);
  padding: .85em 1.25em .8em; border: 1.5px solid rgba(255, 255, 255, .85); border-radius: 2em;
  background: rgba(22, 6, 17, .42); color: #fff; cursor: pointer;
  font-size: clamp(11px, 1vw, 16px); -webkit-tap-highlight-color: transparent;
  opacity: 0; visibility: hidden; pointer-events: none;
}
.t-game.playing .game .pass { visibility: visible; pointer-events: auto; animation: passin .4s linear 1.2s both; }
@keyframes passin { to { opacity: .95; } }
/* With the button there, the line at the top moves over to make room for it on a narrow screen. */
@media (max-width: 560px) {
  .t-game .hud { left: auto; right: 12px; translate: none; font-size: 16px; }
  .game .pips { left: auto; right: 12px; translate: none; top: calc(max(calc(env(safe-area-inset-top, 0px) + 12px), 5.5vh) + 24px); }
}
/* What is left in the magazines, low on the right as on any shooter. */
.game .rounds {
  position: absolute; right: max(18px, env(safe-area-inset-right)); bottom: max(calc(env(safe-area-inset-bottom, 0px) + 16px), 3vh);
  margin: 0; font-size: clamp(26px, 3vw, 54px); color: #fff; opacity: 0; transition: opacity .3s linear;
}
.t-game.playing .game .rounds { opacity: .95; }
.game .rounds.empty { color: var(--hot); animation: blink .5s steps(1) infinite; }
.game .rounds.free { color: var(--pink); scale: 1.5; transform-origin: 100% 100%; }
/* Jumping, on a screen with no keys. */
.game .jump {
  position: absolute; right: max(20px, env(safe-area-inset-right)); bottom: 24vh; width: 58px; height: 58px;
  border: 2px solid rgba(255, 255, 255, .8); border-radius: 50%; background: rgba(255, 92, 171, .28);
  opacity: 0; visibility: hidden; pointer-events: none; -webkit-tap-highlight-color: transparent;
}
.game .jump::before { content: ""; position: absolute; left: 50%; top: 54%; width: 15px; height: 15px; border-left: 3px solid #fff; border-top: 3px solid #fff; translate: -50% -50%; rotate: 45deg; }
@media (hover: none) {
  .t-game.playing .game .jump { opacity: .85; visibility: visible; pointer-events: auto; }
}
/* The line that opens it is read, then steps aside for the hands, and the shade under it with it. */
.t-game.playing .narr { opacity: 0 !important; transition: opacity .35s linear 1s; }
.t-game .shade { transition: opacity .4s linear; }
.t-game.playing .shade { opacity: 0; transition-delay: 1s; }
/* The world's own display, at the top while the game is played. */
.t-game .hud {
  position: absolute; left: 50%; top: max(calc(env(safe-area-inset-top, 0px) + 12px), 5.5vh); translate: -50% 0;
  margin: 0; white-space: nowrap; font-size: clamp(19px, 2.1vw, 38px);
  opacity: 0; transition: opacity .3s linear; z-index: 4;
}
.t-game.playing .hud, .t-game.played .hud { opacity: 1; }
.t-game .hud .then, .t-game.flipped .hud .was { display: none; }
.t-game.flipped .hud .then { display: inline-block; animation: hudflip .5s var(--snap) both; }
@keyframes hudflip {
  0% { scale: 1.9; color: var(--hot); }
  100% { scale: 1; }
}
/* One heart for each thing that comes: filled when it is shot down, dimmed when it gets through. */
.game .pips {
  position: absolute; left: 50%; top: calc(max(calc(env(safe-area-inset-top, 0px) + 12px), 5.5vh) + clamp(19px, 2.1vw, 38px) + 8px); translate: -50% 0;
  margin: 0; display: flex; gap: .45em; font: 400 clamp(15px, 1.5vw, 26px)/1 var(--tape); color: #fff;
  text-shadow: 0 0 .5em var(--hot), 0 0 .15em var(--hot);
  opacity: 0; transition: opacity .3s linear;
}
.t-game.playing .pips { opacity: 1; }
.game .pips i { font-style: normal; transition: scale .25s var(--snap), opacity .25s linear; }
.game .pips i::before { content: "\2661"; }
.game .pips i.got { scale: 1.25; color: var(--pink); }
.game .pips i.got::before { content: "\2665"; }
.game .pips i.lost { opacity: .3; }


/* ---------------------------------------------------------------- narration as subtitles */

/* The "subtitles" feature: narration set the way a film subtitles its voice. One place for every
   line, centred and clear of the bottom of a phone: plain heavy white letters with a little shadow
   and nothing behind them. (A dark band behind the words was tried first; Stan: "just a little
   shadow but white typo not a huge banner".) */
:root:not(.off-subtitles) .narr {
  left: 50%; right: auto; top: auto; bottom: 13.5vh;
  translate: -50% 0;
  width: max-content; max-width: min(88vw, 34em);
  color: #fff;
  font-family: "Anybody", "Helvetica Neue", Arial, sans-serif;
  font-style: normal; font-weight: 800; font-stretch: 92%;
  font-size: clamp(19px, 2.2vw, 40px);
  line-height: 1.22; letter-spacing: 0;
  text-align: center; text-wrap: balance;
  text-shadow: 0 .06em .16em rgba(0, 0, 0, .78), 0 0 .05em rgba(0, 0, 0, .55);
}
/* The film's first line is narration too. */
:root:not(.off-subtitles) .law {
  left: 50%; right: auto; top: auto; bottom: 13.5vh;
  translate: -50% 0;
  width: max-content; max-width: min(88vw, 34em);
  text-align: center;
  /* Whole from the first moment: it is being said. */
  transform: none;
}
:root:not(.off-subtitles) .law .serif {
  color: #fff;
  font-family: "Anybody", "Helvetica Neue", Arial, sans-serif;
  font-style: normal; font-weight: 800; font-stretch: 92%;
  font-size: clamp(19px, 2.2vw, 40px);
  line-height: 1.22; letter-spacing: 0;
  text-wrap: balance;
  text-shadow: 0 .06em .16em rgba(0, 0, 0, .78), 0 0 .05em rgba(0, 0, 0, .55);
  /* It comes with the reader's first tap, on every screen, as the narrator says it: before that tap the
     film is its picture and "Tap! Tap!". */
  opacity: min(1, calc(var(--u) * 2.5)); translate: 0 calc((1 - min(1, var(--u) * 2.5)) * 40%);
}

/* ---------------------------------------------------------------- tap! tap! */

/* The "tapper" feature: a sign at the bottom of the first screens. It takes no tap of its own. */
#tapper {
  position: fixed; left: 50%; bottom: max(3.4vh, env(safe-area-inset-bottom, 0px) + 1.4vh);
  z-index: 60;
  display: flex; gap: .5em;
  padding: .62em 1.25em .66em;
  border-radius: 2em;
  translate: -50% 0;
  pointer-events: none;
  color: var(--ink);
  font: 900 clamp(15px, 1.5vw, 26px)/1 var(--wide);
  font-stretch: 135%;
  letter-spacing: -.01em;
  text-transform: uppercase;
  white-space: nowrap;
  background:
    radial-gradient(130% 95% at 50% -32%, rgba(255, 255, 255, .92) 0 46%, rgba(255, 255, 255, 0) 47%),
    linear-gradient(180deg, var(--a2), var(--a6));
  box-shadow:
    inset 0 0 0 .07em rgba(255, 255, 255, .8),
    inset 0 -.45em .7em rgba(255, 255, 255, .42),
    0 .3em 1.2em var(--hot),
    0 0 0 .05em rgba(0, 0, 0, .28);
  animation: tapper-bob 1.1s var(--snap, ease-in-out) infinite;
  transition: opacity .5s, scale .5s;
}
#tapper span { display: inline-block; animation: tapper-pop 1.1s ease-in-out infinite; }
#tapper span + span { animation-delay: .22s; }
#tapper.done, #tapper.away { opacity: 0; scale: .8; }
.gated #tapper, .off-tapper #tapper, .off-tap #tapper { display: none; }
@keyframes tapper-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-.32em); } }
@keyframes tapper-pop { 0%, 55%, 100% { transform: scale(1); } 22% { transform: scale(1.22); } }
@media (prefers-reduced-motion: reduce) { #tapper, #tapper span { animation: none; } }
