/* tethery.app's home page. The design is the owner's chosen mockups in
   docs/brand/mockups/favorites/, each a 1344 x 752 screen, and this sheet reproduces them: every
   size below is in --u, which is one pixel of that 1344-wide frame, so at 1344 the page sits on
   the mockups' own coordinates and at other widths it scales with them (to 1.2x on wide screens).
   Under 900 px the page stacks for phones. One column grid runs the length of the page, and the
   light behind it is one moving mesh gradient (mesh.js), so no section has a background of its own
   and nothing is cut where one meets the next. The rest of the site still uses site.css. */

@font-face { font-family: "Satoshi"; src: url("/fonts/Satoshi-Variable.woff2?v=e739aff9b4d02c26") format("woff2"); font-weight: 300 900; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url("/fonts/IBMPlexMono-300.woff2?v=d2ce22595d55a52b") format("woff2"); font-weight: 300; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url("/fonts/IBMPlexMono-400.woff2?v=08949f728dc52d52") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url("/fonts/IBMPlexMono-400-ext.woff2?v=6bc0f226a5b7884a") format("woff2"); font-weight: 400; font-display: swap; unicode-range: U+0100-024F, U+1E00-1EFF; }
@font-face { font-family: "IBM Plex Mono"; src: url("/fonts/IBMPlexMono-500.woff2?v=01d285447409c8a5") format("woff2"); font-weight: 500; font-display: swap; }

:root {
  --u: calc(min(100vw, 1612px) / 1344);
  --ground: #050608;
  --ink: #F1F2F4;
  --ink-2: #B9BCC2;
  --muted: #8E9298;
  --tally: #FA4C33;
  --tally-hi: #FF6650;
  --line: rgba(255, 255, 255, 0.075);
  --rule: rgba(255, 255, 255, 0.2);
  --sans: "Satoshi", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* a strong ease-out, for things arriving */
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; background: var(--ground); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
html[data-motion="still"] { scroll-behavior: auto; }
/* The wheel's glide (scroll.js, Lenis): what its own stylesheet asks of the page. */
html.lenis, html.lenis body { height: auto; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-smooth iframe { pointer-events: none; }
body { margin: 0; background: var(--ground); color: var(--ink); font: 400 16px/1.5 var(--sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; overflow-x: hidden; }
a { color: inherit; }
/* "Privacy Policy" never breaks between its two words: a link over two lines is two boxes, and the specks
   that outline a link on hover (mesh.js) drew one box round both (the owner, 2026-10-24). */
a[href="/privacy"] { white-space: nowrap; }
:focus-visible { outline: 2px solid var(--tally); outline-offset: 3px; border-radius: 6px; }
::selection { background: rgba(250, 76, 51, 0.35); color: #fff; }
h1, h2, h3, p, ul, ol, figure, blockquote { margin: 0; padding: 0; }
/* No line ends the block with one word on it (the owner, 2026-10-24): the browser rebalances the last lines
   instead. Lines a design breaks by hand (br.wide) still break where they are put. */
h1, h2, h3, p, li, blockquote, figcaption { text-wrap: pretty; }
ul, ol { list-style: none; }
img { display: block; max-width: 100%; }

.home { position: relative; }
.frame { position: relative; width: calc(1344 * var(--u)); margin: 0 auto; }

/* The light: one canvas behind the whole page, fixed to the window (mesh.js). It fades in once it
   has drawn, so a slow start shows the plain ground rather than a flash. */
.mesh { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; display: block; opacity: 0; transition: opacity 900ms var(--ease); }
.mesh.is-live { opacity: 1; }
/* On a touch screen it is laid in the page instead and carried by the phone's own scrolling, a quarter
   of the window past each edge; mesh.js moves it to the window each time it draws. 100vh is the window
   with the browser's bars away, so their coming and going never resizes it. */
.mesh.is-paged { position: absolute; inset: auto; top: 0; left: 0; height: 150vh; will-change: transform; }


/* The column grid: five lines that run the length of the page, the first on the hero's left edge
   and the rest a column apart, 307 mockup pixels, so the last mirrors the first. Every heading and
   block starts on one of them. Each section draws the same five, so they meet without a seam. They
   sit over the light and under the words, by order alone: nothing on the page takes a z-index, so
   the photographs' lighten blends reach the light behind them. */
/* Where a line crosses the edge between two sections it carries a small cross, as a HUD marks its
   reticle: the top of each section draws the bar and the lower arm, the bottom of the one above
   draws the upper arm, so the two halves meet on the edge whatever each section clips. */
.lines { position: absolute; inset: 0; pointer-events: none; --g: linear-gradient(var(--line), var(--line)); --t: linear-gradient(var(--tick), var(--tick)); }
.lines::before, .lines::after { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: calc(1344 * var(--u)); transform: translateX(-50%); background-repeat: no-repeat; }
.lines::before { background-image: var(--g), var(--t), var(--t), var(--t), var(--g), var(--t), var(--t), var(--t); background-position: calc(58 * var(--u)) 0, calc(58 * var(--u) - 5px) 0, calc(58 * var(--u)) 0, calc(58 * var(--u)) 100%, calc(365 * var(--u)) 0, calc(365 * var(--u) - 5px) 0, calc(365 * var(--u)) 0, calc(365 * var(--u)) 100%; background-size: 1px 100%, 11px 1px, 1px 6px, 1px 6px, 1px 100%, 11px 1px, 1px 6px, 1px 6px; }
.lines::after { background-image: var(--g), var(--t), var(--t), var(--t), var(--g), var(--t), var(--t), var(--t), var(--g), var(--t), var(--t), var(--t); background-position: calc(672 * var(--u)) 0, calc(672 * var(--u) - 5px) 0, calc(672 * var(--u)) 0, calc(672 * var(--u)) 100%, calc(979 * var(--u)) 0, calc(979 * var(--u) - 5px) 0, calc(979 * var(--u)) 0, calc(979 * var(--u)) 100%, calc(1286 * var(--u)) 0, calc(1286 * var(--u) - 5px) 0, calc(1286 * var(--u)) 0, calc(1286 * var(--u)) 100%; background-size: 1px 100%, 11px 1px, 1px 6px, 1px 6px, 1px 100%, 11px 1px, 1px 6px, 1px 6px, 1px 100%, 11px 1px, 1px 6px, 1px 6px; }

/* Glass. The light moves behind everything, so the things that sit on it are panes: a little
   frosted, lit along their top edge, with a hairline border. Panels that hold only words carry
   Payload's fine diagonal scanlines at a few percent, the same hairlines the light shows. */
:root { --tick: rgba(255, 255, 255, 0.34);
  --scan: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='5' height='5'%3E%3Cpath d='M4 0h1v1H4zM3 1h1v1H3zM2 2h1v1H2zM1 3h1v1H1zM0 4h1v1H0z' fill='%23fff' fill-opacity='.07'/%3E%3C/svg%3E");
  --bracket: rgba(255, 255, 255, 0.62); --b: linear-gradient(var(--bracket), var(--bracket)); }
/* Corner brackets, as a viewfinder frames its subject. Every panel on the site wears them (the film's frame,
   the free band, the installers' pane, /pricing's board of features), and the grid's crosses are kept for
   where one section meets the next (the owner, 2026-10-24). */
.brackets::after { content: ""; position: absolute; inset: -1px; pointer-events: none; background-repeat: no-repeat;
  background-image: var(--b), var(--b), var(--b), var(--b), var(--b), var(--b), var(--b), var(--b);
  background-size: 12px 1px, 1px 12px, 12px 1px, 1px 12px, 12px 1px, 1px 12px, 12px 1px, 1px 12px;
  background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%; }

/* ------------------------------------------------------------------ the top bar */
/* In bar.css, which every page of the site loads (templates/partials/bar.html). */

/* ------------------------------------------------------------------ buttons */
/* Liquid glass. Each button is a lens over the light behind it: the body is mostly clear and
   blurs and saturates what it sits on, so the specks show through it. What makes it read as glass
   is light, not paint: a rim that is brightest on the edge facing the pointer, a sheen across the
   top, light gathering inside the bottom edge where it leaves the glass, and a soft highlight that
   follows the pointer over the surface (home.js sets --mx, --my and --rim). The download is the
   same glass tinted tally, so it still stands out. The rim and the highlight sit behind the words
   (z-index -1 inside the button's own stacking context), so they light the glass, not the label.
   The corners match the bar's download button: everything that frames something on this page is
   square-cornered or nearly, and the bar's two curves run parallel. */
@property --rim { syntax: "<angle>"; inherits: true; initial-value: 165deg; }
.button { --mx: 50%; --my: -40%; --lit: 0; --rim: 165deg; isolation: isolate;
  transition: --rim 500ms var(--ease), background-color 320ms var(--ease), box-shadow 320ms var(--ease), transform 460ms cubic-bezier(0.25, 1.3, 0.45, 1); }
.button::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; padding: 1px; pointer-events: none;
  background: linear-gradient(var(--rim), rgba(255, 255, 255, var(--rim-a)) 0%, rgba(255, 255, 255, calc(var(--rim-a) * 0.25)) 22%, rgba(255, 255, 255, 0.02) 48%, rgba(255, 255, 255, calc(var(--rim-b) * 0.3)) 76%, rgba(255, 255, 255, var(--rim-b)) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; }
.button::after { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; pointer-events: none;
  background: radial-gradient(calc(110 * var(--u)) circle at var(--mx) var(--my), rgba(255, 255, 255, var(--spec)), rgba(255, 255, 255, 0) 70%);
  mix-blend-mode: screen; mix-blend-mode: plus-lighter; opacity: var(--lit); transition: opacity 320ms var(--ease); }
.button:hover { --lit: 1; }
/* Pressed, it gives at once; let go, it springs back a touch past where it was. */
.button:active { transform: scale(0.965); transition-duration: 500ms, 320ms, 320ms, 90ms; transition-timing-function: var(--ease), var(--ease), var(--ease), ease-out; }

.button { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: calc(11 * var(--u)); height: calc(48.5 * var(--u)); padding: 0 calc(26 * var(--u)); border: 0; border-radius: calc(12 * var(--u));
  font: 500 calc(16 * var(--u))/1 var(--sans); color: #fff; text-decoration: none; cursor: pointer; text-shadow: 0 1px 1px rgba(90, 10, 0, 0.25);
  --rim-a: 0.85; --rim-b: 0.45; --spec: 0.34;
  /* At rest a see-through tint, saturated, with its colour gathered at the edges so it stays
     luminous while what is behind shows through the middle; hovered, it thickens to nearly solid.
     The colour is a background colour, which can be animated, and the sheen an image over it. */
  background-color: rgba(255, 64, 34, 0.5);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0) 52%, rgba(255, 150, 120, 0.14));
  -webkit-backdrop-filter: blur(10px) saturate(220%); backdrop-filter: blur(10px) saturate(220%);
  box-shadow: inset 0 0 calc(16 * var(--u)) rgba(255, 78, 46, 0.6), inset 0 0 calc(5 * var(--u)) rgba(255, 190, 170, 0.28), inset 0 1px 1px rgba(255, 255, 255, 0.5), inset 0 10px 14px -12px rgba(255, 255, 255, 0.45), inset 0 -14px 18px -12px rgba(255, 196, 176, 0.6), inset 0 -1px 1px rgba(255, 255, 255, 0.2),
    0 2px 6px -2px rgba(40, 4, 0, 0.6), 0 12px 32px -12px rgba(250, 76, 51, 0.7); }
.button:hover { background-color: rgba(255, 72, 42, 0.93);
  box-shadow: inset 0 0 calc(16 * var(--u)) rgba(255, 96, 64, 0.5), inset 0 0 calc(5 * var(--u)) rgba(255, 200, 180, 0.34), inset 0 1px 1px rgba(255, 255, 255, 0.6), inset 0 10px 14px -12px rgba(255, 255, 255, 0.5), inset 0 -14px 18px -12px rgba(255, 206, 188, 0.7), inset 0 -1px 1px rgba(255, 255, 255, 0.24),
    0 2px 6px -2px rgba(40, 4, 0, 0.6), 0 14px 38px -12px rgba(250, 76, 51, 0.85); }
.button svg { width: calc(17 * var(--u)); height: calc(17 * var(--u)); fill: currentColor; margin-top: -2px; }
/* Clear glass: nearly no colour of its own, so what it says is carried by the light behind it. */
.button--quiet { --rim-a: 0.6; --rim-b: 0.24; --spec: 0.2; text-shadow: none;
  background-color: rgba(255, 255, 255, 0.025);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0) 60%);
  -webkit-backdrop-filter: blur(14px) saturate(170%) brightness(1.15); backdrop-filter: blur(14px) saturate(170%) brightness(1.15);
  box-shadow: inset 0 0 calc(8 * var(--u)) rgba(255, 255, 255, 0.07), inset 0 1px 1px rgba(255, 255, 255, 0.24), inset 0 10px 14px -12px rgba(255, 255, 255, 0.18), inset 0 -14px 18px -12px rgba(255, 255, 255, 0.12),
    0 2px 6px -2px rgba(0, 0, 0, 0.5), 0 12px 30px -14px rgba(0, 0, 0, 0.8); }
.button--quiet:hover { background-color: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 0 calc(8 * var(--u)) rgba(255, 255, 255, 0.1), inset 0 1px 1px rgba(255, 255, 255, 0.3), inset 0 10px 14px -12px rgba(255, 255, 255, 0.22), inset 0 -14px 18px -12px rgba(255, 255, 255, 0.18),
    0 2px 6px -2px rgba(0, 0, 0, 0.5), 0 14px 34px -14px rgba(0, 0, 0, 0.85); }
.button--quiet svg { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linejoin: round; }
/* In Chrome the glass is a lens (home.js), which adds red light to what shows through the download.
   Elsewhere there is no lens to light it, so its tint carries more of the colour. */
html:not(.has-lens) .button:not(.button--quiet):not(:hover) { background-color: rgba(255, 64, 34, 0.7); }
/* Where the reader asks for less see-through, the glass goes solid. */
@media (prefers-reduced-transparency: reduce) {
  .button, .button--quiet { -webkit-backdrop-filter: none; backdrop-filter: none; }
  .button, .button:hover { background-color: #F04A31; }
  .button--quiet, .button--quiet:hover { background-color: #1C1D21; }
}
.actions { display: flex; flex-wrap: wrap; gap: calc(22 * var(--u)); }
/* The walkthrough's heading wraps its button and adds nothing to how it looks. */
.hero__how { display: flex; font: inherit; }
/* The arrow after the words sits on their x-height, not on the line box the play mark was nudged for. */
.hero__how svg { margin-top: 0; transform: translateY(0.08em); }

/* The motion switch (motion.js): clear glass in the corner, over everything, for a reader who needs
   the page to hold still. At rest it is the icon alone, so it does not compete with the page;
   pointed at or focused, it grows to the left to say what pressing it does. The words are always
   there for a screen reader, only clipped. Paused, it grows at once. Without a script there is
   nothing for it to stop, so it is not shown. */
.button.motion { position: fixed; z-index: 60; right: max(16px, env(safe-area-inset-right)); bottom: max(16px, env(safe-area-inset-bottom));
  height: 36px; padding: 0 11px; gap: 0; border-radius: 10px; font: 500 13px/1 var(--sans); color: #D4D6DA; }
.button.motion:hover { color: #fff; }
.button.motion svg { flex: none; width: 14px; height: 14px; margin-top: 0; }
.motion span { max-width: 0; margin-left: 0; opacity: 0; overflow: hidden; white-space: nowrap;
  transition: max-width 360ms var(--ease), margin-left 360ms var(--ease), opacity 220ms var(--ease); }
.motion:hover span, .motion:focus-visible span { max-width: 96px; margin-left: 8px; opacity: 1; transition-delay: 0ms, 0ms, 80ms; }
html[data-motion="still"] .motion span { transition: none; }
.motion__play { display: none; }
html[data-motion="still"] .motion__pause { display: none; }
html[data-motion="still"] .motion__play { display: block; }
html:not([data-motion]) .motion { display: none; }

/* ------------------------------------------------------------------ hero (1c-hero-3d-scene) */
.hero { position: relative; height: calc(752 * var(--u)); overflow: hidden; }
/* The scene is opaque and as dark as the ground, so lighten lets the page's light show through
   wherever the scene is darker than it, and its foot fades into the page. */
.hero-scene { position: absolute; inset: 0; z-index: 0; mix-blend-mode: lighten;
  -webkit-mask-image: linear-gradient(to bottom, #000 82%, transparent); mask-image: linear-gradient(to bottom, #000 82%, transparent); }
.hero-scene canvas { display: block; width: 100% !important; height: 100% !important; }
/* Before the scene draws, and without WebGL or under reduced motion's first frame, the still. */
.hero-scene::before { content: ""; position: absolute; inset: 0; background: url("/img/hero-poster.webp?v=2113a692c36c1346") center / cover no-repeat; transition: opacity 600ms var(--ease); }
.hero-scene.is-live::before { opacity: 0; }
/* In the hero only the two lines beside the words are drawn; the three that would cross the scene
   come in under it, where the floor fades. */
/* The grid starts here: the two lines beside the words fade in from under the top bar. */
.hero .lines::before { -webkit-mask-image: linear-gradient(to bottom, transparent calc(90 * var(--u)), #000 calc(340 * var(--u))); mask-image: linear-gradient(to bottom, transparent calc(90 * var(--u)), #000 calc(340 * var(--u))); }
.hero .lines::after { -webkit-mask-image: linear-gradient(to bottom, transparent calc(100% - 150 * var(--u)), #000); mask-image: linear-gradient(to bottom, transparent calc(100% - 150 * var(--u)), #000); }
.hero__in { height: 100%; pointer-events: none; }
.hero__in > * { pointer-events: auto; }
.hero h1 { position: absolute; left: calc(58 * var(--u)); top: calc(222 * var(--u)); width: calc(600 * var(--u));
  /* 45, not the mockup's 47: "Premiere Pro and After Effects," (the owner added "Pro", 2026-10-24) has to
     hold one line in the mockup's 600, or the title takes three lines and runs into the lead. */
  font: 600 calc(45 * var(--u))/calc(52 * var(--u)) var(--sans); letter-spacing: -0.032em; color: #F3F3F3; }
.hero__lead { position: absolute; left: calc(58 * var(--u)); top: calc(347 * var(--u)); width: calc(500 * var(--u));
  font: 450 calc(17.5 * var(--u))/calc(26 * var(--u)) var(--sans); color: #C4C7CC; letter-spacing: -0.006em; }
.hero .actions { position: absolute; left: calc(58 * var(--u)); top: calc(439 * var(--u)); }
/* At least 200 wide, as the Mac's has always been, and wider for words that need it: "Download for Windows"
   in a fixed 200 put its logo against the edge (the owner, 2026-11-02). */
.hero .button { min-width: calc(200 * var(--u)); padding: 0 calc(24 * var(--u)); white-space: nowrap; }
.hero .button--quiet { width: auto; padding: 0 calc(24 * var(--u)); white-space: nowrap; }
/* Under the buttons, what the download is: the whole product, free, until a license takes the
   watermark off (the owner, 2026-10-19). */
.hero__note { position: absolute; left: calc(58 * var(--u)); top: calc(505 * var(--u)); font: 400 calc(12 * var(--u))/1.4 var(--mono); letter-spacing: 0.02em; color: var(--muted); }
/* Where it wraps (a phone), the two lines are even, not merely free of a word alone (the owner, 2026-10-24). */
.hero__note { text-wrap: balance; }

/* The guided sequence, on a wide window or a phone held upright. hero.ts sets .is-journey,
   .is-step once a step is past halfway, and data-words (below); while the sequence holds the page,
   html.in-sequence. The chapter is laid out on the mockup's pixels after
   round3/seq-01-checking-a-cut.png: the rail on the first column line, and the words in a block 236
   wide whose right edge is on the second (the owner, 2026-10-11), left-aligned inside it. */
.hero__stage, .hero__words { display: contents; }
.journey { display: none; }
html.in-sequence { overflow: hidden; }
/* While the sequence holds the page, a finger on the hero is the sequence's: it drags the phone in the
   scan rather than panning or zooming the page (hero.ts reads it). */
html.in-sequence [data-hero] { touch-action: none; }
.is-journey .journey { display: block; position: absolute; inset: 0; pointer-events: none; }
.journey__ch { display: contents; }
.is-step .hero__in > * { pointer-events: none; }
.is-step .journey__actions { pointer-events: auto; }
/* The scrub cursor is for the timeline: in the first chapter, and in the second once the session is
   live (.is-scrub); the round trip has nothing to scrub. */
.is-step.is-scrub .hero-scene { cursor: ew-resize; }
/* On a wide window the sequence fills the window, so the scene runs off its foot rather than ending in a
   hard edge where the hero's box did (the owner, 2026-10-11), and only the last few pixels fade.
   hero.ts frames everything for the box's own height from the top, so the box growing moves nothing. */
@media (min-width: 900px) {
  html.in-sequence .is-journey { height: max(calc(752 * var(--u)), 100vh); }
  /* The scene is that tall from the start, clipped by the hero's box until the sequence grows the box to
     it, so entering never resizes the renderer (2026-10-14). A resize clears the canvas and reallocates
     the scene's render targets; on an iPad that stalled long enough for the scene to blink out and the
     headline's fade to pass unseen. Its foot still fades where the box's foot is. */
  .is-journey .hero-scene { bottom: auto; height: max(calc(752 * var(--u)), 100vh);
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(752 * 0.82 * var(--u)), transparent calc(752 * var(--u)));
    mask-image: linear-gradient(to bottom, #000 calc(752 * 0.82 * var(--u)), transparent calc(752 * var(--u))); }
  /* The hero's grid lines are not drawn: the three across the scene come in under the hero where its floor
     fades, and the one beside the words was crossed by the laptop's base, all with the scene running to
     the foot of the window (the owner, 2026-10-12). The rail keeps its own line. They fade rather than
     vanish, so ending the sequence (Explore Tethery, a swipe or the wheel) does not snap them back into
     the hero as it scrolls away (the owner, 2026-10-23). */
  .is-journey .lines { transition: opacity 900ms var(--ease); }
  html.in-sequence .is-journey .lines { opacity: 0; }
  html.in-sequence .is-journey .hero-scene { -webkit-mask-image: linear-gradient(to bottom, #000 94%, transparent); mask-image: linear-gradient(to bottom, #000 94%, transparent); }
}
.journey__title, .journey__body, .journey__actions { position: absolute; left: calc((365 - 236) * var(--u)); margin: 0; }
/* The words answer the gesture, not the camera (2026-10-11). The moment a step is taken, hero.ts sets
   data-words on the hero to the chapter it is going to (0 is the hero's own headline), and each piece
   of text says whose it is (data-ch; "all" for the rail, the label and the buttons). What is showing
   leaves at once, in 200 ms, upwards going on and downwards going back. The chapter's own come in over
   400 ms on a strong ease-out, --jdelay into the move (hero.ts: half of it), so they are in as the
   camera settles. Tied to the step's progress, the old words took 0.86 s to go and the new 0.86 s to
   come, both linear, and a jump showed the chapters it passed. Transitions also turn round from
   wherever they are when a step is reversed. */
.is-journey .hero__in, .journey [data-ch] { transition: opacity 200ms ease-out, transform 200ms ease-out; }
.is-journey:not([data-words="0"]) .hero__in { opacity: 0; transform: translateY(-12px); }
.journey [data-ch] { opacity: 0; transform: translateY(16px); }
[data-words="2"] .journey [data-ch="1"], [data-words="3"] .journey [data-ch="1"], [data-words="3"] .journey [data-ch="2"] { transform: translateY(-10px); }
.is-journey[data-words="0"] .hero__in,
[data-words="1"] .journey [data-ch="1"], [data-words="2"] .journey [data-ch="2"], [data-words="3"] .journey [data-ch="3"],
[data-words="1"] .journey [data-ch="all"], [data-words="2"] .journey [data-ch="all"], [data-words="3"] .journey [data-ch="all"] {
  opacity: 1; transform: none; transition: opacity 400ms var(--ease-out) var(--jdelay, 0ms), transform 400ms var(--ease-out) var(--jdelay, 0ms); }
html[data-motion="still"] .is-journey .hero__in, html[data-motion="still"] .journey [data-ch], html[data-motion="still"] .is-journey .lines { transition: none; }
/* While the phone waits in front of the Mac for the reader to move it over the code (.is-scan). */
.journey__hint { position: absolute; left: calc((365 - 236) * var(--u)); top: calc(478 * var(--u)); width: calc(236 * var(--u)); margin: 0;
  font: 500 calc(12.5 * var(--u))/calc(20 * var(--u)) var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--tally);
  opacity: 0; transform: translateY(8px); transition: opacity 360ms var(--ease), transform 360ms var(--ease); }
.journey__hint span { color: #8A93A0; }
.is-scan .journey__hint { opacity: 1; transform: none; }
.is-scan .hero-scene { cursor: grab; }
html[data-motion="still"] .journey__hint { transition: none; }
/* The log: two rows show, the version and the one before (the owner, 2026-10-11: four, in a box wider
   than its words). A row is the version, the time so far and what it is doing, then, once the phone has
   shown it, what is wrong with it, in red. The one before is struck through, dimmed and pushed up as
   each new one slides in under it (--row, from hero.ts). Each row is as wide as its words, so the strike
   is too. */
.journey__log { --row-h: calc(27 * var(--u)); position: absolute; left: calc((365 - 236) * var(--u)); top: calc(458 * var(--u)); width: calc(280 * var(--u)); height: calc(2 * var(--row-h)); margin: 0;
  padding: 0; list-style: none; overflow: hidden; font: 500 calc(11.5 * var(--u))/1 var(--mono); }
.journey__log li { position: absolute; left: 0; top: 0; height: var(--row-h);
  display: flex; align-items: center; gap: calc(12 * var(--u)); white-space: nowrap;
  transform: translateY(calc(var(--row, 0) * var(--row-h))); transition: transform 300ms var(--ease), opacity 300ms var(--ease); }
.journey__log li.is-new { transform: translateY(calc((var(--row, 0) + 0.7) * var(--row-h))); opacity: 0; transition: none; }
.journey__log li[style*="--row: -"] { opacity: 0; }
.log__v { width: calc(22 * var(--u)); letter-spacing: 0.06em; color: var(--tally); }
.log__t { font-size: calc(14.5 * var(--u)); letter-spacing: 0.02em; color: #EDEFF2; font-variant-numeric: tabular-nums; }
.log__what { position: relative; letter-spacing: 0.08em; text-transform: uppercase; color: #8A93A0; }
.is-found .log__what { color: var(--tally); }
/* A version that needed another: struck through, the line drawn across it, and dimmed. */
.journey__log li.is-struck { opacity: 0.42; }
/* The strike is drawn by scaling it out from the left, not by growing its width, which lays the row
   out again on every frame of it. */
.journey__log li::after { content: ""; position: absolute; left: 0; top: 50%; height: 1px; width: 100%; background: rgba(237, 239, 242, 0.75);
  transform: scaleX(0); transform-origin: left; transition: transform 420ms var(--ease); }
.journey__log li.is-struck::after { transform: none; }
.journey__log li.is-struck .log__v { color: #8A93A0; }
html[data-motion="still"] .journey__log li, html[data-motion="still"] .journey__log li::after { transition: none; }
.journey__title { top: calc(200 * var(--u)); width: calc(236 * var(--u)); font: 600 calc(47 * var(--u))/calc(50 * var(--u)) var(--sans); letter-spacing: -0.032em; color: #F3F3F3; }
.journey__body { top: calc(322 * var(--u)); width: calc(236 * var(--u)); font: 400 calc(13.5 * var(--u))/calc(22 * var(--u)) var(--mono); color: #A4A8AF; }
.journey__actions { top: calc(606 * var(--u)); display: flex; align-items: center; gap: calc(26 * var(--u)); }
.journey__next { width: calc(112 * var(--u)); height: calc(38 * var(--u)); padding: 0; gap: 10px; font-size: calc(14 * var(--u)); }
.journey__next svg { width: 15px; height: 15px; }
.hero .journey__next { width: calc(169 * var(--u)); }
.journey__skip { font: 400 calc(12.5 * var(--u))/1 var(--mono); color: #8A93A0; text-decoration: none; }
.journey__skip:hover { color: #EDEFF2; }
/* The end of the session chapter (.is-more, hero.ts), and of the sequence: the problem shown and answered,
   an invitation to scroll on to the rest (the owner, 2026-10-13, after round4/ch3-more-b-cta-cards.png). On a wide window it stands in the
   scene, under the phone, from the grid's fourth line; on a phone it takes the chapter's line, and Next is
   its button. It comes in a line at a time as the tether settles, the eyebrow, the line and the button,
   so it reads as the next thing to do; it leaves at once. */
.journey__more { position: absolute; left: calc(979 * var(--u)); top: calc(418 * var(--u)); width: calc(307 * var(--u)); z-index: 2; pointer-events: none; }
/* These replace the button's own transitions, so they carry its colour too: without it the hover's
   near-solid red snapped back to glass the moment the invitation went inert. Transform is last, as in
   the button's own list, so .button:active's four durations still press it in 90 ms. */
.journey__more > * { opacity: 0; transform: translateY(14px); transition: opacity 320ms var(--ease), background-color 320ms var(--ease), box-shadow 320ms var(--ease), transform 320ms var(--ease); }
.is-more .journey__more { pointer-events: auto; }
.is-more .journey__more > * { opacity: 1; transform: none; transition: opacity 650ms var(--ease-out), background-color 320ms var(--ease), box-shadow 320ms var(--ease), transform 650ms var(--ease-out); }
.is-more .journey__more > :nth-child(2) { transition-delay: 140ms; }
.is-more .journey__more > :nth-child(3) { transition-delay: 300ms; }
.journey__more-eye { display: flex; align-items: center; gap: calc(10 * var(--u)); margin: 0; font: 500 calc(12.5 * var(--u))/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--tally); }
/* A live dot beside Connected (the owner, 2026-10-13), breathing as the viewer's playing dot does
   (viewer.css, `rolling`): full and ringed, then four tenths with the ring spread, every two seconds. */
.journey__more-eye::before { content: ""; flex: none; width: calc(7 * var(--u)); height: calc(7 * var(--u)); border-radius: 50%; background: currentColor;
  box-shadow: 0 0 0 calc(3 * var(--u)) rgba(250, 76, 51, 0.22), 0 0 10px rgba(250, 76, 51, 0.6); animation: journey-live 2s ease-in-out infinite; }
@keyframes journey-live { 50% { opacity: 0.4; box-shadow: 0 0 0 calc(5 * var(--u)) rgba(250, 76, 51, 0.08), 0 0 6px rgba(250, 76, 51, 0.3); } }
html[data-motion="still"] .journey__more-eye::before { animation: none; }
.journey__more-title { margin: calc(14 * var(--u)) 0 0; font: 600 calc(40 * var(--u))/calc(43 * var(--u)) var(--sans); letter-spacing: -0.03em; color: #F3F3F3;
  text-shadow: 0 2px 24px rgba(4, 5, 6, 0.7); }
.hero .journey__explore { margin-top: calc(24 * var(--u)); width: calc(220 * var(--u)); }
.journey__explore svg { fill: none; }
html[data-motion="still"] .journey__more > * { transition: none; }
/* Where the reader is (the owner, 2026-10-14, after round6/stages-a): the story's three parts as stops on a
   thin line under the site's bar, each a dot with its number and name under it, the part showing in red with
   a glow, the ones before it red without, the ones to come hollow. The line fills in red to the part showing
   and glides there (--fill, hero.ts). It is the anchor: the chapters' words move to wherever their view
   leaves room, and this stays put. It replaced a rail of 01 to 03 down the window's left edge and then a pill
   of three tabs, which with the site's bar above it read as two rounded bars. */
.journey__rail { --step: calc(176 * var(--u)); --dot: calc(9 * var(--u)); position: absolute; left: calc((365 - 236) * var(--u)); top: calc(112 * var(--u)); z-index: 60;
  margin: 0; padding: 0; list-style: none; display: flex; }
.is-step .journey__rail { pointer-events: auto; }
.journey__rail::before, .journey__rail::after { content: ""; position: absolute; left: calc(var(--dot) / 2); top: calc(var(--dot) / 2); height: 1px; pointer-events: none; }
.journey__rail::before { width: calc(2 * var(--step)); background: rgba(237, 239, 242, 0.16); }
.journey__rail::after { width: var(--fill, 0); background: var(--tally); box-shadow: 0 0 6px rgba(250, 76, 51, 0.7); }
.journey__rail.is-set::after { transition: width 620ms var(--ease); }
html[data-motion="still"] .journey__rail::after { transition: none; }
.journey__rail li { position: relative; z-index: 1; width: var(--step); }
.journey__rail li:last-child { width: auto; }
.journey__rail button { position: relative; display: block; margin: 0; border: 0; background: none; cursor: pointer; white-space: nowrap; text-align: left;
  padding: calc(var(--dot) + 15 * var(--u)) 0 0; font: 500 calc(11 * var(--u))/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase;
  color: #5d636c; transition: color 300ms var(--ease); -webkit-tap-highlight-color: transparent; }
.journey__rail button::before { content: ""; position: absolute; left: 0; top: 0; width: var(--dot); height: var(--dot); box-sizing: border-box; border-radius: 50%;
  border: 1px solid rgba(237, 239, 242, 0.35); background: #0b0c0e; transition: background-color 300ms var(--ease), border-color 300ms var(--ease), box-shadow 300ms var(--ease); }
.journey__rail li.is-done button { color: #9198A2; }
.journey__rail li.is-done button::before { border-color: transparent; background: rgba(250, 76, 51, 0.75); }
.journey__rail li.is-on button { color: var(--tally); }
.journey__rail li.is-on button::before { border-color: transparent; background: var(--tally); box-shadow: 0 0 0 calc(3 * var(--u)) rgba(250, 76, 51, 0.18), 0 0 12px rgba(250, 76, 51, 0.8); }
.journey__rail li:not(.is-on) button:hover { color: #C9CDD3; }
.journey__rail button:focus-visible { outline: 1px solid rgba(237, 239, 242, 0.5); outline-offset: 4px; }
html[data-motion="still"] .journey__rail button, html[data-motion="still"] .journey__rail button::before { transition: none; }
/* Centred under the bar instead (to compare). */
html[data-rail="centre"] .journey__rail { left: 50%; translate: calc(-1 * var(--step) - var(--dot) / 2) 0; }
@media (min-width: 900px) {
  /* Each chapter's words go where its view leaves room (the owner, 2026-10-14): editing as it is and the
     session on the left, the round trip top right over the devices, which sit large and low to the left.
     Next and Exit go with the words. They are one pair for every chapter, so rather than slide across
     the window they come in again at their new place, on the words' own time (--jdelay), with an
     animation named for the chapter so each change of chapter restarts it. */
  .journey__title--trip, .journey__body--trip { left: calc(50% - 190 * var(--u)); width: calc(380 * var(--u)); text-align: center; }
  .journey__title--trip { top: calc(296 * var(--u)); }
  .journey__body--trip { top: calc(376 * var(--u)); }
  [data-words="2"] .journey__actions { left: 50%; top: calc(504 * var(--u)); translate: -50% 0; }
  [data-words="1"] .journey .journey__actions { animation: words-in-1 400ms var(--ease-out) var(--jdelay, 0ms) both; }
  [data-words="2"] .journey .journey__actions { animation: words-in-2 400ms var(--ease-out) var(--jdelay, 0ms) both; }
  [data-words="3"] .journey .journey__actions { animation: words-in-3 400ms var(--ease-out) var(--jdelay, 0ms) both; }
  html[data-motion="still"] .journey .journey__actions { animation: none; }
  /* The end of the sequence: the invitation is the only thing to read or press (the owner, 2026-10-14). The
     session chapter's words and Next and Exit leave once the phone has joined (.is-joined, hero.ts), as the
     camera pulls back to the view whose Mac fills the room they stood in; the pill stays to say
     the story is done, and Escape still leaves. On a phone the invitation takes the chapter's line instead
     (below), and Next is its button. */
  .is-joined[data-words="3"] .journey .journey__title--sess, .is-joined[data-words="3"] .journey .journey__body--sess,
  .is-joined[data-words="3"] .journey .journey__actions {
    opacity: 0; transform: translateY(-10px); visibility: hidden; animation: none;
    transition: opacity 300ms ease-out, transform 300ms ease-out, visibility 0s 300ms; }
}
/* The round trip's road: no line of its own, only the specks that fill it (mesh.js). */
.journey__road { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.journey__road path { fill: none; stroke: none; }
@keyframes words-in-1 { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes words-in-2 { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes words-in-3 { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
/* The sequence on a phone held upright (the owner, 2026-10-11: the scene on top, the words below).
   In the sequence the scene fills the screen behind everything (as it does in the hero) and the camera frames
   each chapter in the room between the progress bar at the top and that chapter's words at the foot, so
   the Mac is as wide as the screen allows and nothing cuts it off. The words sit at the foot, over a
   darkening of the floor; the buttons are on the right, and the pause switch on the left. */
@media (max-width: 899px) {
  .is-journey:not([data-words="0"]) { min-height: 100svh; }
  .is-journey .journey { z-index: 1; bottom: auto; height: 100svh; box-sizing: border-box; padding: 0 20px max(16px, env(safe-area-inset-bottom));
    display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; grid-template-areas: "text" "actions"; }
  .is-journey:not([data-words="0"]) .journey::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 50%;
    background: linear-gradient(rgba(4, 5, 6, 0), rgba(4, 5, 6, 0.85) 45%); pointer-events: none; z-index: -1; }
  .journey__ch { display: flex; flex-direction: column; grid-area: text; align-self: end; }
  .journey__title, .journey__body, .journey__log, .journey__hint, .journey__actions { position: relative; left: auto; top: auto; width: auto; }
  .journey__title { font-size: 32px; line-height: 36px; }
  .journey__body { margin-top: 10px; font-size: 13.5px; line-height: 21px; }
  .journey__log, .journey__hint { margin-top: 16px; }
  /* The round trip's log of versions under the progress bar, so its words sit at the foot like every
     other chapter's, two rows, the version and the one before (the owner, 2026-10-11). */
  .journey__log { --row-h: 22px; position: absolute; top: 84px; left: 20px; right: 20px; width: auto; height: calc(2 * var(--row-h)); margin: 0; }
  .journey__actions { grid-area: actions; flex-direction: row-reverse; justify-content: flex-start; gap: 20px; margin-top: 18px; }
  .journey .journey__next { width: 132px; height: 44px; font-size: 16px; }
  .journey__skip { font-size: 14px; padding: 12px 4px; }
  /* Where the reader is: a count beside the chapter's title, in red (the owner, 2026-10-11: the bar in four
     under the site's bar, and its label, said it less plainly and took room from the scene). */
  .journey__rail { display: none; }
  /* The end of the session chapter on a phone: the invitation takes the chapter's place, and Next is its
     button. The chapter's title and line leave as the other chapters' words do, and the invitation fades
     in where they stood, a line at a time (the owner, 2026-10-16: it appeared at once, under the title,
     and the three headings stacked). Next turns the call to action's colour and says where it goes. */
  .journey__ch--sess { position: relative; }
  .journey__more { position: absolute; left: 0; right: 0; top: auto; bottom: 0; width: auto; }
  .is-more[data-words="3"] .journey .journey__title--sess, .is-more[data-words="3"] .journey .journey__body--sess {
    opacity: 0; transform: translateY(-10px); transition: opacity 200ms ease-out, transform 200ms ease-out; }
  .is-more .journey__more > :nth-child(1) { transition-delay: 200ms; }
  .is-more .journey__more > :nth-child(2) { transition-delay: 340ms; }
  .journey__more-eye { font-size: 12px; }
  .journey__more-title { margin-top: 10px; font-size: 32px; line-height: 36px; }
  .journey__explore { display: none; }
  .is-more .journey .journey__next { width: auto; padding: 0 22px; }
  .is-more .journey__next-say { display: none; }
  .is-more .journey__next-more { display: inline; }
  .is-more .journey__next svg { rotate: 90deg; }
  .journey__title { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
  /* The count is drawn from the title's data-of, not written in it, so the heading says only its words
     to a search engine or a screen reader ("Checking a cut today.", not "Checking a cut today.1/3"). */
  .journey .journey__title::after { content: attr(data-of); display: inline; flex: none; font: 500 13px/1 var(--mono); letter-spacing: 0.08em; color: var(--tally); }
  /* The pause switch on the left, the height of the buttons beside it. In the sequence it is placed from
     the top of the same small viewport as they are: from the bottom, a browser's toolbar covered it. */
  .button.motion { right: auto; left: max(20px, env(safe-area-inset-left)); bottom: max(16px, env(safe-area-inset-bottom)); height: 44px; min-width: 44px; justify-content: center; }
  html.in-sequence .button.motion { top: calc(100svh - max(16px, env(safe-area-inset-bottom)) - 44px); bottom: auto; }
}
/* A short phone (an iPhone SE is 667 tall): the words and the log a little tighter. */
@media (max-width: 899px) and (max-height: 760px) {
  .journey__title, .journey__more-title { font-size: 28px; line-height: 32px; }
  .journey__body { font-size: 13px; line-height: 19px; margin-top: 8px; }
  .journey__hint { margin-top: 12px; }
}
/* The scan on a touch screen, until the reader touches it: a finger presses on the phone, drags it onto
   the code and lifts, over and over (hero.ts places it from the phone and the code, --f0 and --f1). */
.journey__finger, .journey__next-more { display: none; }
.is-guide .journey__finger { display: block; position: absolute; left: 0; top: 0; z-index: 2; width: 44px; height: 44px; margin: -22px 0 0 -22px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.2); border: 2px solid rgba(255, 255, 255, 0.85); box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.08);
  animation: journey-finger 2.6s cubic-bezier(0.65, 0, 0.35, 1) infinite; }
@keyframes journey-finger {
  0% { opacity: 0; transform: translate(var(--f0x), var(--f0y)) scale(1.3); }
  14% { opacity: 1; transform: translate(var(--f0x), var(--f0y)) scale(1); }
  22% { opacity: 1; transform: translate(var(--f0x), var(--f0y)) scale(0.86); }
  70% { opacity: 1; transform: translate(var(--f1x), var(--f1y)) scale(0.86); }
  84%, 100% { opacity: 0; transform: translate(var(--f1x), var(--f1y)) scale(1.15); }
}
html[data-motion="still"] .is-guide .journey__finger { animation: none; transform: translate(var(--f0x), var(--f0y)); }
/* The hero at rest, however the walkthrough was left: the scan's ring on the Mac's screen, with a ripple
   leaving it, and the hints' voice under it saying a click plays it (hero.ts places it, --t0, and sets .is-try).
   It arrives once the page has settled. Over either screen the pointer says it can be clicked (.is-tryover). */
.journey__try { display: none; }
.is-try .journey__try { display: block; position: absolute; left: 0; top: 0; z-index: 2; transform: translate(var(--t0x), var(--t0y));
  pointer-events: auto; cursor: pointer; animation: journey-foot-in 600ms var(--ease) 1.4s both; }
.journey__tryring { position: absolute; left: -22px; top: -22px; width: 44px; height: 44px; border-radius: 50%; box-sizing: border-box;
  background: rgba(255, 255, 255, 0.2); border: 2px solid rgba(255, 255, 255, 0.85); box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.08);
  transition: background-color 200ms var(--ease); animation: journey-try 2.4s cubic-bezier(0.65, 0, 0.35, 1) infinite; }
.journey__tryring::after { content: ""; position: absolute; inset: -2px; border-radius: 50%; border: 2px solid rgba(255, 255, 255, 0.7);
  animation: journey-ripple 2.4s cubic-bezier(0.2, 0.6, 0.35, 1) infinite; }
.is-tryover .journey__tryring { background: rgba(255, 255, 255, 0.34); }
@keyframes journey-try { 0%, 100% { transform: scale(1); } 18% { transform: scale(0.86); } 36% { transform: scale(1); } }
@keyframes journey-ripple { 0%, 18% { opacity: 0; transform: scale(1); } 22% { opacity: 1; } 80%, 100% { opacity: 0; transform: scale(2.1); } }
/* (On a dark pill: bare, the words lay on Premiere's timeline and could not be read.) */
.journey__trysay { position: absolute; left: var(--tsay, 0px); top: 38px; translate: -50% 0; width: max-content; white-space: nowrap; padding: 5px 12px 6px; border-radius: 999px;
  background: rgba(8, 9, 10, 0.78); border: 1px solid rgba(255, 255, 255, 0.12); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  font: 500 max(11px, calc(12 * var(--u)))/1.5 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: #E6E8EB; }
.journey__trysay b { font-weight: 500; color: var(--tally); text-shadow: 0 0 14px rgba(255, 77, 61, 0.5), 0 0 3px rgba(255, 77, 61, 0.35); }
.journey__try .for-touch { display: none; }
@media (pointer: coarse) { .journey__try .for-touch { display: inline; } .journey__try .for-mouse { display: none; } }
.is-tryover { cursor: pointer; }
html[data-motion="still"] .journey__tryring, html[data-motion="still"] .journey__tryring::after { animation: none; }
html[data-motion="still"] .journey__tryring::after { opacity: 0; }
/* Where the timeline scrubs, until the reader has scrubbed it: the scan's finger, on a wide window too
   (the owner, 2026-10-11), pressing on the timeline, dragging from a quarter of the way along to three
   quarters and back, and lifting (hero.ts places it, --s0 and --sw). */
.journey__scrub, .journey__scrubsay { display: none; }
.is-scrubhint .journey__scrub { display: block; position: absolute; left: 0; top: 0; z-index: 2; width: 44px; height: 44px; margin: -22px 0 0 -22px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.2); border: 2px solid rgba(255, 255, 255, 0.85); box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.08); box-sizing: border-box;
  animation: journey-scrub 3s cubic-bezier(0.65, 0, 0.35, 1) infinite; }
@keyframes journey-scrub {
  0% { opacity: 0; transform: translate(var(--s0x), var(--s0y)) scale(1.3); }
  12% { opacity: 1; transform: translate(var(--s0x), var(--s0y)) scale(1); }
  20% { opacity: 1; transform: translate(var(--s0x), var(--s0y)) scale(0.86); }
  52% { opacity: 1; transform: translate(calc(var(--s0x) + var(--sw)), var(--s0y)) scale(0.86); }
  82% { opacity: 1; transform: translate(var(--s0x), var(--s0y)) scale(0.86); }
  94%, 100% { opacity: 0; transform: translate(var(--s0x), var(--s0y)) scale(1.15); }
}
/* The foot of the window, on a wide window: every hint (the scan's, and the scrub's for as long as the
   timeline scrubs) and the round trip's log, in one place, on one line each, from the grid's middle line
   (the owner, 2026-10-11: centred under the timeline they sat off the grid, and the instruction broke
   over two lines). The hints float a little and their instruction glows, rather than bobbing. A phone
   has the gestures alone: the words there clashed with the chapter's own. */
@media (min-width: 900px) {
  /* Centred on the window (the owner, 2026-10-14: they began on the grid's middle line and read as off
     centre under the round trip's centred words). */
  .journey__hint, .is-scrubsay .journey__scrubsay, .journey__log { position: absolute; left: 50%; translate: -50% 0; top: auto; bottom: calc(100% - min(100%, 100vh) + 44px); z-index: 2; margin: 0; }
  .journey__hint, .is-scrubsay .journey__scrubsay { display: block; width: max-content; white-space: nowrap; text-align: center;
    font: 500 calc(13.5 * var(--u))/1.5 var(--mono); letter-spacing: 0.08em; text-transform: uppercase;
    text-shadow: 0 1px 14px rgba(4, 5, 6, 0.95), 0 0 4px rgba(4, 5, 6, 0.9); }
  .journey__hint { transform: translateY(8px); }
  .is-scan .journey__hint { transform: none; }
  .is-scrubsay .journey__scrubsay { color: #A4A8AF; animation: journey-foot-in 500ms var(--ease) both, journey-float 4.8s ease-in-out 500ms infinite; }
  .is-scan .journey__hint { animation: journey-float 4.8s ease-in-out 360ms infinite; }
  .journey__scrubsay b, .journey__hint .hint-scan { font-weight: 500; color: var(--tally);
    text-shadow: 0 0 14px rgba(255, 77, 61, 0.5), 0 0 3px rgba(255, 77, 61, 0.35), 0 1px 14px rgba(4, 5, 6, 0.95); }
  .journey__hint .hint-scan span { text-shadow: 0 1px 14px rgba(4, 5, 6, 0.95), 0 0 4px rgba(4, 5, 6, 0.9); }
  .journey__log { width: calc(420 * var(--u)); height: calc(2 * var(--row-h)); }
  .journey__log li { right: 0; width: max-content; margin: 0 auto; }
}
/* The Color tab's own line, in the scrub hint's voice, in the words' column under the tab's line on a
   wide window, until the reader takes a slider (.is-lumsay, hero.ts). Over the pill bar it lay on the
   Mac's timeline and could not be read. A phone has the finger alone, as with the scrub. */
.journey__lumsay { display: none; }
@media (min-width: 900px) {
  .is-lumsay .journey__lumsay { display: block; position: absolute; left: calc((365 - 236) * var(--u)); top: calc(430 * var(--u)); width: calc(236 * var(--u)); z-index: 2; margin: 0;
    font: 500 calc(12.5 * var(--u))/calc(20 * var(--u)) var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: #8A93A0;
    animation: journey-foot-in 500ms var(--ease) both, journey-float 4.8s ease-in-out 500ms infinite; }
  .journey__lumsay b { font-weight: 500; color: var(--tally); text-shadow: 0 0 14px rgba(250, 76, 51, 0.5), 0 0 3px rgba(250, 76, 51, 0.35), 0 1px 14px rgba(4, 5, 6, 0.95); }
}
html[data-motion="still"] .journey__lumsay { animation: none; }
@keyframes journey-foot-in { from { opacity: 0; } }
/* (It floats on the translate the foot already uses to centre the hints, so the two add up rather than one
   undoing the other.) */
@keyframes journey-float { 0%, 100% { translate: -50% 0; } 50% { translate: -50% -3px; } }
[data-words="1"] .journey__scrubsay .for-ch3, [data-words="3"] .journey__scrubsay .for-ch1 { display: none; }
@media (max-width: 899px) { .journey__hint { display: none; } }
html[data-motion="still"] .journey__scrub, html[data-motion="still"] .journey__scrubsay, html[data-motion="still"] .journey__hint { animation: none; }
html[data-motion="still"] .journey__scrub { transform: translate(calc(var(--s0x) + var(--sw) / 2), var(--s0y)) scale(0.86); }

/* ------------------------------------------------------------------ see Tethery in action (.film) */
/* The launch film, in a frame the grid's full width from the first line to the last, 16:9, under its heading, after
   5b-demo-video-a.png without its line in mono (the owner, 2026-10-27): the play button in the middle with the film's length beside it, and its
   progress along the frame's foot in tally. Until it is pressed, a silent stretch of it loops over its first frame
   (film.js); while motion is paused that frame stays. Pressed, the film plays with sound and a glass bar of controls
   comes up while the pointer moves. */
.film { position: relative; }
.film__in { padding: calc(64 * var(--u)) 0 calc(96 * var(--u)); }
.film h2 { margin-left: calc(58 * var(--u)); font: 600 calc(51.3 * var(--u))/calc(53 * var(--u)) var(--sans); letter-spacing: -0.03em; color: #F1F1F1; }
.film__frame { position: relative; margin: calc(40 * var(--u)) 0 0 calc(58 * var(--u)); width: calc(1228 * var(--u)); aspect-ratio: 16 / 9;
  border: 1px solid rgba(255, 255, 255, 0.08); background: #000; }
.film__screen, .film__screen > * { position: absolute; inset: 0; width: 100%; height: 100%; }
.film__screen > img, .film__screen > video { display: block; object-fit: cover; }
.film__loop { opacity: 0; transition: opacity 600ms var(--ease); }
.film__frame.is-looping .film__loop { opacity: 1; }
.film__video { opacity: 0; }
.film__frame.is-film .film__video { opacity: 1; }
/* Dims the picture under the play button while the specks draw it, and while the film is paused. */
.film__screen::after { content: ""; position: absolute; inset: 0; background: #000; opacity: 0; transition: opacity 500ms var(--ease); }
.film__frame.is-specked .film__screen::after { opacity: 0.42; }
.film__frame[data-state="paused"] .film__screen::after { opacity: 0.3; }
/* The page's light is drawn behind the frame, and the frame blacks it out there (mesh.js, data-veil), so the specks
   a magnet holds can be drawn over the picture: the stream to the play button and the button they make (playMark).
   The picture is blended with screen, as the Mac in Every device is, so over that black it shows exactly as encoded
   and the specks add to it. Nothing between it and the light may isolate it (a z-index, an opacity, a transform).
   Only where the magnet can run: with WebGL, a mouse, and motion on; anywhere else the frame is plain black. */
@media (pointer: fine) {
  html[data-motion="full"].has-veils .film__frame { background: none; }
  html[data-motion="full"].has-veils .film__screen { mix-blend-mode: screen; }
}
/* Screen adds whatever is under the picture, the column grid's three middle lines too, so the grid has the frame
   cut out of it: 1228 wide and 16:9, centred, its foot 96 above the section's. The outer lines are the frame's edges. */
@media (pointer: fine) and (min-width: 900px) {
  html[data-motion="full"].has-veils #film > .lines { -webkit-mask: linear-gradient(#000, #000), linear-gradient(#000, #000); mask: linear-gradient(#000, #000), linear-gradient(#000, #000);
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%, calc(1228 * var(--u)) calc(690.75 * var(--u)); mask-size: 100% 100%, calc(1228 * var(--u)) calc(690.75 * var(--u));
    -webkit-mask-position: 0 0, 50% calc(100% - 96 * var(--u)); mask-position: 0 0, 50% calc(100% - 96 * var(--u));
    -webkit-mask-composite: xor; mask-composite: exclude; }
}
/* The play button covers the whole frame, so it can be pressed anywhere on it; its mark sits in the middle. */
.film__start { position: absolute; inset: 0; display: block; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; border-radius: 0; background: none; color: inherit; font: inherit; cursor: pointer; }
.film__play { position: absolute; left: 50%; top: 50%; display: grid; place-items: center; width: calc(84 * var(--u)); height: calc(84 * var(--u)); margin: calc(-42 * var(--u)) 0 0 calc(-42 * var(--u));
  border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.3); color: rgba(241, 242, 244, 0.94); }
/* Its glass, so the mark reads over the picture. */
.film__play::before { content: ""; position: absolute; inset: 0; border-radius: inherit; background: rgba(8, 9, 11, 0.42);
  -webkit-backdrop-filter: blur(12px) saturate(140%); backdrop-filter: blur(12px) saturate(140%); }
.film__play svg { position: relative; }
.film__play svg { width: calc(30 * var(--u)); height: calc(30 * var(--u)); margin-left: calc(4 * var(--u)); fill: currentColor; }
.film__start:hover .film__play { border-color: rgba(255, 255, 255, 0.55); }
/* The length beside it, as 5b has it. */
.film__len { position: absolute; left: calc(50% + 58 * var(--u)); top: 50%; transform: translateY(-50%); font: 400 calc(16 * var(--u))/1 var(--mono); letter-spacing: 0.02em; color: rgba(241, 242, 244, 0.88);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.7); }
/* While the page's specks draw the play button (mesh.js, playMark), the painted ring, its glass and the triangle fade
   out after the first of them arrive, and back in as they leave. */
.film__play, .film__len { transition: border-color 500ms var(--ease), opacity 500ms var(--ease); }
.film__play::before, .film__play svg { transition: opacity 500ms var(--ease); }
.film__frame.is-specked .film__play { border-color: transparent; transition-delay: 300ms; }
.film__frame.is-specked .film__play::before, .film__frame.is-specked .film__play svg { opacity: 0; transition-delay: 300ms; }
/* Once the film has started, the bar tells the time. */
.film__frame:not([data-state="preview"]) .film__len { opacity: 0; }
/* Pressed, the button waits for the first frame with a turning arc in its ring. */
.film__play::after { content: ""; position: absolute; inset: -1px; border-radius: inherit; border: 1.5px solid transparent; border-top-color: var(--tally); opacity: 0; }
.film__frame[data-state="loading"] .film__play::after { opacity: 1; animation: film-turn 900ms linear infinite; }
html[data-motion="still"] .film__frame[data-state="loading"] .film__play::after { animation: none; border-color: var(--tally); }
@keyframes film-turn { to { transform: rotate(1turn); } }
.film__frame[data-state="loading"] .film__start { cursor: progress; }
.film__frame[data-state="playing"] .film__start { display: none; }

/* The film's progress, a tally line along the frame's foot. */
.film__line { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: rgba(255, 255, 255, 0.12); pointer-events: none; }
.film__line i { display: block; width: calc(var(--p, 0) * 100%); height: 100%; background: var(--tally); }
.film__frame[data-state="preview"] .film__line i { width: 0; }

/* The controls: a glass bar over the picture's foot, up while the film is paused and while the pointer moves or a
   control has the keyboard's focus as it plays. */
.film__bar { position: absolute; left: calc(14 * var(--u)); right: calc(14 * var(--u)); bottom: calc(16 * var(--u)); display: flex; align-items: center; gap: calc(6 * var(--u));
  height: calc(46 * var(--u)); padding: 0 calc(10 * var(--u)); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: calc(10 * var(--u));
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02)), rgba(10, 11, 13, 0.58); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
  -webkit-backdrop-filter: blur(14px) saturate(160%); backdrop-filter: blur(14px) saturate(160%);
  opacity: 0; visibility: hidden; transform: translateY(calc(6 * var(--u))); transition: opacity 260ms var(--ease), transform 360ms var(--ease-out), visibility 0s 360ms; }
.film__frame[data-state="paused"] .film__bar, .film__frame[data-state="playing"].is-active .film__bar, .film__frame[data-state="playing"] .film__bar:has(:focus-visible),
.film__frame.is-menu .film__bar {
  opacity: 1; visibility: visible; transform: none; transition: opacity 200ms var(--ease), transform 360ms var(--ease-out), visibility 0s; }
.film__frame[data-state="playing"]:not(.is-active):not(.is-menu) { cursor: none; }
html[data-motion="still"] .film__bar { transform: none; }
.film__btn { display: grid; place-items: center; flex: none; width: calc(34 * var(--u)); height: calc(34 * var(--u)); padding: 0; border: 0; border-radius: calc(8 * var(--u)); background: none; color: var(--ink); cursor: pointer;
  transition: background-color 200ms var(--ease), color 200ms var(--ease); }
.film__btn:hover { background: rgba(255, 255, 255, 0.09); }
.film__btn svg { width: calc(16 * var(--u)); height: calc(16 * var(--u)); fill: currentColor; }
.film__frame[data-state="playing"] .i-play, .film__frame:not([data-state="playing"]) .i-pause, .film__btn[aria-pressed="true"] .i-on, .film__btn[aria-pressed="false"] .i-off { display: none; }
.film__cc { width: auto; padding: 0 calc(8 * var(--u)); font: 500 calc(11.5 * var(--u))/1 var(--mono); letter-spacing: 0.08em; }
.film__cc[aria-pressed="true"] { color: var(--tally); }
/* Quality: the rung chosen, or Auto, in the same mono as CC; it shows once the film knows its rungs. */
.film__q { width: auto; min-width: calc(46 * var(--u)); padding: 0 calc(8 * var(--u)); font: 500 calc(11.5 * var(--u))/1 var(--mono); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; }
.film__q[hidden] { display: none; }
.film__q[aria-expanded="true"] { background: rgba(255, 255, 255, 0.09); }
/* Its menu, the bar's glass above the button (film.js places it): Auto, then each rung, tallest first, the chosen
   one marked with the tally dot. Auto names the rung it is playing. */
.film__menu { position: absolute; z-index: 1; display: flex; flex-direction: column; min-width: calc(148 * var(--u)); padding: calc(5 * var(--u));
  border: 1px solid rgba(255, 255, 255, 0.12); border-radius: calc(10 * var(--u));
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02)), rgba(10, 11, 13, 0.72); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 8px 24px rgba(0, 0, 0, 0.35);
  -webkit-backdrop-filter: blur(14px) saturate(160%); backdrop-filter: blur(14px) saturate(160%);
  animation: film-menu 180ms var(--ease-out); }
.film__menu[hidden] { display: none; }
@keyframes film-menu { from { opacity: 0; transform: translateY(calc(4 * var(--u))); } }
html[data-motion="still"] .film__menu { animation: none; }
.film__menu button { position: relative; display: flex; align-items: center; gap: calc(10 * var(--u)); height: calc(32 * var(--u)); margin: 0; padding: 0 calc(12 * var(--u)) 0 calc(28 * var(--u));
  border: 0; border-radius: calc(6 * var(--u)); background: none; color: var(--ink); font: 500 calc(12 * var(--u))/1 var(--mono); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; text-align: left; cursor: pointer;
  transition: background-color 160ms var(--ease); }
.film__menu button:hover, .film__menu button:focus-visible { background: rgba(255, 255, 255, 0.09); }
.film__menu button:focus-visible { outline-offset: -2px; }
.film__menu button[aria-checked="true"]::before { content: ""; position: absolute; left: calc(12 * var(--u)); top: 50%; width: calc(6 * var(--u)); height: calc(6 * var(--u)); margin-top: calc(-3 * var(--u)); border-radius: 50%; background: var(--tally); }
.film__menu span { margin-left: auto; color: var(--ink-2); }
.film__menu span:empty { display: none; }
.film__time { flex: none; margin: 0 calc(8 * var(--u)) 0 calc(4 * var(--u)); font: 400 calc(12.5 * var(--u))/1 var(--mono); letter-spacing: 0.02em; font-variant-numeric: tabular-nums; color: var(--ink-2); }
/* The scrubber: the same tally line, with a thumb, filled to where the film is (--p, film.js). */
.film__seek { flex: 1; min-width: 0; height: calc(28 * var(--u)); margin: 0 calc(8 * var(--u)); background: none; cursor: pointer; -webkit-appearance: none; appearance: none; }
.film__seek::-webkit-slider-runnable-track { height: 3px; border-radius: 2px; background: linear-gradient(90deg, var(--tally) calc(var(--p, 0) * 100%), rgba(255, 255, 255, 0.2) 0); }
.film__seek::-moz-range-track { height: 3px; border-radius: 2px; background: linear-gradient(90deg, var(--tally) calc(var(--p, 0) * 100%), rgba(255, 255, 255, 0.2) 0); }
.film__seek::-webkit-slider-thumb { -webkit-appearance: none; width: calc(12 * var(--u)); height: calc(12 * var(--u)); margin-top: calc(1.5px - 6 * var(--u)); border: 0; border-radius: 50%; background: var(--ink); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); }
.film__seek::-moz-range-thumb { width: calc(12 * var(--u)); height: calc(12 * var(--u)); border: 0; border-radius: 50%; background: var(--ink); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); }
.film__seek:focus-visible { outline-offset: 1px; }

/* The voiceover's captions, when CC is on: drawn by film.js from the track, above the bar. */
.film__cap { position: absolute; left: 50%; bottom: calc(84 * var(--u)); transform: translateX(-50%); width: max-content; max-width: 78%; padding: calc(6 * var(--u)) calc(14 * var(--u)); border-radius: calc(6 * var(--u));
  background: rgba(8, 9, 11, 0.66); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  font: 500 calc(21 * var(--u))/1.3 var(--sans); letter-spacing: -0.005em; color: #fff; text-align: center; pointer-events: none; }
.film__cap:empty { display: none; }

/* Full screen is the frame, so the controls and the captions come with it. */
.film__frame:fullscreen { width: 100%; height: 100%; margin: 0; border: 0; background: #000; aspect-ratio: auto; }
.film__frame:fullscreen .film__screen { mix-blend-mode: normal; }
.film__frame:fullscreen .film__screen > * { object-fit: contain; }
.film__frame:-webkit-full-screen { width: 100%; height: 100%; margin: 0; border: 0; background: #000; aspect-ratio: auto; }
.film__frame:-webkit-full-screen .film__screen { mix-blend-mode: normal; }
.film__frame:-webkit-full-screen .film__screen > * { object-fit: contain; }

/* ------------------------------------------------------------------ what it does, row by row (.feat) */
/* Four rows after the guided sequence, the words on one side and the feature playing on the other,
   alternating (the owner, 2026-10-14, after diffusion.studio): the picture from the first line, two and a
   half columns wide, the words from the fourth; flipped, the words from the first and the picture ending
   on the last. The pictures stand on the page's own light, with no box round them (the owner: not in
   containers). Each is laid out in a 768 x 472 frame, and everything in it is in --k, one of its pixels,
   so it scales as one picture. features.js plays it while it is on screen. */
.feat { position: relative; }
.feat__in { height: calc(640 * var(--u)); }
/* The Sound row's picture is a single phone on its side, shorter than the other rows' Macs, so its row is
   shorter too: at 640 the gap under it to Every device read wider than the others (the owner, 2026-10-17). */
@media (min-width: 900px) { #controls .feat__in { height: calc(540 * var(--u)); } }
.feat__stage { --k: var(--u); position: absolute; left: calc(58 * var(--u)); top: calc(84 * var(--u)); width: calc(768 * var(--k)); height: calc(472 * var(--k)); }
.feat--flip .feat__stage { left: calc((1286 - 768) * var(--u)); }
.feat--flip .win { left: auto; right: calc(68 * var(--k)); }
.feat__words { position: absolute; left: calc(979 * var(--u)); top: calc(118 * var(--u)); width: calc(330 * var(--u)); }
.feat--flip .feat__words { left: calc(58 * var(--u)); }
.feat__eye { display: flex; align-items: center; gap: calc(10 * var(--u)); font: 500 calc(12.5 * var(--u))/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--tally); }
.feat__eye::before { content: ""; width: calc(7 * var(--u)); height: calc(7 * var(--u)); border-radius: 50%; background: currentColor; box-shadow: 0 0 10px rgba(250, 76, 51, 0.6); }
.feat h2 { margin-top: calc(18 * var(--u)); font: 600 calc(38 * var(--u))/calc(42 * var(--u)) var(--sans); letter-spacing: -0.03em; color: #F3F3F3; }
.feat__words > h2 + p { margin-top: calc(22 * var(--u)); font: 400 calc(16.5 * var(--u))/calc(26 * var(--u)) var(--sans); color: #A4A8AF; }
/* A row's way into the blog post about it, under its words (the color row's: the color post). */
.feat__more { display: inline-block; margin-top: calc(16 * var(--u)); font: 450 calc(15 * var(--u))/1.4 var(--sans); color: #E6E6E6; white-space: nowrap;
  text-decoration: underline; text-decoration-color: rgba(255, 255, 255, 0.3); text-underline-offset: 4px; transition: text-decoration-color 160ms var(--ease); }
.feat__more:hover { text-decoration-color: var(--tally); }
/* A footnote under a row's words: the Sound row's, that After Effects streams no sound. */
.feat__mark { color: var(--tally); text-decoration: none; margin-left: 0.1em; }
/* Set apart from the words above: a short rule, a wider gap, smaller and dimmer type. */
.feat__note { margin-top: calc(34 * var(--u)); padding-top: calc(14 * var(--u)); max-width: calc(300 * var(--u)); border-top: 1px solid rgba(255, 255, 255, 0.1);
  font: 400 calc(11 * var(--u))/calc(17 * var(--u)) var(--mono); letter-spacing: 0.01em; color: #5C6168; }

/* Premiere, drawn plainly: its greys, its blue for values and the playhead. */
.win { position: absolute; left: 0; top: calc(30 * var(--k)); width: calc(700 * var(--k)); height: calc(390 * var(--k)); overflow: hidden;
  background: #1B1B1B; border: 1px solid #2E2E2E; border-radius: calc(10 * var(--k)); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
  font-family: var(--sans); color: #B4B4B4; }
.pm { position: absolute; left: calc(254 * var(--k)); right: 0; top: 0; bottom: 0; background: #1B1B1B; }
.pm__head { height: calc(34 * var(--k)); padding-left: calc(14 * var(--k)); font: 500 calc(12.5 * var(--k))/calc(34 * var(--k)) var(--sans); color: #D2D2D2; border-bottom: 1px solid #111; }
.pm__pic { position: absolute; left: calc(14 * var(--k)); top: calc(54 * var(--k)); width: calc(418 * var(--k)); height: calc(235 * var(--k)); overflow: hidden; background: #000; }
.pm__pic img, .dev__glass img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.pm__tc { position: absolute; left: calc(14 * var(--k)); top: calc(304 * var(--k)); font: 400 calc(14 * var(--k))/1 var(--mono); color: #2D8CEB; letter-spacing: 0.02em; }

/* Lumetri Color's Basic Correction, larger than Premiere's so it reads at this size. */
.lum { position: absolute; left: 0; top: 0; bottom: 0; width: calc(252 * var(--k)); padding: 0 calc(16 * var(--k)); background: #232323; border-right: 2px solid #141414; }
.lum__tab { display: flex; gap: calc(18 * var(--k)); height: calc(34 * var(--k)); margin: 0 calc(-16 * var(--k)); padding: 0 calc(16 * var(--k)); border-bottom: 1px solid #141414; font: 500 calc(12.5 * var(--k))/calc(34 * var(--k)) var(--sans); color: #8A8A8A; }
.lum__tab span:first-child { color: #EDEDED; box-shadow: inset 0 -2px 0 #2D8CEB; }
.lum__sec { margin-top: calc(14 * var(--k)); font: 600 calc(13 * var(--k))/1 var(--sans); color: #DCDCDC; }
.lum__group { margin-top: calc(16 * var(--k)); font: 500 calc(11.5 * var(--k))/1 var(--sans); color: #8A8A8A; letter-spacing: 0.02em; }
.lum__row { display: grid; grid-template-columns: calc(84 * var(--k)) 1fr calc(48 * var(--k)); align-items: center; height: calc(30 * var(--k)); font: 400 calc(12.5 * var(--k))/1 var(--sans); }
.lum__row i { position: relative; height: calc(3 * var(--k)); border-radius: 2px; background: #3C3C3C; }
.lum__row[data-k="temp"] i { background: linear-gradient(90deg, #3F7BD6, #9C9C9C, #E0B23A); }
.lum__row[data-k="tint"] i { background: linear-gradient(90deg, #3FAF5A, #9C9C9C, #C24FBF); }
.lum__row[data-k="sat"] i { background: linear-gradient(90deg, #8A8A8A, #D9534F 40%, #E0B23A 60%, #3FAF5A 80%, #3F7BD6); }
.lum__row b { position: absolute; top: 50%; left: calc(var(--f, 0.5) * 100%); width: calc(11 * var(--k)); height: calc(11 * var(--k)); margin: calc(-5.5 * var(--k)) 0 0 calc(-5.5 * var(--k)); border-radius: 50%;
  background: #DADADA; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6); }
.lum__row.is-held b { background: #fff; box-shadow: 0 0 0 calc(3 * var(--k)) rgba(45, 140, 235, 0.45); }
.lum__row em { font: 400 calc(12 * var(--k))/1 var(--mono); font-style: normal; color: #2D8CEB; text-align: right; font-variant-numeric: tabular-nums; }

/* The devices: a phone, its glass cut to its rounded shape, and the viewer's LIVE chip. */
.dev { position: absolute; padding: calc(5 * var(--k)); border-radius: calc(24 * var(--k)); background: #0B0B0D;
  box-shadow: inset 0 0 0 calc(1.5 * var(--k)) #3A3A41, 0 24px 60px rgba(0, 0, 0, 0.65); }
.dev__glass { position: relative; width: 100%; height: 100%; overflow: hidden; border-radius: calc(19 * var(--k)); background: #000; }
.dev--side .dev__glass img, .dev--tablet .dev__glass img { object-fit: contain; }
.dev__live { position: absolute; left: calc(14 * var(--k)); top: calc(10 * var(--k)); padding: calc(2 * var(--k)) calc(6 * var(--k)); border-radius: 2px; background: var(--tally); color: #fff; font: 500 calc(8.5 * var(--k))/calc(11 * var(--k)) var(--mono); letter-spacing: 0.08em; text-transform: uppercase; }
.feat__phone { right: 0; bottom: calc(20 * var(--k)); }
.dev--side.feat__phone { width: calc(286 * var(--k)); height: calc(140 * var(--k)); }
.feat__pointer { position: absolute; left: 0; top: 0; width: calc(17 * var(--k)); height: calc(25 * var(--k)); opacity: 0; pointer-events: none; z-index: 3;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 17 25'%3E%3Cpath d='M1 1v19.5l4.6-4.4 3 7 3.2-1.4-3-6.9H15z' fill='%23000' stroke='%23fff' stroke-width='1.3' stroke-linejoin='round'/%3E%3C/svg%3E") 0 0 / 100% 100% no-repeat;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5)); }

/* Sequences: the Program Monitor takes the sequence's shape, and the phone turns with it. */
.pm--seq { left: 0; height: calc(262 * var(--k)); bottom: auto; }
.pm--seq .pm__pic { left: 50%; top: calc(26 * var(--k)); width: calc(372 * var(--k)); height: calc(210 * var(--k)); transform: translateX(-50%); transition: width 650ms var(--ease); }
.is-tall .pm--seq .pm__pic { width: calc(118 * var(--k)); }
.seq__tall { opacity: 0; transition: opacity 250ms ease-out; }
.is-tall .seq__tall { opacity: 1; }
.tl { position: absolute; left: 0; right: 0; top: calc(262 * var(--k)); bottom: 0; background: #232323; border-top: 2px solid #141414; }
.tl__tabs { display: flex; height: calc(32 * var(--k)); padding-left: calc(14 * var(--k)); border-bottom: 1px solid #141414; font: 500 calc(12 * var(--k))/calc(32 * var(--k)) var(--sans); }
.tl__tabs span { padding: 0 calc(14 * var(--k)); color: #8A8A8A; }
.tl__tabs span.is-on { color: #EDEDED; box-shadow: inset 0 -2px 0 #2D8CEB; }
.tl__ruler { position: relative; height: calc(20 * var(--k)); margin-left: calc(96 * var(--k)); border-bottom: 1px solid #141414;
  background: repeating-linear-gradient(90deg, #4A4A4A 0 1px, transparent 1px calc(24 * var(--k))) 0 100% / 100% 40% no-repeat; }
.tl__head { position: absolute; left: calc(var(--p, 0.3) * 100%); top: calc(4 * var(--k)); width: calc(10 * var(--k)); height: calc(14 * var(--k)); margin-left: calc(-5 * var(--k)); background: #2D8CEB;
  clip-path: polygon(0 0, 100% 0, 100% 60%, 50% 100%, 0 60%); }
.tl__head::after { content: ""; position: absolute; left: calc(4.5 * var(--k)); top: 100%; width: 1px; height: calc(130 * var(--k)); background: #2D8CEB; }
.tl__track { position: relative; height: calc(32 * var(--k)); margin-left: calc(96 * var(--k)); border-bottom: 1px solid #1A1A1A; }
.tl__track::before { content: "V"; position: absolute; left: calc(-82 * var(--k)); top: calc(8 * var(--k)); font: 500 calc(11 * var(--k))/1 var(--mono); color: #7A7A7A; }
.tl__track--a::before { content: "A"; }
.tl__track i { position: absolute; top: calc(3 * var(--k)); bottom: calc(3 * var(--k)); border-radius: 2px; background: #8E85D8; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
.tl__track--a i { background: #4C9C69; }
.seq__phone { width: calc(124 * var(--k)); height: calc(256 * var(--k)); right: calc(66 * var(--k)); bottom: calc(10 * var(--k)); transform: rotate(-90deg); transition: transform 650ms var(--ease); }
.seq__phone .scr { position: absolute; left: 50%; top: 50%; width: calc(246 * var(--k)); height: calc(114 * var(--k)); transform: translate(-50%, -50%) rotate(90deg);
  transition: width 650ms var(--ease), height 650ms var(--ease), transform 650ms var(--ease); }
.seq__phone .scr img:first-child { object-fit: contain; }
.is-tall .seq__phone { transform: rotate(0deg); }
.is-tall .seq__phone .scr { width: calc(114 * var(--k)); height: calc(246 * var(--k)); transform: translate(-50%, -50%) rotate(0deg); }

/* Sound: the phone with the viewer's panel open beside the picture, its level moving while the sound plays.
   Its top is the other rows' window's (.win, 30), so it lines up with the words beside it. */
.dev--big { left: calc(84 * var(--k)); top: calc(30 * var(--k)); width: calc(600 * var(--k)); height: calc(288 * var(--k)); border-radius: calc(40 * var(--k)); padding: calc(8 * var(--k)); }
.dev--big .dev__glass { border-radius: calc(32 * var(--k)); }
.dev--big .dev__live { left: calc(40 * var(--k)); top: calc(16 * var(--k)); }
.hud { position: absolute; top: calc(12 * var(--k)); right: calc(36 * var(--k)); bottom: calc(12 * var(--k)); width: calc(206 * var(--k)); padding: calc(12 * var(--k)) calc(14 * var(--k));
  border: 1px solid rgba(255, 255, 255, 0.12); border-radius: calc(14 * var(--k)); background: rgba(14, 15, 18, 0.72); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  opacity: 0; transform: translateX(calc(16 * var(--k))); transition: opacity 300ms var(--ease), transform 300ms var(--ease); }
.is-hud .hud { opacity: 1; transform: none; }
.hud__head { display: flex; align-items: center; gap: calc(6 * var(--k)); }
.hud__id { flex: 1; min-width: 0; }
.hud__id span { display: block; font: 500 calc(8 * var(--k))/1 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: #8A93A0; }
.hud__id b { display: block; margin-top: calc(4 * var(--k)); font: 600 calc(12.5 * var(--k))/1 var(--sans); color: #F1F2F4; }
.hud__btn { display: grid; place-items: center; width: calc(24 * var(--k)); height: calc(24 * var(--k)); border-radius: 50%; background: rgba(255, 255, 255, 0.08); color: #D8DCE2; }
.hud__btn svg { width: calc(12 * var(--k)); height: calc(12 * var(--k)); }
.hud__sound .snd-on, .is-sound .hud__sound .snd-off { display: none; }
.is-sound .hud__sound .snd-on { display: block; }
.is-sound .hud__sound { background: var(--tally); color: #fff; }
.hud__meter { display: flex; flex-direction: column; gap: calc(2 * var(--k)); margin-top: calc(10 * var(--k)); }
.hud__meter i { height: calc(3 * var(--k)); border-radius: 2px; background: linear-gradient(90deg, #2EBE63 0 70%, #E0B23A 70% 88%, #E5484D 88%) 0 0 / calc(178 * var(--k)) 100% no-repeat, rgba(255, 255, 255, 0.07);
  clip-path: inset(0 calc((1 - var(--l, 0)) * 100%) 0 0 round 2px); }
.hud__tele { display: grid; gap: calc(6 * var(--k)); margin-top: calc(12 * var(--k)); }
.hud__tele div { display: flex; justify-content: space-between; font: 400 calc(9.5 * var(--k))/1 var(--mono); }
.hud__tele dt { color: #8A93A0; letter-spacing: 0.06em; text-transform: uppercase; }
.hud__tele dd { margin: 0; color: #EDEFF2; font-variant-numeric: tabular-nums; }
.hud__dot { display: inline-block; width: calc(5 * var(--k)); height: calc(5 * var(--k)); margin-right: calc(5 * var(--k)); border-radius: 50%; background: #2EBE63; vertical-align: middle; }
.feat__tap { position: absolute; left: 0; top: 0; width: calc(40 * var(--k)); height: calc(40 * var(--k)); margin: calc(-20 * var(--k)) 0 0 calc(-20 * var(--k)); border-radius: 50%;
  background: rgba(255, 255, 255, 0.35); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.5); opacity: 0; pointer-events: none; }
.feat__tap.is-on { animation: feat-tap 520ms var(--ease-out); }
@keyframes feat-tap { 0% { opacity: 0.9; transform: scale(0.4); } 100% { opacity: 0; transform: scale(1.3); } }

/* Every device, after round4/ch3-d-every-device.png (the owner's choice): the Mac with the session open,
   an iPhone lying in front of it, an iPad on its stand, an Android phone standing, and a tether from the
   Program Monitor's sun to each, the hero's beam. Each screen carries the timecode it is showing, so the
   playhead dragged on the Mac is seen to move all of them. */
.mac { position: absolute; left: 0; top: calc(12 * var(--k)); width: calc(362 * var(--k)); }
.mac__lid { height: calc(268 * var(--k)); padding: calc(8 * var(--k)); border-radius: calc(12 * var(--k)) calc(12 * var(--k)) calc(4 * var(--k)) calc(4 * var(--k)); background: #0D0D10;
  box-shadow: inset 0 0 0 calc(1.5 * var(--k)) #3A3A41, 0 30px 70px rgba(0, 0, 0, 0.6); }
.mac__ui { position: relative; width: 100%; height: 100%; overflow: hidden; border-radius: calc(3 * var(--k)); background: #1B1B1B; font-family: var(--sans); }
.mac__base { height: calc(12 * var(--k)); margin: 0 calc(-22 * var(--k)); border-radius: 0 0 calc(10 * var(--k)) calc(10 * var(--k)); background: linear-gradient(#5A5A62, #2A2A30 40%, #17171B); }
.mac__panel { position: absolute; left: 0; top: 0; width: calc(94 * var(--k)); height: calc(150 * var(--k)); padding: calc(6 * var(--k)); background: #232323; border-right: 1px solid #141414; text-align: center; }
.mac__tab { font: 600 calc(7 * var(--k))/1 var(--sans); color: #EDEDED; text-align: left; }
.mac__pill { display: inline-flex; align-items: center; gap: calc(3 * var(--k)); margin-top: calc(10 * var(--k)); padding: calc(2 * var(--k)) calc(4 * var(--k)); border-radius: 999px; background: rgba(250, 76, 51, 0.16);
  color: var(--tally); font: 500 calc(5 * var(--k))/1 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; }
.mac__pill i { width: calc(3.5 * var(--k)); height: calc(3.5 * var(--k)); border-radius: 50%; background: currentColor; }
.mac__code { display: block; margin-top: calc(10 * var(--k)); font: 500 calc(11 * var(--k))/1 var(--mono); letter-spacing: 0.14em; color: #F1F1F1; }
.mac__panel img { width: calc(52 * var(--k)); height: calc(52 * var(--k)); margin: calc(8 * var(--k)) auto 0; padding: calc(3 * var(--k)); border-radius: calc(3 * var(--k)); background: #F4F5F7; }
.mac__pm { position: absolute; left: calc(100 * var(--k)); top: calc(6 * var(--k)); width: calc(240 * var(--k)); height: calc(135 * var(--k)); overflow: hidden; background: #000; }
.mac__pm img { width: 100%; height: 100%; object-fit: cover; }
.mac__tl { position: absolute; left: 0; right: 0; top: calc(150 * var(--k)); bottom: 0; padding: calc(8 * var(--k)) calc(8 * var(--k)) 0 calc(24 * var(--k)); background: #232323; border-top: 2px solid #141414; }
.stage--devices .strip__clips { position: relative; display: flex; gap: 1px; height: calc(26 * var(--k)); }
.strip__clips i { flex: 1; border-radius: 1px; background: center / cover no-repeat; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18); opacity: 0.85; }
/* A background is fetched wherever it is on the page, so the clips wait until features.js says the stage is a
   screen away (.is-near). Without scripts (no html[data-motion]) they come with the page. */
:is(html:not([data-motion]) .strip__clips, .strip__clips.is-near) i:nth-child(1) { background-image: url("/img/reel-wide.webp?v=74f05b9f3c43389b"); }
:is(html:not([data-motion]) .strip__clips, .strip__clips.is-near) i:nth-child(2) { background-image: url("/img/reel-pool.webp?v=b210a96db6815fcb"); }
:is(html:not([data-motion]) .strip__clips, .strip__clips.is-near) i:nth-child(3) { background-image: url("/img/reel-corridor.webp?v=5d3242dc133afca9"); }
:is(html:not([data-motion]) .strip__clips, .strip__clips.is-near) i:nth-child(4) { background-image: url("/img/reel-couple.webp?v=f8e9b4bd2634d6a3"); }
:is(html:not([data-motion]) .strip__clips, .strip__clips.is-near) i:nth-child(5) { background-image: url("/img/reel-fireworks.webp?v=0c2419c0e3339a6b"); }
.mac__track { position: relative; height: calc(16 * var(--k)); margin-top: calc(4 * var(--k)); }
.mac__track i { position: absolute; top: 0; bottom: 0; border-radius: 1px; background: #8E85D8; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
.mac__track--a i { background: #4C9C69; }
.strip__head-line { position: absolute; z-index: 1; top: calc(-6 * var(--k)); height: calc(84 * var(--k)); left: calc(var(--p, 0.3) * 100%); width: 1.5px; margin-left: -0.75px; background: #2D8CEB; box-shadow: 0 0 5px rgba(45, 140, 235, 0.7); }
.strip__head-line::before { content: ""; position: absolute; left: 50%; top: 0; width: calc(8 * var(--k)); height: calc(7 * var(--k)); transform: translateX(-50%); background: #2D8CEB; clip-path: polygon(0 0, 100% 0, 100% 55%, 50% 100%, 0 55%); }
.dev--iphone { left: calc(120 * var(--k)); top: calc(318 * var(--k)); width: calc(214 * var(--k)); height: calc(104 * var(--k)); }
.dev--tablet { left: calc(374 * var(--k)); top: calc(214 * var(--k)); width: calc(250 * var(--k)); height: calc(180 * var(--k)); border-radius: calc(18 * var(--k)); padding: calc(9 * var(--k)); }
.dev--tablet .dev__glass { border-radius: calc(9 * var(--k)); }
.dev--android { left: calc(664 * var(--k)); top: calc(178 * var(--k)); width: calc(92 * var(--k)); height: calc(186 * var(--k)); border-radius: calc(14 * var(--k)); padding: calc(4 * var(--k)); }
.dev--android .dev__glass { border-radius: calc(10 * var(--k)); }
.dev--android .dev__glass::after { content: ""; position: absolute; left: 50%; top: calc(6 * var(--k)); width: calc(6 * var(--k)); height: calc(6 * var(--k)); margin-left: calc(-3 * var(--k)); border-radius: 50%; background: #0B0B0D; }
.dev--android .dev__glass img { object-position: 72% 50%; }
/* The table they stand on shines a little under each. */
.stage--devices .dev, .mac { -webkit-box-reflect: below calc(3 * var(--k)) linear-gradient(transparent 72%, rgba(255, 255, 255, 0.14)); }
.dev__tc { position: absolute; left: calc(8 * var(--k)); top: calc(7 * var(--k)); padding: calc(2.5 * var(--k)) calc(5 * var(--k)); border-radius: calc(3 * var(--k)); background: rgba(10, 11, 13, 0.72);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16); color: #F1F2F4; font: 500 calc(8.5 * var(--k))/1 var(--mono); letter-spacing: 0.02em; font-variant-numeric: tabular-nums; }
.mac__pm .dev__tc { left: calc(6 * var(--k)); top: calc(5 * var(--k)); font-size: calc(7 * var(--k)); }
.dev--android .dev__tc { left: 50%; top: calc(16 * var(--k)); transform: translateX(-50%); }
/* The tethers: lit as the connections' chosen road is (.fan__road.is-on), and filled with specks by mesh.js. */
.tethers { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.tethers path { fill: none; stroke: var(--tally); stroke-width: 2.4; stroke-linecap: round; filter: url(#tetherGlow); }
/* The tethers leave from the Program Monitor, so their specks have to be drawn over the Mac's screen, as
   the film's are over its frame: the light paints the lid's outline black (data-veil, mesh.js) and draws
   the held specks over that, and the Mac is blended with screen, so it shows exactly as drawn and the
   specks add to it. Lighten, as the frame has, lost them over anything brighter, the timeline's tracks
   above all. On .mac, not the lid: its reflection makes it a group of its own, and a blend inside it
   would have nothing to blend with. */
.has-veils .stage--devices .mac { mix-blend-mode: screen; }
/* Screen adds whatever is under the Mac, the column grid's line through it too, so the row's grid has the
   Mac's outline, lid and base, cut out of it. The hole is 406 by 280 (the lid, and the base's overhang of
   22 each side) and its left edge is 176 left of the window's middle (the frame's 672, less the stage's
   518 in .feat--flip, plus the overhang). A percentage in a mask's position is of the room left over, so
   that edge is 50% (the middle less 203) and 27 more. */
@media (min-width: 900px) {
  .has-veils #devices > .lines { -webkit-mask: linear-gradient(#000, #000), linear-gradient(#000, #000); mask: linear-gradient(#000, #000), linear-gradient(#000, #000);
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%, calc(406 * var(--u)) calc(280 * var(--u)); mask-size: 100% 100%, calc(406 * var(--u)) calc(280 * var(--u));
    -webkit-mask-position: 0 0, calc(50% + 27 * var(--u)) calc(96 * var(--u)); mask-position: 0 0, calc(50% + 27 * var(--u)) calc(96 * var(--u));
    -webkit-mask-composite: xor; mask-composite: exclude; }
}
/* Each device named under it, centred on it: a short tick from the device to a plain tag. */
.dev__label { position: absolute; transform: translateX(-50%); padding: calc(4 * var(--k)) calc(9 * var(--k)); border: 1px solid rgba(237, 239, 242, 0.18); border-radius: 999px; background: rgba(10, 11, 13, 0.7);
  font: 500 calc(11 * var(--k))/1 var(--mono); letter-spacing: 0.04em; color: #E6E9ED; white-space: nowrap; }
.dev__label::before { content: ""; position: absolute; left: 50%; bottom: 100%; width: 1px; height: calc(10 * var(--k)); background: rgba(237, 239, 242, 0.3); }
.dev__label--iphone { left: calc(227 * var(--k)); top: calc(434 * var(--k)); }
.dev__label--ipad { left: calc(499 * var(--k)); top: calc(406 * var(--k)); }
.dev__label--android { left: calc(710 * var(--k)); top: calc(376 * var(--k)); }
/* The specks the rows draw (features.js): the tethers, and the sound spreading behind the phone. The canvas
   runs past the stage on every side, so the waves are not cut off at its edge. */
.feat__specks { position: absolute; left: calc(-80 * var(--k)); top: calc(-80 * var(--k)); width: calc(928 * var(--k)); height: calc(632 * var(--k)); pointer-events: none; }
/* That canvas is wider than a phone's screen, which let the whole page be swiped sideways: the row clips
   it at the window's edges, and only across, so the waves still run over the rows above and below. */
.feat { overflow-x: clip; }

html[data-motion="still"] .feat * { transition: none !important; animation: none !important; }

/* ------------------------------------------------------------------ what it does (6-feature-ledger) */
.features { position: relative; min-height: calc(752 * var(--u)); }
.features__in { height: calc(752 * var(--u)); }
.features h2 { position: absolute; left: calc(58 * var(--u)); top: calc(20 * var(--u)); font: 700 calc(58 * var(--u))/calc(70 * var(--u)) var(--sans); letter-spacing: -0.03em; color: #EDEDED; }
/* The ledger's rules are the grid's own lines: it spans the frame, the picture takes two columns
   and the other cells one each. */
.ledger { position: absolute; left: calc(58 * var(--u)); top: calc(105 * var(--u)); width: calc(1229 * var(--u)); height: calc(447 * var(--u));
  display: grid; grid-template-columns: calc(614 * var(--u)) calc(307 * var(--u)) 1fr; grid-template-rows: calc(169 * var(--u)) 1fr;
  border: 1px solid var(--rule); }
.cell { position: relative; overflow: hidden; padding: calc(17 * var(--u)) calc(25 * var(--u)) 0; }
.cell + .cell { border-left: 1px solid var(--rule); }
.cell--devices, .cell--many { border-top: 1px solid var(--rule); }
.cell--picture { grid-row: 1 / 3; }
.cell h3 { position: relative; z-index: 1; font: 560 calc(24 * var(--u))/calc(25 * var(--u)) var(--sans); letter-spacing: -0.02em; color: #F1F1F1; max-width: calc(330 * var(--u)); }
.cell p { position: relative; z-index: 1; margin-top: calc(9 * var(--u)); font: 450 calc(16 * var(--u))/calc(19.5 * var(--u)) var(--sans); color: #A4A8AF; max-width: calc(330 * var(--u)); }
.cell--picture h3 { max-width: none; }
.cell--picture img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 100%; z-index: 0; }
.cell--picture::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to bottom, rgba(8, 8, 11, 0.55), transparent 35%); z-index: 0; }
.cell--latency, .cell--network { background-image: var(--scan); }
.cell--latency h3 { max-width: calc(250 * var(--u)); }
.cell__figure { margin-top: calc(22 * var(--u)) !important; font: 400 calc(49 * var(--u))/1 var(--mono) !important; color: #EDEDED !important; letter-spacing: 0.02em; }
.cell--network p { max-width: calc(200 * var(--u)); }
.cell--devices p { max-width: calc(240 * var(--u)); }
.chip-live { position: absolute; z-index: 1; right: calc(12.5 * var(--u)); top: calc(21.5 * var(--u)); padding: calc(2.5 * var(--u)) calc(7 * var(--u)); background: #F24E3A; color: #fff; font: 400 calc(11 * var(--u))/calc(14 * var(--u)) var(--mono); letter-spacing: 0.02em; border-radius: 1px; }
.cell--devices img { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; height: calc(150 * var(--u)); object-fit: cover; object-position: 50% 52%;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22%); mask-image: linear-gradient(to bottom, transparent 0, #000 22%); }
.cell--many h3 { max-width: calc(280 * var(--u)); }
/* Under its words the panel's card sits higher, in a shorter screen. */
.cell--many .panelshot { height: calc(146 * var(--u)); }
.cell--many .panelshot__card { top: calc(18 * var(--u)); }
/* The panel's QR on a screen, built rather than photographed: the code is the real one. */
.panelshot { position: absolute; left: 0; right: 0; bottom: 0; height: calc(205 * var(--u)); overflow: hidden;
  background: linear-gradient(to right, #07090C 0, #0B0E13 calc(40 * var(--u)), #10141B calc(44 * var(--u)), #0E1219 70%, #0B0E14 100%); }
.panelshot::before { content: ""; position: absolute; left: calc(46 * var(--u)); top: 0; bottom: 0; width: calc(8 * var(--u)); background: linear-gradient(#161B22, #12161C); box-shadow: 1px 0 0 rgba(255,255,255,0.05); }
.panelshot::after { content: ""; position: absolute; inset: 0; background: radial-gradient(120% 140% at 70% 40%, transparent 40%, rgba(5, 6, 9, 0.75) 100%); pointer-events: none; }
.panelshot__meter { position: absolute; left: calc(27 * var(--u)); top: calc(18 * var(--u)); bottom: 0; width: calc(9 * var(--u));
  background: linear-gradient(to bottom, #12301F 0 calc(40 * var(--u)), #2EBE63 calc(42 * var(--u)) 100%); filter: blur(0.6px); opacity: 0.85; }
.panelshot__card { position: absolute; left: calc(36 * var(--u)); top: calc(61 * var(--u)); width: calc(250 * var(--u)); height: calc(114 * var(--u)); display: flex; align-items: center; gap: calc(19 * var(--u));
  padding-left: calc(18 * var(--u)); border: 1px solid rgba(220, 228, 240, 0.28); border-radius: calc(7 * var(--u)); background: rgba(18, 22, 30, 0.55); filter: blur(0.25px); }
.panelshot__card img { width: calc(64 * var(--u)); height: calc(64 * var(--u)); padding: calc(4 * var(--u)); background: #F4F5F7; border-radius: calc(4 * var(--u)); box-shadow: 0 0 calc(14 * var(--u)) rgba(255,255,255,0.12); }
.panelshot__card p { margin: 0; font: 500 calc(12.5 * var(--u))/1.35 var(--mono); color: #EDF0F4; max-width: none; }
.panelshot__card b { display: block; font-weight: 500; font-size: calc(15 * var(--u)); margin-bottom: calc(3 * var(--u)); }
.panelshot__card span { color: #B8BFCA; font-weight: 400; font-size: calc(12.5 * var(--u)); }
.also { position: absolute; left: calc(58 * var(--u)); top: calc(584 * var(--u)); width: calc(1000 * var(--u)); }
.also h3 { font: 600 calc(22 * var(--u))/calc(28 * var(--u)) var(--sans); letter-spacing: -0.02em; color: #F1F1F1; }
.also ul { margin-top: calc(10 * var(--u)); display: grid; grid-template-columns: calc(307 * var(--u)) 1fr; grid-auto-flow: column; grid-template-rows: repeat(3, calc(24.5 * var(--u))); }
.also li { font: 400 calc(16 * var(--u))/calc(24.5 * var(--u)) var(--sans); color: #A5A9B0; }

/* ------------------------------------------------------------------ connections (8b-connections-fan) */
.connections { position: relative; min-height: calc(752 * var(--u)); overflow: hidden; }
.connections__in { height: calc(752 * var(--u)); }
.connections h2 { position: absolute; left: calc(58 * var(--u)); top: calc(64 * var(--u)); width: calc(620 * var(--u)); font: 600 calc(51.3 * var(--u))/calc(53 * var(--u)) var(--sans); letter-spacing: -0.03em; color: #F1F1F1; }
.roads { position: absolute; left: calc(58 * var(--u)); top: calc(212 * var(--u)); display: flex; flex-direction: column; }
.road { position: relative; appearance: none; border: 0; background: none; padding: 0; margin: 0; height: calc(63 * var(--u)); text-align: left; cursor: pointer;
  font: 500 calc(27.5 * var(--u))/calc(40 * var(--u)) var(--sans); letter-spacing: -0.02em; color: #8A8B8C; transition: color 200ms var(--ease); }
.road:hover { color: #C9CACC; }
.road[aria-selected="true"] { color: #F4F4F4; }
/* The marker is centred on the button, whose words a button centres too. */
.road::before { content: ""; position: absolute; left: calc(-19 * var(--u)); top: calc(50% - 22.5 * var(--u)); width: calc(4 * var(--u)); height: calc(45 * var(--u)); background: var(--tally); transform: scaleY(0); transition: transform 240ms var(--ease); }
.road[aria-selected="true"]::before { transform: scaleY(1); }
/* Where the light can draw it, the marker is a strip of specks (mesh.js), and the paint steps aside. */
.roads.is-specks .road::before { opacity: 0; }
.road:focus-visible { outline-offset: 6px; }
.fan { position: absolute; left: calc(343 * var(--u)); top: calc(190 * var(--u)); width: calc(900 * var(--u)); height: calc(270 * var(--u)); overflow: visible; }
.fan__device rect, .fan__device path { fill: none; stroke: #E9E9EA; stroke-width: 1.6; }
.fan__device .fan__inner { stroke: rgba(233, 233, 234, 0.35); stroke-width: 1; fill: #050505; }
.fan__device .fan__notch { fill: none; stroke-width: 1.3; }
.fan__device .fan__home { stroke-width: 2.4; stroke-linecap: round; }
.fan__road path, .fan__road rect { fill: none; stroke: #8E8F90; stroke-width: 1.5; stroke-linecap: round; transition: stroke 260ms var(--ease), opacity 260ms var(--ease); }
.fan__road > path { stroke: #7C7D7E; }
.fan__road .fan__mark path, .fan__road .fan__mark rect { stroke: #E7E7E8; stroke-width: 1.6; }
.fan__road .fan__fill { fill: #E7E7E8; stroke: none; }
.fan__road .fan__chip { stroke: rgba(235, 235, 236, 0.55) !important; stroke-width: 1.2 !important; fill: rgba(8, 8, 9, 0.85) !important; }
.fan__road text { font: 400 14.5px var(--mono); fill: #EDEDEE; text-anchor: middle; letter-spacing: 0.01em; }
.fan__dot { fill: #9A9B9C; transition: fill 260ms var(--ease); }
.fan__road.is-on > path { stroke: var(--tally); stroke-width: 2.4; filter: url(#roadGlow); }
/* The chosen road keeps its lit line under the specks, as the feature rows' tethers do, so the page's two kinds of
   beam are one (the owner, 2026-10-22). From 2026-10-17 it stepped back to a faint guide here so the specks read as
   the beam, and next to the tethers' lit lines it read as a different thing. */
.fan__road { cursor: pointer; }
.fan__road .fan__hit { stroke: transparent !important; stroke-width: 18; filter: none; transition: none; pointer-events: stroke; }
.fan__road.is-on .fan__dot { fill: var(--tally); }
.fan__road.is-on .fan__chip { stroke: var(--tally) !important; }
.fan__road.is-on .fan__mark path, .fan__road.is-on .fan__mark rect:not(.fan__chip) { stroke: #fff; }
.road-detail { position: absolute; left: calc(365 * var(--u)); top: calc(508 * var(--u)); width: calc(560 * var(--u)); }
.road-detail p { font: 450 calc(18 * var(--u))/calc(25 * var(--u)) var(--sans); color: #C2C4C8; max-width: calc(470 * var(--u)); }
/* Under the chosen kind, the particular links of that kind, in boxes (Wi-Fi's: the two ways the phone opens it). */
.road-detail__tags { display: flex; flex-wrap: wrap; gap: calc(8 * var(--u)); margin-top: calc(22 * var(--u)); }
.road-detail__tags[hidden] { display: none !important; }
.road-detail p.road-detail__tags { max-width: none; }
/* After the last box, unboxed: the kind has more links than the ones named. */
.road-detail__etc { align-self: center; font: 400 calc(14.2 * var(--u))/1 var(--mono); color: #8E9298; }
.road-detail__tags .tag { margin-top: 0; }
/* A box that is an address carries what kind of address it is, quieter, before it. */
.road-detail__tags .tag i { margin-right: calc(8 * var(--u)); font-style: normal; color: #8E9298; }
/* Each mark in the diagram answers the pointer over its whole box, not only on its thin strokes (home.js). */
.fan__markhit { fill: transparent; stroke: none !important; pointer-events: all; cursor: pointer; }
.tag { display: inline-block; margin-top: calc(26 * var(--u)); padding: 0 calc(9.5 * var(--u)); height: calc(26.5 * var(--u)); border: 1px solid rgba(255, 255, 255, 0.45); border-radius: calc(3.5 * var(--u));
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.02)); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
  font: 400 calc(14.2 * var(--u))/calc(25 * var(--u)) var(--mono); color: #EDEDEE; }

/* ------------------------------------------------------------------ reviews (9-reviews) */
.reviews { position: relative; min-height: calc(752 * var(--u)); overflow: hidden; }
.reviews__in { height: calc(752 * var(--u)); }
.reviews__label { position: absolute; left: calc(58 * var(--u)); top: calc(107 * var(--u)); font: 500 calc(16 * var(--u))/calc(20 * var(--u)) var(--sans); color: #DADADA; }
.quote { position: absolute; }
.quote blockquote p { color: #F3F3F3; font-weight: 600; letter-spacing: -0.016em; }
.quote--lead { left: calc(58 * var(--u)); top: calc(207 * var(--u)); width: calc(580 * var(--u)); }
.quote--lead blockquote p { font-size: calc(41.5 * var(--u)); line-height: calc(49 * var(--u)); }
.quote--one { left: calc(672 * var(--u)); top: calc(133 * var(--u)); width: calc(614 * var(--u)); }
.quote--two { left: calc(672 * var(--u)); top: calc(431 * var(--u)); width: calc(614 * var(--u)); }
.quote--one blockquote p, .quote--two blockquote p { font-size: calc(29.5 * var(--u)); line-height: calc(35 * var(--u)); letter-spacing: -0.014em; }
.quote--one::after { content: ""; position: absolute; left: 0; right: 0; top: calc(245.5 * var(--u)); height: 1px; background: rgba(255, 255, 255, 0.14); }
.quote figcaption { display: flex; align-items: center; gap: calc(25 * var(--u)); margin-top: calc(30 * var(--u)); }
.quote--lead figcaption { gap: calc(20 * var(--u)); margin-top: calc(40 * var(--u)); }
.quote figcaption img { width: calc(80 * var(--u)); height: calc(80 * var(--u)); border-radius: 50%; object-fit: cover; box-shadow: 0 0 0 1px rgba(255,255,255,0.06); }
.quote--lead figcaption img { width: calc(77 * var(--u)); height: calc(77 * var(--u)); }
.quote figcaption span { font: 400 calc(14.5 * var(--u))/calc(21.5 * var(--u)) var(--mono); color: #8B8F95; }
.quote figcaption b { display: block; font-weight: 400; color: #E6E6E6; }
/* On /pricing the count of editors heads the reviews (partials/reviews.html, round13 C6): it stands where the
   label was, and the quotes keep their places under it, 145 lower, as they sat below it in the mockup. */
.reviews__count { position: absolute; left: calc(58 * var(--u)); top: calc(80 * var(--u)); }
.reviews__count h2 { margin-top: calc(14 * var(--u)); font: 700 calc(36 * var(--u))/1.1 var(--sans); letter-spacing: -0.025em; color: #F2F2F3; }
.reviews__count em { font-style: normal; color: #FF5442; }
.reviews--count, .reviews--count .reviews__in { min-height: calc(825 * var(--u)); height: calc(825 * var(--u)); }
.reviews--count .quote--lead { top: calc(285 * var(--u)); }
.reviews--count .quote--one { top: calc(225 * var(--u)); }
.reviews--count .quote--two { top: calc(523 * var(--u)); }

/* ------------------------------------------------------------------ price (round7/pricing-g2-buy-the-bundle, the owner's choice of 2026-10-16) */
/* The cards are templates/partials/pricing.html, styled by plans.css, which /pricing shares. */
.price { position: relative; }
.price__in { padding: calc(40 * var(--u)) 0 calc(56 * var(--u)); }
/* /pricing (templates/pricing.html): the same section under the bar. */
.price--page .price__in { padding-top: calc(150 * var(--u)); padding-bottom: calc(110 * var(--u)); }
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ------------------------------------------------------------------ /pricing's own sections (round13) */
/* A section's small label over its heading, in tally with its dot. */
.eye { display: flex; align-items: center; gap: calc(8 * var(--u)); font: 500 calc(11 * var(--u))/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: #FF5442; }
.eye::before { content: ""; width: calc(7 * var(--u)); height: calc(7 * var(--u)); border-radius: 50%; background: currentColor; box-shadow: 0 0 8px rgba(255, 61, 39, 0.8); }

/* See the difference (round13, B1): a phone 900 wide and as long as an iPhone on its side, the reel 16:9 in
   the middle of its screen. --x is the line's place across the picture (compare.js): the watermark is cut off
   at it and the line is drawn there. A finger on the phone drags the line sideways and still scrolls the page. */
.compare { position: relative; --x: 50%; }
.compare__in { padding: calc(120 * var(--u)) 0 calc(110 * var(--u)); }
.compare__head { margin-left: calc(58 * var(--u)); }
.compare h2 { margin-top: calc(14 * var(--u)); font: 650 calc(48 * var(--u))/1.06 var(--sans); letter-spacing: -0.032em; color: #F2F2F3; }
.compare__say { margin-top: calc(16 * var(--u)); max-width: calc(480 * var(--u)); font: 400 calc(12.5 * var(--u))/1.6 var(--mono); color: #B0B3B8; }
.compare__phone { position: relative; width: calc(900 * var(--u)); aspect-ratio: 2.165; margin: calc(64 * var(--u)) auto 0; padding: calc(11 * var(--u)); border-radius: calc(46 * var(--u));
  background: linear-gradient(145deg, #34363c, #0d0e11 45%, #26282d); touch-action: pan-y pinch-zoom;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.09), 0 60px 120px -40px rgba(0, 0, 0, 0.95), 0 0 160px -40px rgba(250, 76, 51, 0.4); }
.compare__screen { position: absolute; inset: calc(11 * var(--u)); display: grid; place-items: center; overflow: hidden; border-radius: calc(36 * var(--u)); background: #000; }
.compare__frame { position: relative; height: 100%; aspect-ratio: 16 / 9; cursor: ew-resize; }
.compare__frame:focus-visible { outline-offset: -2px; }
.compare__frame video, .compare__mark { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* The free version's watermark, the plug-in's own (scripts/build-watermark-png.sh), over the picture left of the line. */
.compare__mark { max-width: none; clip-path: inset(0 calc(100% - var(--x)) 0 0); pointer-events: none; }
.compare__cut { position: absolute; top: 0; bottom: 0; left: var(--x); width: 2px; margin-left: -1px; background: rgba(255, 255, 255, 0.9); box-shadow: 0 0 12px rgba(0, 0, 0, 0.6); pointer-events: none; }
.compare__cut span { position: absolute; top: 50%; left: 50%; display: grid; place-items: center; width: calc(40 * var(--u)); height: calc(40 * var(--u)); margin: calc(-20 * var(--u)) 0 0 calc(-20 * var(--u)); border-radius: 50%;
  background: rgba(255, 255, 255, 0.92); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5); }
.compare__cut svg { width: calc(18 * var(--u)); height: calc(18 * var(--u)); fill: none; stroke: #0B0B0C; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* The two versions at the phone's edges, the mockup's legend. The one with more of the picture is lit, with an
   upright tally bar beside it, outside the free version's left edge and the full version's right, as the
   connections' chosen road has; while the page's light can draw it, the specks make the bar and travel from
   one version to the other (mesh.js, .is-specks). Hovering a name swipes the line all the way to it and
   leaving it brings the line back to rest; pressing one leaves it there (compare.js). */
.compare__caps { position: relative; display: flex; justify-content: space-between; width: calc(900 * var(--u)); margin: calc(26 * var(--u)) auto 0; }
.compare__cap { position: relative; appearance: none; display: flex; flex-direction: column; align-items: flex-start; gap: calc(6 * var(--u)); padding: 0; margin: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.compare__cap[data-for="full"] { align-items: flex-end; text-align: right; }
.compare__cap b { position: relative; font: 600 calc(15 * var(--u))/1 var(--sans); color: #8E939C; transition: color 240ms var(--ease); }
.compare__cap:hover b { color: #C9CACC; }
.compare__caps[data-side="free"] [data-for="free"] b, .compare__caps[data-side="full"] [data-for="full"] b { color: #F2F2F3; }
.compare__cap::before { content: ""; position: absolute; top: 0; bottom: 0; left: calc(-19 * var(--u)); width: calc(4 * var(--u)); background: var(--tally); transform: scaleY(0); transition: transform 240ms var(--ease); }
.compare__cap[data-for="full"]::before { left: auto; right: calc(-19 * var(--u)); }
.compare__caps[data-side="free"] [data-for="free"]::before, .compare__caps[data-side="full"] [data-for="full"]::before { transform: scaleY(1); }
.compare__caps.is-specks .compare__cap::before { opacity: 0; }
.compare__cap span { margin-top: calc(8 * var(--u)); font: 400 calc(11 * var(--u))/1.4 var(--mono); color: #8E939C; }
.compare__cap:focus-visible { outline-offset: 6px; }
.compare__pill { display: none; }
html[data-motion="still"] .compare__cap b, html[data-motion="still"] .compare__cap::before, html[data-motion="still"] .compare__pill, html[data-motion="still"] .compare__cap span { transition: none; }

/* Unlock in the full version (round13, F1): three cards, each a still of what a license turns on. */
.unlock { position: relative; }
.unlock__in { padding: calc(40 * var(--u)) calc(58 * var(--u)) calc(110 * var(--u)); }
.unlock h2 { margin-top: calc(14 * var(--u)); font: 650 calc(40 * var(--u))/1.08 var(--sans); letter-spacing: -0.03em; color: #F2F2F3; }
/* One board on the installers' pane (/download, .dl-pane): its dark fill, its edge and its diagonal lines,
   square, with the viewfinder's corners (.brackets), and its cells parted by rules (the owner, 2026-10-24: one
   container rather than three cards). The board runs from the grid's first line to its last. Its top row is
   the three cards, in equal thirds; the bento row under them is four cells a grid column wide, so its rules
   fall on the grid's lines. */
.unlock__board { position: relative; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); margin-top: calc(40 * var(--u)); border: 1px solid rgba(255, 255, 255, 0.09);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012)), rgba(10, 10, 12, 0.55);
  -webkit-backdrop-filter: blur(18px) saturate(140%); backdrop-filter: blur(18px) saturate(140%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 30px 60px -30px rgba(0, 0, 0, 0.9); }
.unlock__board::before { content: ""; position: absolute; inset: 0; background-image: var(--scan); opacity: 0.5; pointer-events: none; }
@media (prefers-reduced-transparency: reduce) { .unlock__board { -webkit-backdrop-filter: none; backdrop-filter: none; background: #0E0E10; } }
.unlock__cell { position: relative; grid-column: span 4; padding: calc(18 * var(--u)) calc(18 * var(--u)) calc(24 * var(--u)); }
.unlock__cell + .unlock__cell { border-left: 1px solid rgba(255, 255, 255, 0.1); }
.unlock__cell h3 { position: relative; margin-top: calc(20 * var(--u)); font: 600 calc(19 * var(--u))/1.2 var(--sans); letter-spacing: -0.01em; color: #F2F2F3; }
.unlock__cell > p { position: relative; margin-top: calc(8 * var(--u)); font: 400 calc(12 * var(--u))/1.6 var(--mono); color: #A9AEB7; }
/* The bento row: a figure or an icon over a smaller heading, the row ruled off from the cards above. */
.unlock__cell--small { grid-column: span 4; padding: calc(22 * var(--u)) calc(22 * var(--u)) calc(26 * var(--u)); border-top: 1px solid rgba(255, 255, 255, 0.1); }
.unlock__cell--small:nth-child(4) { border-left: 0; }
.unlock__cell--small h3 { margin-top: calc(16 * var(--u)); font-size: calc(16 * var(--u)); }
.unlock__cell--small > p:last-child { font-size: calc(11 * var(--u)); }
.unlock__cell > .unlock__figure { position: relative; height: calc(30 * var(--u)); margin: 0; font: 400 calc(34 * var(--u))/calc(30 * var(--u)) var(--mono); letter-spacing: 0.02em; color: #EDEDED; }
.unlock__icon { position: relative; display: grid; place-items: center; width: calc(30 * var(--u)); height: calc(30 * var(--u)); border: 1px solid rgba(255, 255, 255, 0.14); color: #C6C9CE; }
.unlock__icon svg { width: calc(17 * var(--u)); height: calc(17 * var(--u)); fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.unlock__art { position: relative; display: grid; place-items: center; height: calc(230 * var(--u)); overflow: hidden; border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  background: radial-gradient(70% 70% at 50% 40%, rgba(250, 76, 51, 0.12), transparent 70%); }
/* Audio: the home page's Sound row (.dev--big, drawn in --k), about half its size, with the panel open and the sound on. */
.unlock__art--sound { --k: calc(0.56 * var(--u)); }
.unlock__art--sound .dev--big { left: 50%; top: 50%; transform: translate(-50%, -50%); }
.unlock__art--sound .hud { transition: none; }
/* Notifications: a lock screen rising from the card's foot, the notification on it. */
.lock { align-self: end; width: calc(230 * var(--u)); height: calc(204 * var(--u)); padding: calc(16 * var(--u)) calc(12 * var(--u)) 0; border-radius: calc(26 * var(--u)) calc(26 * var(--u)) 0 0;
  background: radial-gradient(80% 60% at 30% 10%, #3b2a4a, transparent 70%), radial-gradient(80% 70% at 90% 90%, #3a1e18, transparent 70%), #121218;
  box-shadow: 0 0 0 calc(5 * var(--u)) #1d1e22, 0 0 0 calc(6 * var(--u)) rgba(255, 255, 255, 0.1); }
.lock__time { text-align: center; font: 300 calc(40 * var(--u))/1 -apple-system, BlinkMacSystemFont, var(--sans); color: rgba(255, 255, 255, 0.9); }
.lock__note { display: flex; gap: calc(8 * var(--u)); margin-top: calc(26 * var(--u)); padding: calc(9 * var(--u)); border-radius: calc(14 * var(--u)); background: rgba(40, 40, 46, 0.78); text-align: left; }
.lock__note img { flex: none; width: calc(26 * var(--u)); height: calc(26 * var(--u)); border-radius: calc(6 * var(--u)); }
.lock__note > div { flex: 1; min-width: 0; }
.lock__note p { font: 400 calc(9.5 * var(--u))/1.35 -apple-system, BlinkMacSystemFont, var(--sans); color: #EDEDF0; }
.lock__note p:first-child { display: flex; justify-content: space-between; }
.lock__note p:first-child span { color: #9A9AA2; }
/* Several devices: a tablet, a phone on its side and one upright, in --s so the three fit their card. */
.unlock__art--devices { --s: calc(0.8 * var(--u)); grid-template-columns: auto auto auto; gap: calc(14 * var(--s)); align-items: end; align-content: center; justify-content: center; }
.mini { position: relative; padding: calc(5 * var(--s)); border-radius: calc(18 * var(--s)); background: linear-gradient(145deg, #34363c, #0d0e11 45%, #26282d);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.09), 0 24px 40px -18px rgba(0, 0, 0, 0.9); }
.mini img { width: 100%; height: 100%; border-radius: calc(13 * var(--s)); object-fit: cover; }
.mini--tablet { width: calc(190 * var(--s)); height: calc(138 * var(--s)); border-radius: calc(12 * var(--s)); }
.mini--side { width: calc(150 * var(--s)); height: calc(72 * var(--s)); border-radius: calc(12 * var(--s)); }
.mini--tablet img, .mini--side img { border-radius: calc(8 * var(--s)); }
.mini--upright { width: calc(70 * var(--s)); height: calc(146 * var(--s)); }

/* ------------------------------------------------------------------ questions (10-faq) */
.faq { position: relative; min-height: calc(752 * var(--u)); overflow: hidden; }
.faq__in { min-height: calc(752 * var(--u)); }
.faq__head { position: absolute; left: calc(58 * var(--u)); top: calc(147 * var(--u)); }
.faq h2 { font: 650 calc(54.5 * var(--u))/calc(62 * var(--u)) var(--sans); letter-spacing: -0.032em; color: #EFEFEF; }
.faq__head p { margin-top: calc(11 * var(--u)); font: 400 calc(12.9 * var(--u))/1.4 var(--mono); color: #B0B3B8; }
.faq__head a { text-decoration: none; }
.faq__head a:hover { color: #fff; text-decoration: underline; text-decoration-color: var(--tally); }
.faq__list { position: relative; margin-left: calc(672 * var(--u)); width: calc(614 * var(--u)); padding-top: calc(115 * var(--u)); padding-bottom: calc(110 * var(--u)); }
.faq details + details { border-top: 1px solid var(--rule); }
.faq summary { position: relative; display: flex; align-items: center; height: calc(73.3 * var(--u)); padding-right: calc(56 * var(--u)); cursor: pointer; list-style: none;
  font: 500 calc(22.5 * var(--u))/1.2 var(--sans); letter-spacing: -0.015em; color: #F1F1F1; }
.faq summary::-webkit-details-marker { display: none; }
/* Each question is an H3 for its outline; it looks as the summary always has. */
.faq summary h3 { font: inherit; letter-spacing: inherit; color: inherit; }
.faq summary::before, .faq summary::after { content: ""; position: absolute; right: calc(18 * var(--u)); top: 50%; width: calc(25 * var(--u)); height: 1.5px; background: #C9C9CA; transition: transform 260ms var(--ease), background 260ms var(--ease); }
.faq summary::after { transform: rotate(90deg); }
.faq details[open]:not(.is-closing) summary::before { transform: rotate(45deg); background: var(--tally); }
.faq details[open]:not(.is-closing) summary::after { transform: rotate(135deg); background: var(--tally); }
.faq details:first-child summary { height: calc(68 * var(--u)); align-items: flex-start; padding-top: calc(18 * var(--u)); }
.faq details[open]:not(.is-closing) summary { height: calc(62 * var(--u)); }
/* While a question grows or shrinks (home.js), its answer is clipped to the height so far. */
.faq details.is-moving { overflow: hidden; }
.faq details p { margin-top: calc(-2 * var(--u)); padding-bottom: calc(21 * var(--u)); max-width: calc(585 * var(--u)); font: 400 calc(12.8 * var(--u))/calc(25.5 * var(--u)) var(--mono); color: #B5B8BD; }
.faq details p a { color: #E6E6E6; }

/* ------------------------------------------------------------------ footer (round 2, C) */
/* Four sections, one to each of the grid's columns (58, 365, 672 and 979), then the lockup across the first three
   with the fine print beside it in the fourth. The light is the footer's own pool, low on the left (mesh.js). */
.footer { position: relative; }
.footer::before { content: ""; position: absolute; top: 0; left: 50%; width: calc(1229 * var(--u)); transform: translateX(-50%); height: 1px; background: rgba(255, 255, 255, 0.16); }
.footer__in { padding: calc(84 * var(--u)) 0 0; }
.footer__links { display: grid; grid-template-columns: repeat(4, calc(307 * var(--u))); margin-left: calc(58 * var(--u)); }
.footer__group { display: flex; flex-direction: column; align-items: flex-start; padding: 0 calc(28 * var(--u)); }
.footer__group:first-child { padding-left: 0; }
.footer__head { margin: 0 0 calc(18 * var(--u)); font: 500 calc(11 * var(--u))/1.4 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: #6E727A; }
.footer__links a { display: flex; align-items: center; gap: calc(10 * var(--u)); padding: calc(6 * var(--u)) 0; font: 400 calc(16 * var(--u))/1.4 var(--sans); color: #C9CDD3; text-decoration: none; white-space: nowrap; }
.footer__links a:hover { color: #fff; }
.footer__social svg { width: calc(17 * var(--u)); height: calc(17 * var(--u)); fill: none; stroke: #8A8E95; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.footer__social:hover svg { stroke: currentColor; }
.footer__end { display: grid; grid-template-columns: calc(921 * var(--u)) calc(307 * var(--u)); align-items: end; margin: calc(96 * var(--u)) 0 0 calc(58 * var(--u)); padding-bottom: calc(44 * var(--u)); }
.footer__home { display: block; width: max-content; line-height: 0; }
.footer__home img { display: block; width: calc(860 * var(--u)); height: auto; }
.footer__fine { padding-left: calc(28 * var(--u)); font: 400 calc(12.5 * var(--u))/1.55 var(--sans); color: #6E727A; }
.footer__fine p { margin: 0; max-width: calc(250 * var(--u)); }
.footer__fine p + p { margin-top: calc(10 * var(--u)); }

/* ------------------------------------------------------------------ phones and narrow windows */
@media (max-width: 899px) {
  :root { --u: 1px; }
  .frame { width: auto; padding: 0 20px; }
  /* On a phone the grid is the two margins. */
  .lines::before { width: 100%; background-image: var(--g), var(--g); background-size: 1px 100%; background-position: 20px 0, calc(100% - 20px) 0; }
  .lines::after, .hero .lines { display: none; }

  .hero { height: auto; padding: 128px 0 0; }
  .hero-scene { position: relative; inset: auto; height: min(84vw, 480px); margin-top: 4px; -webkit-mask-image: linear-gradient(to bottom, transparent, #000 10%, #000 80%, transparent); mask-image: linear-gradient(to bottom, transparent, #000 10%, #000 80%, transparent); }
  .hero__in { position: absolute; inset: 0; height: auto; }
  .hero { display: flex; flex-direction: column-reverse; }
  .hero__in { position: relative; padding: 0 20px; }
  .hero h1, .hero__lead, .hero .actions, .hero__note { position: static; width: auto; }
  .hero__note { margin-top: 14px; font-size: 12px; }
  .hero h1 { font-size: clamp(34px, 9.6vw, 46px); line-height: 1.1; max-width: 13em; }
  .hero__lead { margin-top: 18px; font-size: 17px; line-height: 1.55; max-width: 30em; }
  /* The two buttons one above the other, the same width: the column's (the owner, 2026-10-11). */
  .hero .actions { margin-top: 28px; gap: 12px; display: flex; flex-direction: column; align-items: stretch; max-width: 420px; }
  .hero .actions .button { width: 100%; padding: 0 22px; height: 52px; font-size: 16px; justify-content: center; }
  .button svg { width: 17px; height: 17px; }

  .features, .connections, .reviews, .faq { min-height: 0; }
  .features__in, .connections__in, .reviews__in, .faq__in { height: auto; min-height: 0; padding-top: 88px; padding-bottom: 88px; }
  .features h2, .ledger, .also, .connections h2, .roads, .fan, .road-detail,
  .reviews__label, .quote, .faq__head { position: relative; left: auto; top: auto; width: auto; }
  .film__in { padding: 72px 20px 8px; }
  .film h2 { margin-left: 0; font-size: clamp(32px, 9vw, 46px); line-height: 1.12; }
  .film__frame { margin: 28px 0 0; width: 100%; }
  .film__play { width: 64px; height: 64px; margin: -32px 0 0 -32px; }
  .film__play svg { width: 24px; height: 24px; margin-left: 3px; }
  .film__len { left: calc(50% + 44px); font-size: 14px; }
  .film__bar { left: 8px; right: 8px; bottom: 9px; gap: 2px; height: 42px; padding: 0 4px; border-radius: 9px; }
  .film__btn { width: 36px; height: 36px; border-radius: 8px; }
  .film__btn svg { width: 16px; height: 16px; }
  .film__cc { padding: 0 7px; font-size: 11px; }
  .film__q { min-width: 0; padding: 0 6px; font-size: 11px; }
  .film__menu { min-width: 136px; padding: 4px; border-radius: 9px; }
  /* Rows short enough that the menu stays inside an upright phone's frame. */
  .film__menu button { gap: 10px; height: 30px; padding: 0 12px 0 26px; border-radius: 6px; font-size: 12px; }
  .film__menu button[aria-checked="true"]::before { left: 11px; width: 6px; height: 6px; margin-top: -3px; }
  .film__time { margin: 0 4px; font-size: 11.5px; }
  /* The film's length goes, which the play button showed, so the scrubber keeps its room beside Quality. */
  .film__dur { display: none; }
  .film__seek { height: 32px; margin: 0 4px; }
  .film__seek::-webkit-slider-thumb { width: 14px; height: 14px; margin-top: -5.5px; }
  .film__seek::-moz-range-thumb { width: 14px; height: 14px; }
  .film__cap { bottom: 60px; max-width: 88%; padding: 4px 10px; font-size: 14px; border-radius: 5px; }
  /* A phone's frame is too short for the play button and two lines of captions at once. */
  .film__frame[data-state="paused"] .film__cap { display: none; }
  .feat__in { height: auto; display: flex; flex-direction: column; padding-top: 72px; padding-bottom: 8px; }
  .feat__words { order: -1; position: static; width: auto; }
  .feat__eye { font-size: 12px; gap: 9px; }
  .feat__eye::before { width: 7px; height: 7px; }
  .feat h2 { margin-top: 14px; font-size: clamp(28px, 8vw, 38px); line-height: 1.12; max-width: 14em; }
  .feat__note { margin-top: 26px; padding-top: 12px; max-width: 30em; font-size: 11.5px; line-height: 1.5; }
  .feat__words > h2 + p { margin-top: 14px; font-size: 16px; line-height: 1.55; max-width: 32em; }
  .feat__more { margin-top: 12px; font-size: 15px; }
  .feat__stage, .feat--flip .feat__stage { --k: calc((100vw - 40px) / 768); position: relative; left: auto; top: auto; margin-top: 28px; }
  /* The phone ends at 318 of the stage's 472 (30 down, 288 tall): stopped short, the gap under it is the others'. */
  .stage--controls { height: calc(360 * var(--k)); }

  .features h2 { font-size: clamp(38px, 11vw, 56px); line-height: 1.1; }
  .ledger { margin-top: 32px; height: auto; display: block; border: 1px solid var(--rule); }
  .cell { padding: 20px 20px 22px; min-height: 0; }
  .cell + .cell { border-left: 0; border-top: 1px solid var(--rule); }
  .cell h3 { font-size: 22px; line-height: 1.15; max-width: none; }
  .cell p { font-size: 16px; line-height: 1.35; margin-top: 8px; max-width: 32em; }
  .cell--picture { min-height: 380px; }
  .cell__figure { margin-top: 18px !important; font-size: 48px !important; }
  .chip-live { top: 22px; right: 16px; font-size: 11px; line-height: 14px; padding: 2px 7px; }
  .cell--devices { padding-bottom: 200px; }
  .cell--devices img { height: 190px; }
  .cell--many { padding-bottom: 170px; }
  .cell--many .panelshot { height: 150px; }
  .cell--many .panelshot__card { top: 18px; }
  .panelshot { height: 205px; --u: 1px; }
  .panelshot__card { left: 64px; width: auto; right: 16px; }
  .also { margin-top: 40px; }
  .also h3 { font-size: 22px; }
  .also ul { display: block; margin-top: 10px; }
  .also li { font-size: 16px; line-height: 1.7; }

  .connections h2 { font-size: clamp(32px, 9vw, 46px); line-height: 1.12; }
  .roads { margin-top: 28px; flex-direction: row; flex-wrap: wrap; gap: 4px 20px; }
  .road { font-size: 20px; height: 44px; line-height: 44px; }
  .road::before { left: 0; top: auto; bottom: 2px; width: 100%; height: 2px; transform: scaleX(0); }
  .road[aria-selected="true"]::before { transform: scaleX(1); }
  .fan { margin: 24px -20px 0; width: calc(100% + 40px); height: auto; }
  /* Every road's words and boxes are laid in the same two cells, the hidden ones kept invisible rather than
     taken out, so the box is the tallest road's whichever is chosen. When it took the chosen one's height,
     the page below moved with every tap, and the light placed by the sections jumped with it (the owner,
     2026-10-16). */
  .road-detail { margin-top: 16px; display: grid; grid-template-columns: minmax(0, 1fr); }
  .road-detail > p[data-detail] { grid-area: 1 / 1; align-self: end; }
  .road-detail > p[data-tag] { grid-area: 2 / 1; align-self: start; }
  .road-detail > p[data-detail][hidden] { display: block !important; visibility: hidden; }
  .road-detail > p.road-detail__tags[hidden] { display: flex !important; visibility: hidden; }
  .road-detail p { font-size: 17px; line-height: 1.5; }
  .tag { height: 28px; line-height: 26px; font-size: 14px; padding: 0 10px; margin-top: 18px; }

  .reviews__label { font-size: 15px; }
  .quote { margin-top: 40px; }
  .quote--lead blockquote p { font-size: 30px; line-height: 1.3; }
  .quote--one blockquote p, .quote--two blockquote p { font-size: 23px; line-height: 1.35; }
  .quote--one::after { display: none; }
  .quote--two { padding-top: 40px; border-top: 1px solid rgba(255,255,255,0.14); }
  .quote figcaption { margin-top: 22px; gap: 18px; }
  .quote figcaption img, .quote--lead figcaption img { width: 64px; height: 64px; }
  .quote figcaption span { font-size: 14px; line-height: 21px; }

  .price__in { padding: 72px 0 56px; }
  .price--page .price__in { padding-top: 112px; }

  .compare__in { padding: 72px 20px 64px; }
  .compare__head { margin-left: 0; }
  .compare h2, .unlock h2 { font-size: 34px; }
  .compare__say { font-size: 13px; }
  .compare__phone { width: 100%; margin-top: 36px; padding: 7px; border-radius: 26px; }
  .compare__screen { inset: 7px; border-radius: 20px; }
  .compare__cut span { width: 34px; height: 34px; margin: -17px 0 0 -17px; }
  /* On a phone the two names sit together and take turns: a square slides under the one with more of the
     picture, and its line fades up under both (the owner, 2026-10-24: there is no room for the two edges). */
  .compare__caps { display: grid; grid-template-columns: repeat(2, 140px); justify-content: center; width: auto; margin-top: 22px; padding-bottom: 34px; }
  .compare__cap, .compare__cap[data-for="full"] { align-items: center; text-align: center; }
  .compare__cap b { padding: 9px 0; font-size: 15px; }
  .compare__cap { position: static; }
  .compare__cap::before { display: none; }
  .compare__caps[data-side="free"] [data-for="free"] b, .compare__caps[data-side="full"] [data-for="full"] b { color: #FF5442; }
  .compare__cap span { position: absolute; left: 0; right: 0; bottom: 0; margin: 0; text-align: center; font-size: 12px; opacity: 0; transform: translateY(8px);
    transition: opacity 240ms var(--ease), transform 420ms var(--ease-out); }
  .compare__caps[data-side="free"] [data-for="free"] span, .compare__caps[data-side="full"] [data-for="full"] span { opacity: 1; transform: none; transition-delay: 80ms; }
  .compare__pill { display: block; position: absolute; top: 0; left: calc(50% - 140px); width: 140px; height: 33px; border: 1px solid rgba(255, 84, 66, 0.5); background: rgba(255, 64, 34, 0.08);
    box-shadow: 0 0 18px -6px rgba(250, 76, 51, 0.6); transition: transform 420ms var(--ease-out); }
  .compare__caps[data-side="full"] .compare__pill { transform: translateX(100%); }
  .unlock__in { padding: 24px 20px 64px; }
  /* On a phone the cards are one column, each ruled off from the next, and the bento row two by two. */
  .unlock__board { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 28px; }
  .unlock__cell { grid-column: 1 / -1; padding: 14px 14px 20px; }
  .unlock__cell + .unlock__cell { border-left: 0; border-top: 1px solid rgba(255, 255, 255, 0.1); }
  .unlock__cell > p { font-size: 13px; }
  .unlock__cell--small { grid-column: span 1; padding: 18px 14px 20px; }
  .unlock__cell--small:nth-child(5) { border-left: 1px solid rgba(255, 255, 255, 0.1); }
  .unlock__cell--small:nth-child(6) { grid-column: 1 / -1; }
  .unlock__cell--small h3 { font-size: 16px; } .unlock__cell--small > p:last-child { font-size: 12px; }
  .unlock__art { height: 210px; }
  .unlock__art--sound { --k: min(0.5px, calc((100vw - 80px) / 640)); }
  .unlock__art--devices { --s: min(0.72px, calc((100vw - 84px) / 450)); }
  .reviews__count { position: relative; left: auto; top: auto; }
  .reviews__count h2 { font-size: 30px; }
  .reviews--count, .reviews--count .reviews__in { height: auto; min-height: 0; }
  .reviews--count .reviews__in { padding-top: 64px; }
  .reviews--count .quote { top: auto; }
  .reviews--count .quote--lead { margin-top: 48px; }
  .footer::before { width: calc(100% - 40px); }

  .faq h2 { font-size: 44px; line-height: 1.1; }
  .faq__head p { font-size: 13px; }
  .faq__list { margin: 36px 0 0; width: auto; padding: 0; }
  .faq summary { height: auto !important; min-height: 64px; padding: 16px 48px 16px 0 !important; align-items: center !important; font-size: 19px; }
  .faq summary::before, .faq summary::after { width: 20px; }
  .faq details p { font-size: 13.5px; line-height: 1.75; padding-bottom: 24px; margin-top: 0; }

  .footer__in { padding: 40px 20px 0; }
  .footer__links { grid-template-columns: 1fr 1fr; gap: 28px 20px; margin: 0; }
  .footer__group, .footer__group:first-child { padding: 0; }
  .footer__head { margin: 0 0 4px; font-size: 11px; }
  /* Each link a full tap tall: 44 px, the size a finger needs. */
  .footer__links a { padding: 11px 0; font-size: 15px; line-height: 22px; white-space: normal; }
  .footer__social svg { width: 17px; height: 17px; }
  .footer__end { display: block; margin: 40px 0 0; padding-bottom: 48px; }
  /* The width belongs on the link, not the image. Safari sizes a max-content box around an image whose width is a
     percentage from the logo's own 6,490 px, which made every phone page that wide. */
  .footer__home { width: min(100%, 420px); }
  .footer__home img { width: 100%; }
  .footer__fine { margin-top: 24px; padding: 0; font-size: 12px; }
  .footer__fine p { max-width: none; }
  .footer__fine p + p { margin-top: 8px; }
}


@media (prefers-reduced-motion: reduce) {
  .road::before, .fan__road path, .faq summary::before, .faq summary::after { transition: none !important; }
}
/* The same when the reader has paused motion with the switch, and the buttons stop springing. */
html[data-motion="still"] .road::before, html[data-motion="still"] .fan__road path,
html[data-motion="still"] .faq summary::before, html[data-motion="still"] .faq summary::after { transition: none !important; }
html[data-motion="still"] .button:active { transform: none; }
@media (max-width: 899px) { br.wide { display: none; } }
/* The hero on a phone held upright (the owner, 2026-10-11): the scene fills the screen and the words
   and buttons sit at its foot, over a darkening, so the Mac and the phone are above them and whole. (The
   scene had a box under the headline, and the bottom of the screen cut it off.) hero.ts frames the scene
   in the room above the headline, and through the sequence it stays where it is. */
@media (max-width: 899px) and (orientation: portrait) {
  .hero { height: 100svh; min-height: 560px; padding: 0; display: block; }
  .hero-scene { position: absolute; inset: 0; height: auto; margin: 0; -webkit-mask-image: none; mask-image: none; }
  /* Its foot clears the pause switch's row (44 px, 16 up), so the two buttons keep the column's width. */
  .hero__in { position: absolute; left: 0; right: 0; bottom: 0; top: auto; z-index: 1; height: auto; padding: 0 20px calc(max(16px, env(safe-area-inset-bottom)) + 58px); }
  .hero__in::before { content: ""; position: absolute; left: 0; right: 0; top: -72px; bottom: 0; z-index: -1; pointer-events: none;
    background: linear-gradient(rgba(4, 5, 6, 0), rgba(4, 5, 6, 0.86) 72px); }
  /* That darkening reaches the hero's foot at full strength, so the section below carries it on and lets it
     fade: stopped at the seam, it drew a hard black edge across the light behind the page. */
  .film::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 160px; pointer-events: none;
    background: linear-gradient(rgba(4, 5, 6, 0.86), rgba(4, 5, 6, 0.5) 40%, rgba(4, 5, 6, 0)); }
  .film__in { position: relative; }
  .hero h1 { font-size: clamp(30px, 8.6vw, 40px); line-height: 1.08; }
  .hero__lead { margin-top: 12px; font-size: 15.5px; line-height: 1.5; }
  .hero .actions { margin-top: 20px; gap: 10px; }
  .hero .actions .button { height: 48px; }
  .hero__note { margin-top: 10px; }
}
/* No pause switch on a touch screen (the owner, 2026-10-11): there is no pointer to stir the light, and
   the switch took the whole bottom row. The phone's own Reduce Motion setting still pauses everything
   (motion.js follows it). Its room goes back to the hero's words; in the sequence Exit sits just left of
   Next, both in the thumb's reach (the owner, 2026-10-11: Skip, on the left edge, was hard to reach). */
@media (hover: none) and (pointer: coarse) {
  .button.motion { display: none; }
}
@media (hover: none) and (pointer: coarse) and (max-width: 899px) and (orientation: portrait) {
  .hero__in { padding-bottom: max(20px, env(safe-area-inset-bottom)); }
}
