/* ==========================================================================
   Lê Xuân Lộc — portfolio
   Static, dependency-free. One self-hosted typeface (Fraunces, OFL) for
   display; system faces for everything else.
   Order: tokens · base · top bar · hero + sky · day rail · chapters · story
          (sticky stage + beats) · spreads · night sections · about · essays ·
          stages · clips · living art · motion
   ========================================================================== */

:root {
  color-scheme: light;

  --bg: #fdfcfa;
  --ink: #14130f;
  --ink-2: rgba(20, 19, 15, 0.78);
  --ink-3: rgba(20, 19, 15, 0.62);
  --hair: rgba(20, 19, 15, 0.1);
  --wash: #f4f2ed;
  --amber: #855300;

  /* The day's skies, top of the page to the bottom. */
  --dawn: #fdfcfa;
  --morning: #fbf1dc;
  --noon: #f6f7f2;
  --sunset: #f6d9bd;
  --dusk: #2b1f35;
  --night: #0c0c0b;

  /* Every face here carries Vietnamese diacritics. */
  --serif: "Fraunces", ui-serif, "New York", "Iowan Old Style", Georgia, serif;
  --sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  --gutter: clamp(1.25rem, 4vw, 2rem);
  --gutter: max(clamp(1.25rem, 4vw, 2rem), env(safe-area-inset-left), env(safe-area-inset-right));
  --text: min(34rem, 100vw - 2 * var(--gutter));
  --wall: min(64rem, 100vw - 2 * var(--gutter));
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);

  /* One grain texture, reused on every tinted surface. */
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 .7 -.08'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

/* Sections under an evening or night sky flip their ink. */
.on-dark {
  --ink: #f3efe6;
  --ink-2: rgba(243, 239, 230, 0.74);
  --ink-3: rgba(243, 239, 230, 0.52);
  --hair: rgba(255, 255, 255, 0.14);
  --amber: #ffb42e;
  color: var(--ink);
}

/* ---------- base ---------- */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overflow-x: hidden;
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 1rem/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3,
p,
ul,
ol,
figure {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img,
video,
svg {
  display: block;
  max-width: 100%;
}

img,
video {
  height: auto;
}

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 32%, transparent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-color: currentColor;
}

button {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection {
  background: #ffe2a6;
  color: #14130f;
}

[hidden] {
  display: none !important;
}

.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: var(--gutter);
  top: -4rem;
  z-index: 50;
  padding: 0.5rem 0.9rem;
  border-radius: 0 0 0.5rem 0.5rem;
  background: #14130f;
  color: #fdfcfa;
  text-decoration: none;
}

.skip:focus {
  top: 0;
}

.col {
  width: var(--text);
  margin-inline: auto;
}

.wide {
  width: var(--wall);
  margin-inline: auto;
}

.label {
  font: 500 0.75rem/1.3 var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ---------- top bar ---------- */

.bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 2rem;
  padding-top: calc(clamp(1.5rem, 4vw, 2.5rem) + env(safe-area-inset-top));
}

.me {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  text-decoration: none;
  line-height: 1.35;
}

.me h1,
.me b {
  display: block;
  font-size: inherit;
  font-weight: 600;
}

.me span span,
.me div span {
  display: block;
  color: var(--ink-3);
}

.mark {
  flex: none;
  display: block;
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  overflow: hidden;
  background: #0c0c0b;
  box-shadow: 0 0 0 1px var(--hair);
}

.mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.nav {
  display: flex;
  align-items: center;
  gap: 1.35rem;
}

.nav a {
  margin-inline: -0.35rem;
  padding: 0.6rem 0.35rem;
  color: var(--ink-3);
  text-decoration: none;
  transition: color 0.15s;
}

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

/* ---------- hero: one sentence, one picture ---------- */

.hero {
  padding-top: clamp(2rem, 5vw, 3.5rem);
}

.hero__say {
  max-width: 17em;
  font: 340 clamp(2rem, 1.05rem + 3.9vw, 3.5rem) / 1.08 var(--serif);
  letter-spacing: -0.022em;
  text-wrap: balance;
}

.hero__say em {
  font-style: italic;
  font-weight: 300;
}

.hero__meta {
  margin-top: 1.1rem;
  max-width: 36rem;
  color: var(--ink-2);
  text-wrap: pretty;
}

.hero__meta a {
  color: var(--ink);
}

.sky {
  margin-top: clamp(1.75rem, 4vw, 2.75rem);
}

.sky__box {
  position: relative;
  overflow: hidden;
  border-radius: 1rem;
  width: 100%;
  aspect-ratio: 16 / 7;
  /* On a short window the picture stops growing, and the slice gives up empty sky, not the horizon. */
  max-height: max(22rem, 46vh);
  max-height: max(22rem, 46svh);
  background: #f3ead9;
  isolation: isolate;
}

.sky__box::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--grain);
  mix-blend-mode: soft-light;
  opacity: 0.6;
  pointer-events: none;
}

.sky svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Each motif is one app. Point at one, or at its name below, and it becomes that app's icon:
   the rest step back, it lifts off the picture, and it floats there. */

/* How far the sun has come up (the page moves it as you scroll), and how high a lifted motif floats.
   Registered as lengths so they can be animated and used in sums. */
@property --rise {
  syntax: "<length>";
  inherits: true;
  initial-value: 34px;
}

@property --float {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

/* At rest the picture is the dawn one: sun low, a tint over the sky. Where the browser can
   tie animation to scrolling, the sun then climbs and the tint leaves (see "Scroll effects"). */
.sky .sunrise {
  transform: translateY(var(--rise, 34px));
}

.sky .dawn {
  opacity: 0.85;
}

.sky .m {
  transition: opacity 0.25s;
}

.sky a.m {
  cursor: pointer;
  outline: none;
}

/* Only the still shapes catch the pointer; the drawing moves freely and can't slip from under it. */
.sky .m__art {
  pointer-events: none;
}

/* The orbits rest faint and come up for Knowless; the sun's bright core and halo show only then. */
.sky .orbits {
  opacity: 0.6;
  transition: opacity 0.4s;
}

.sky .m-sun .halo,
.sky .m-sun .core {
  opacity: 0;
  transition: opacity 0.25s;
}

.sky figcaption {
  margin-top: 0.9rem;
  font-size: 0.875rem;
  color: var(--ink-3);
}

/* Pointing needs a pointer: on a touch screen a tap just follows the link, and nothing is left lit. */
@media (hover: hover) and (pointer: fine) {
  .sky:has(.m-knowless:hover, [data-app="knowless"]:hover, [data-app="knowless"]:focus-visible) .m:not(.m-knowless),
  .sky:has(.m-baseline:hover, [data-app="baseline"]:hover, [data-app="baseline"]:focus-visible) .m:not(.m-baseline, .m-dot),
  .sky:has(.m-lifelong:hover, [data-app="lifelong"]:hover, [data-app="lifelong"]:focus-visible) .m:not(.m-lifelong),
  .sky:has(.m-lockeat:hover, [data-app="lockeat"]:hover, [data-app="lockeat"]:focus-visible) .m:not(.m-lockeat),
  .sky:has(.m-elsewhere:hover, [data-app="elsewhere"]:hover, [data-app="elsewhere"]:focus-visible) .m:not(.m-elsewhere, .m-sun) {
    opacity: 0.22;
  }

  .sky:has(.m-knowless:hover, [data-app="knowless"]:hover, [data-app="knowless"]:focus-visible) .orbits {
    opacity: 1;
  }
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* The float is always there and only its height changes, so a motif glides back down
     when you leave it. It runs only while the pointer or the keyboard is on the picture. */
  .sky .m__art {
    transition: translate 0.5s var(--ease), scale 0.5s var(--ease), --float 0.5s var(--ease);
    animation: levitate 2.2s ease-in-out infinite alternate paused;
  }

  .sky:is(:hover, :focus-within) .m__art {
    animation-play-state: running;
  }

  .sky:has(.m-knowless:hover, [data-app="knowless"]:hover, [data-app="knowless"]:focus-visible) .m-knowless .m__art,
  .sky:has(.m-baseline:hover, [data-app="baseline"]:hover, [data-app="baseline"]:focus-visible) :is(.m-baseline, .m-dot) .m__art,
  .sky:has(.m-lifelong:hover, [data-app="lifelong"]:hover, [data-app="lifelong"]:focus-visible) .m-lifelong .m__art,
  .sky:has(.m-lockeat:hover, [data-app="lockeat"]:hover, [data-app="lockeat"]:focus-visible) .m-lockeat .m__art,
  .sky:has(.m-elsewhere:hover, [data-app="elsewhere"]:hover, [data-app="elsewhere"]:focus-visible) :is(.m-elsewhere, .m-sun) .m__art {
    translate: 0 -16px;
    --float: -6px;
    transition: translate 0.5s var(--ease), scale 0.5s var(--ease), --float 1.2s ease-in-out 0.3s;
  }

  /* The bowl already rides the water; that is its float. */
  .sky:has(.m-lockeat:hover, [data-app="lockeat"]:hover, [data-app="lockeat"]:focus-visible) .m-lockeat .m__art {
    --float: 0px;
  }

  /* baseline: the dot is the loudest shape in its icon, so it comes forward. */
  .sky .m-dot .m__art {
    transform-box: fill-box;
    transform-origin: center;
  }

  .sky:has(.m-baseline:hover, [data-app="baseline"]:hover, [data-app="baseline"]:focus-visible) .m-dot .m__art {
    translate: 0 -24px;
    scale: 1.5;
    --float: -4px;
  }

  /* Elsewhere: the waves only float; the sun comes all the way up, a whole circle above the water. */
  .sky:has(.m-elsewhere:hover, [data-app="elsewhere"]:hover, [data-app="elsewhere"]:focus-visible) .m-elsewhere .m__art {
    translate: 0 -4px;
  }

  /* The sun changes role, so it moves more slowly and starts softly. Its sums use --rise,
     so it ends in the same place however far the page has scrolled. */
  .sky .m-sun .m__art {
    transform-origin: 640px 266px;
    transition: translate 0.7s cubic-bezier(0.4, 0, 0.2, 1), scale 0.7s cubic-bezier(0.4, 0, 0.2, 1), --float 0.5s var(--ease);
  }

  .sky:has(.m-elsewhere:hover, [data-app="elsewhere"]:hover, [data-app="elsewhere"]:focus-visible) .m-sun .m__art {
    translate: 0 calc(-46px - var(--rise, 34px));
    scale: 0.75;
    --float: -8px;
    transition: translate 0.7s cubic-bezier(0.4, 0, 0.2, 1), scale 0.7s cubic-bezier(0.4, 0, 0.2, 1), --float 1.2s ease-in-out 0.3s;
  }

  /* Knowless: the sun becomes the small bright core of its icon. It condenses, lights, then blooms. */
  .sky:has(.m-knowless:hover, [data-app="knowless"]:hover, [data-app="knowless"]:focus-visible) .m-sun .m__art {
    translate: 0 min(-16px, calc(-30px - var(--rise, 34px)));
    scale: 0.55;
    --float: -10.9px;
    transition: translate 0.7s cubic-bezier(0.4, 0, 0.2, 1), scale 0.7s cubic-bezier(0.4, 0, 0.2, 1), --float 1.2s ease-in-out 0.3s;
  }

  .sky:has(.m-knowless:hover, [data-app="knowless"]:hover, [data-app="knowless"]:focus-visible) .m-sun .core {
    opacity: 1;
    transition: opacity 0.5s 0.15s;
  }

  .sky:has(.m-knowless:hover, [data-app="knowless"]:hover, [data-app="knowless"]:focus-visible) .m-sun .halo {
    opacity: 1;
    transition: opacity 0.6s 0.3s;
  }

  .is-paused .sky .m__art,
  .sky .is-away .m__art {
    animation-play-state: paused;
  }

  @keyframes levitate {
    to {
      transform: translateY(var(--float));
    }
  }
}

/* The five apps, by name. Each name is the key to its motif above. */
.proof {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(0.5rem, 2.4vw, 2rem);
  margin-top: clamp(1rem, 2.4vw, 1.5rem);
}

.proof a {
  display: block;
  color: var(--ink);
  text-decoration: none;
}

.proof__name {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: 0.7rem;
  font-size: 1.0625rem;
  line-height: 1.25;
}

.proof__name img {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 0.6rem;
  box-shadow: 0 0 0 1px var(--hair);
  transition: transform 0.45s var(--ease);
}

.proof a:hover .proof__name img {
  transform: translateY(-0.2rem);
}

.proof__note {
  margin-top: 1rem;
  font-size: 0.8125rem;
  color: var(--ink-3);
}

/* On a laptop the picture is a band, not a poster, so the five names sit on the first screen.
   One height, tall enough that every motif, lifted and floating, clears the top edge. */
@media (min-width: 56rem) {
  .sky__box {
    aspect-ratio: auto;
    height: 19rem;
    max-height: none;
  }
}

/* A little less air above the fold on a laptop. */
@media (min-width: 40rem) {
  .bar {
    padding-top: calc(1.75rem + env(safe-area-inset-top));
  }

  .hero {
    padding-top: 2.25rem;
  }

  .sky {
    margin-top: 2rem;
  }

  .proof {
    margin-top: 1.25rem;
  }
}

@media (max-width: 39.99rem) {
  .sky__box {
    aspect-ratio: 2 / 1;
    max-height: none;
  }

  /* On a phone the five names still fit in one row: icon above, name below. */
  .proof {
    gap: 0.25rem;
  }

  .proof__name {
    grid-template-columns: none;
    justify-items: center;
    row-gap: 0.35rem;
    font-size: 0.8125rem;
    text-align: center;
  }

  .proof__name img {
    width: 2.25rem;
    height: 2.25rem;
  }

  /* The narrowest phones: the longest name still has to fit under its icon. */
  @media (max-width: 21.5rem) {
    .proof__name {
      font-size: 0.6875rem;
    }
  }

  .me span span,
  .me div span {
    font-size: 0.875rem;
  }

  .nav {
    gap: 1.1rem;
    font-size: 0.9375rem;
  }
}

/* ---------- rail: five stops on a ruled line, and a marker for how far you have read ---------- */

.rail {
  position: fixed;
  z-index: 40;
  top: calc(0.6rem + env(safe-area-inset-top));
  left: 50%;
  width: min(21rem, 100vw - 1.5rem);
  height: 3rem;
  border-radius: 999px;
  /* Glass: the page shows through, blurred; a bright rim along the top edge, a soft one inside. */
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0.26) 55%, rgba(255, 255, 255, 0.4)),
    rgba(250, 248, 244, 0.36);
  -webkit-backdrop-filter: blur(18px) saturate(1.8) brightness(1.04);
  backdrop-filter: blur(18px) saturate(1.8) brightness(1.04);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 -1px 0 rgba(255, 255, 255, 0.35),
    inset 0 0 0 1px rgba(255, 255, 255, 0.35),
    0 0 0 1px rgba(20, 19, 15, 0.07),
    0 10px 28px -12px rgba(20, 19, 15, 0.38),
    0 2px 6px -2px rgba(20, 19, 15, 0.12);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -140%);
  transition: transform 0.45s var(--ease), opacity 0.3s, visibility 0s 0.45s, background-color 0.4s;
  --pad: 1.75rem;
  --inset: 1.1rem;
  --track: 2.25rem;
  --rule: rgba(20, 19, 15, 0.18);
  --rule-on: #1d1a14;
  --surface: rgba(255, 255, 255, 0.92);
}

.rail.is-on {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition: transform 0.45s var(--ease), opacity 0.3s, background-color 0.4s;
}

/* Over the dark chapters the rail goes dark with the page. */
.rail.is-night {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.04) 55%, rgba(255, 255, 255, 0.08)),
    rgba(26, 23, 30, 0.44);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    inset 0 -1px 0 rgba(255, 255, 255, 0.08),
    inset 0 0 0 1px rgba(255, 255, 255, 0.1),
    0 0 0 1px rgba(0, 0, 0, 0.35),
    0 10px 28px -12px rgba(0, 0, 0, 0.7);
  --rule: rgba(255, 255, 255, 0.24);
  --rule-on: #f3efe6;
  --surface: rgba(26, 23, 30, 0.92);
}

/* Where the browser can't blur what is behind, or the visitor asked for less transparency: a solid bar. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .rail {
    background: rgba(253, 252, 250, 0.95);
  }

  .rail.is-night {
    background: rgba(22, 20, 24, 0.92);
  }
}

@media (prefers-reduced-transparency: reduce) {
  .rail {
    background: #fdfcfa;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  .rail.is-night {
    background: #17151a;
  }
}

/* The line: faint for the whole length, inked as far as you have read. */
.rail::before,
.rail::after {
  content: "";
  position: absolute;
  left: var(--inset);
  top: var(--track);
  height: 2px;
  border-radius: 2px;
}

.rail::before {
  right: var(--inset);
  background: var(--rule);
}

.rail::after {
  width: calc(var(--pad) - var(--inset) + (100% - 2 * var(--pad)) * var(--day, 0));
  background: var(--rule-on);
}

/* Each stop: an icon above the line, and a tick where it meets the line. */
.rail a {
  position: absolute;
  top: 0;
  left: calc(var(--pad) + (100% - 2 * var(--pad)) * var(--at));
  display: grid;
  justify-items: center;
  align-content: start;
  width: 2.75rem;
  height: 100%;
  padding-top: 0.5rem;
  transform: translateX(-50%);
}

.rail a::after {
  content: "";
  position: absolute;
  left: calc(50% - 1px);
  top: calc(var(--track) - 5px);
  width: 2px;
  height: 5px;
  border-radius: 1px 1px 0 0;
  background: var(--rule);
  transition: background-color 0.2s;
}

.rail a[aria-current="true"]::after {
  background: var(--rule-on);
}

.rail a img {
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 0.3rem;
  box-shadow: 0 0 0 1px rgba(20, 19, 15, 0.14);
  opacity: 0.6;
  filter: saturate(0.7);
  transform: scale(0.88);
  transition: opacity 0.2s, filter 0.2s, transform 0.3s var(--ease), box-shadow 0.3s;
}

.rail a:hover img,
.rail a[aria-current="true"] img {
  opacity: 1;
  filter: none;
  transform: scale(1.12);
  box-shadow: 0 0 0 1px rgba(20, 19, 15, 0.14), 0 4px 9px -4px rgba(20, 19, 15, 0.5);
}

.rail.is-night a img {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.28);
}

.rail.is-night a:hover img,
.rail.is-night a[aria-current="true"] img {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.4), 0 4px 9px -4px rgba(0, 0, 0, 0.8);
}

.rail a:focus-visible {
  outline-color: var(--rule-on);
  outline-offset: -5px;
  border-radius: 0.9rem;
}

/* The marker rides on the line, clear of the icons. */
.rail__sun {
  position: absolute;
  z-index: 1;
  top: calc(var(--track) + 1px);
  left: calc(var(--pad) + (100% - 2 * var(--pad)) * var(--day, 0));
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 50%;
  background: #e09400;
  box-shadow: 0 0 0 2.5px var(--surface), 0 1px 4px rgba(20, 19, 15, 0.35);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* ---------- "Before" ---------- */

.before {
  padding-top: clamp(4.5rem, 11vw, 8rem);
  padding-bottom: clamp(3rem, 8vw, 6rem);
}

.lede > * + * {
  margin-top: 1.1em;
}

.lede p {
  font-size: 1.125rem;
  line-height: 1.6;
}

.lede .label {
  margin-bottom: 1.5rem;
}

/* The design-system rules: short lines, each on its own. */
.lede .rules {
  display: grid;
  gap: 0.55rem;
  padding-left: 1.1rem;
  list-style: disc;
  font-size: 1.0625rem;
  line-height: 1.5;
}

.lede .rules li::marker {
  color: var(--amber);
}

/* ---------- chapters: the sky changes as the day goes ---------- */

.chapter {
  position: relative;
  padding: var(--fade, 9rem) 0 clamp(4rem, 9vw, 7rem);
  background: linear-gradient(to bottom, var(--from), var(--sky) var(--fade, 9rem));
  background-color: var(--sky);
}

.chapter--baseline {
  --from: var(--dawn);
  --sky: var(--morning);
}

.chapter--lockeat {
  --from: var(--morning);
  --sky: var(--noon);
}

.chapter--elsewhere {
  --from: var(--noon);
  --sky: var(--sunset);
}

.chapter--lifelong {
  --sky: var(--dusk);
  --fade: 16rem;
  background: linear-gradient(to bottom, var(--sunset), #e3a48f 4.5rem, #a8627c 9rem, #5a3a5c 12.5rem, var(--dusk) 16rem);
  background-color: var(--dusk);
}

/* Sections overlap by a pixel, so no light seam shows between two dark skies at odd zoom levels. */
.chapter + .chapter,
.chapter + .night,
.night + .night,
.night + .about {
  margin-top: -1px;
}

.chapter--knowless {
  --from: var(--dusk);
  --sky: var(--night);
  --fade: 12rem;
}

.chapter__head {
  display: grid;
  gap: 1.25rem;
  justify-items: start;
}

.scene {
  width: clamp(8rem, 22vw, 11rem);
  aspect-ratio: 5 / 3;
  overflow: visible;
}

.chapter__head .label {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.1rem 1.1rem;
  min-height: 2.0625rem;
  color: var(--ink-2);
}

/* Where the app can be had. */
.store {
  display: inline-block;
  padding: 0.55rem 0;
  color: var(--ink);
}

.chapter__head h2 {
  max-width: 14em;
  font: 340 clamp(2.25rem, 1.2rem + 4.4vw, 4rem) / 1.02 var(--serif);
  letter-spacing: -0.025em;
  text-wrap: balance;
}

/* ---------- story: beats, each with its picture ----------
   The plain layout, used on phones, on short windows and without script:
   every beat is followed by its own picture, at a size a thumb can use.
   Where there is room, the pictures stack on a stage that stays put while
   the beats scroll past it (the media query further down). */

.story {
  display: grid;
  margin-top: clamp(1.5rem, 4vw, 3rem);
}

.story__stage,
.beats {
  display: contents;
}

/* Beat 1, picture 1, beat 2, picture 2, and so on. */
.beat:nth-child(1) { order: 1; }
.shot:nth-child(1) { order: 2; }
.beat:nth-child(2) { order: 3; }
.shot:nth-child(2) { order: 4; }
.beat:nth-child(3) { order: 5; }
.shot:nth-child(3) { order: 6; }
.beat:nth-child(4) { order: 7; }
.shot:nth-child(4) { order: 8; }
.beat:nth-child(5) { order: 9; }
.shot:nth-child(5) { order: 10; }
.beat:nth-child(6) { order: 11; }
.shot:nth-child(6) { order: 12; }

.shot {
  display: grid;
  justify-items: center;
  gap: 0.9rem;
  padding: 0.5rem 0 3.25rem;
}

/* One definite cell, so a phone's height: 100% has something to measure against. */
.shot__media {
  display: grid;
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  place-items: center;
  width: 100%;
  min-height: 0;
}

.shot .phone {
  width: min(17rem, 70vw);
  aspect-ratio: 0.472;
}

.shot .phone > img,
.shot .phone > video,
.shot .phone > .screen {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.shot__card {
  max-width: 100%;
  max-height: 100%;
  border-radius: 0.875rem;
  box-shadow: 0 30px 50px -28px rgba(12, 9, 4, 0.55);
}

.shot .shot__card {
  max-width: min(100%, 24rem);
}

/* A clip that is not a phone screen: my demo video already draws its own phone. */
.shot__clip {
  position: relative;
  display: block;
  width: min(100%, 20rem);
  aspect-ratio: 3 / 5;
  overflow: hidden;
  border-radius: 0.875rem;
  background: #0c0c0b;
  box-shadow: 0 30px 50px -28px rgba(12, 9, 4, 0.55);
}

.shot__clip video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A line drawing of an idea that was tried and dropped, on its own sheet. */
.sheet {
  position: relative;
  display: grid;
  place-items: center;
  width: min(100%, 17rem);
  aspect-ratio: 1;
  padding: 1.25rem;
  border-radius: 1.25rem;
  background: rgba(255, 180, 46, 0.045);
  box-shadow: inset 0 0 0 1px rgba(255, 180, 46, 0.2);
}

.sketch {
  width: 100%;
  max-height: 100%;
  aspect-ratio: 1;
}

/* Pause and play for anything on the stage that moves by itself. */
.hold {
  position: absolute;
  z-index: 3;
  left: 0.7rem;
  bottom: 0.7rem;
  display: grid;
  place-items: center;
  width: 2.125rem;
  height: 2.125rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(28, 26, 22, 0.6);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  backdrop-filter: blur(10px) saturate(1.4);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
  cursor: pointer;
}

/* A finger needs more than the circle it sees. */
.hold::after {
  content: "";
  position: absolute;
  inset: -0.4rem;
}

.phone > .hold::after {
  inset: -0.4rem -0.2rem -0.4rem -0.4rem;
}

/* While the whole page is paused, a drawing's own control would have nothing to do. */
html.is-paused [data-pause] > .hold {
  display: none;
}

.hold::before {
  content: "";
  width: 0.5rem;
  height: 0.62rem;
  border: solid #fff;
  border-width: 0 0.16rem;
}

.hold.is-held::before {
  width: 0;
  height: 0;
  margin-left: 0.14rem;
  border-style: solid;
  border-width: 0.34rem 0 0.34rem 0.56rem;
  border-color: transparent transparent transparent #fff;
}

.hold:focus-visible {
  outline-color: #fff;
}

/* A clip driven by the scroll needs no pause control. */
.is-scrubbed > .hold {
  display: none;
}

/* Beside the phone, not over its screen. */
.phone > .hold {
  left: auto;
  right: -2.75rem;
  bottom: 6%;
}

.for {
  max-width: 24rem;
  text-align: center;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--ink-2);
  text-wrap: balance;
}

.for b {
  margin-right: 0.4em;
  font: 500 0.6875rem/1 var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber);
}

.for small {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.8125rem;
  color: var(--ink-3);
}

.for small a {
  color: var(--ink-2);
}

.beat {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: 1.75rem 0 1.25rem;
}

.beat h3 {
  margin-bottom: 0.9rem;
  font: 500 0.75rem/1.3 var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber);
}

.beat p {
  max-width: 27rem;
  font-size: clamp(1.125rem, 1rem + 0.5vw, 1.3125rem);
  line-height: 1.5;
  text-wrap: pretty;
}

.beat p + p {
  margin-top: 0.9em;
}

.beat .more {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.5rem;
  font-size: 1rem;
}

.more a {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.65em 1.1em;
  border-radius: 999px;
  background: var(--ink);
  color: var(--sky, var(--bg));
  font-weight: 550;
  text-decoration: none;
}

.more a.more__alt {
  background: none;
  color: var(--ink);
  box-shadow: inset 0 0 0 1px currentColor;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 34%, transparent);
}

/* On a phone the first still of the logging screen would be followed by two more views of the
   same screen (the clip, then the recreation), so the story goes straight on to the clip. */
@media (max-width: 39.99rem) {
  #baseline .shot:nth-child(2) {
    display: none;
  }
}

/* A short, wide window (a phone on its side): beat on the left, its picture on the right. */
@media (min-width: 40rem) and (max-height: 39.99rem) {
  .story {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 clamp(1.5rem, 5vw, 3rem);
    align-items: center;
  }

  .shot {
    padding: 1.5rem 0;
  }

  /* Pictures fit the height of the window. The two recreations keep their width: they need it to work. */
  .shot .phone,
  .shot .shot__clip {
    width: auto;
    height: 84vh;
    height: 84svh;
    max-width: 100%;
  }

  .shot .phone--play {
    width: min(15rem, 100%);
    height: auto;
  }

  .shot .sheet {
    width: min(100%, 72vh);
  }

  .pair .phone {
    max-width: calc(84vh * 0.472);
    max-width: calc(84svh * 0.472);
  }

  .pair .shot__card {
    max-width: calc(84vh * 0.7);
    max-width: calc(84svh * 0.7);
  }

  .trailer {
    max-height: calc(100vh - 4.5rem);
    max-height: calc(100svh - 4.5rem);
    margin-inline: auto;
  }
}

/* Room for a stage: two columns, the pictures stacked and pinned, the beats passing. */
@media (min-width: 44rem) and (min-height: 40rem) {
  .js .story {
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: 0 clamp(3.75rem, 7vw, 6rem);
    align-items: start;
  }

  .js .story__stage {
    display: block;
    position: sticky;
    top: 4.25rem;
    z-index: 2;
    height: calc(100vh - 5.5rem);
    height: calc(100svh - 5.5rem);
    container-type: size;
  }

  /* The room under the last beat keeps the stage still while that beat is read. */
  .js .beats {
    display: block;
    padding-bottom: 14vh;
    padding-bottom: 14svh;
  }

  /* Every picture sits in the same place: the picture row is capped, the
     caption starts right under it, and the pair is centred on the stage.
     Hidden pictures stay in the reading and tab order; only their opacity goes. */
  .js .shot {
    position: absolute;
    inset: 0;
    grid-template-rows: minmax(0, 40rem);
    grid-template-rows: minmax(0, min(40rem, 100cqw / 0.472));
    align-content: center;
    gap: 0.75rem;
    padding: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.45s;
  }

  .js .shot.is-on {
    opacity: 1;
    pointer-events: auto;
  }

  .js .shot__media {
    height: 100%;
  }

  .js .shot figcaption {
    align-self: start;
  }

  /* On the stage a phone is sized by height, so it always fits the window. */
  .js .shot .phone {
    width: auto;
    height: 100%;
    max-height: 40rem;
  }

  .js .shot .shot__clip {
    width: auto;
    height: 100%;
    height: min(100%, 100cqw / 0.6);
    max-height: 40rem;
  }

  .js .shot .sheet {
    width: min(100%, 21rem);
    aspect-ratio: 4 / 5;
    padding: 1.5rem;
  }

  .js .shot .shot__card {
    max-width: 100%;
  }

  /* A short, wide picture keeps its caption close instead of holding the phone's row. */
  .js .shot--snug {
    grid-template-rows: auto;
  }

  .js .shot--snug .shot__media {
    height: auto;
  }

  .js .beat {
    justify-content: center;
    min-height: 72vh;
    min-height: 72svh;
    padding: 2rem 0;
    opacity: 0.3;
    transition: opacity 0.4s;
  }

  .js .beat.is-on {
    opacity: 1;
  }

  /* The pads' hints (the nudging arrows, the ring on the microphone) wait until their picture is on stage. */
  .js .shot:not(.is-on) .pb-hint__v,
  .js .shot:not(.is-on) .pb-hint__h,
  .js .shot:not(.is-on) .pk-say::after {
    animation: none;
  }
}

/* ---------- spreads: the three shorter chapters ---------- */

.spread {
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  margin-top: clamp(1.75rem, 4vw, 3rem);
  align-items: start;
}

.spread__text > * + * {
  margin-top: 1.05em;
}

.spread__text p {
  max-width: 27rem;
  font-size: clamp(1.125rem, 1rem + 0.5vw, 1.3125rem);
  line-height: 1.5;
  text-wrap: pretty;
}

.spread__text .quiet {
  font-size: 0.9375rem;
  color: var(--ink-2);
}

.pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.875rem, 2.5vw, 1.75rem);
  align-items: start;
}

.pair figure:nth-child(2) {
  margin-top: clamp(1.5rem, 5vw, 3.5rem);
}

.pair .shot__card {
  width: 100%;
}

.pair .for {
  margin-top: 0.8rem;
  text-align: left;
}

.spread__text .more {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.5rem;
  font-size: 1rem;
}

/* On a phone two pictures side by side are too small to read: they become a row you swipe. */
@media (max-width: 39.99rem) {
  .pair {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: min(62vw, 17rem);
    margin-inline: calc(var(--gutter) * -1);
    padding: 0.25rem var(--gutter) 0.75rem;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
  }

  .pair::-webkit-scrollbar {
    display: none;
  }

  .pair figure {
    scroll-snap-align: start;
  }

  .pair figure:nth-child(2) {
    margin-top: 0;
  }
}

@media (min-width: 56rem) {
  .spread {
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  }

  .spread--flip {
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  }

  .spread--flip .spread__text {
    order: 2;
  }

  /* Intro, pictures, then the story: stacked on a phone; on a desk the text
     runs down the left while the pictures hold the right. */
  .spread--three {
    grid-template-areas: "a pics" "b pics";
    align-items: start;
  }

  .spread--three .spread__a {
    grid-area: a;
    align-self: end;
  }

  .spread--three .spread__b {
    grid-area: b;
  }

  .spread--three .pair {
    grid-area: pics;
    align-self: center;
  }
}

.folder {
  margin-top: clamp(3rem, 8vw, 5.5rem);
  color: var(--ink-2);
}

.folder + .closing {
  margin-top: 0.75rem;
}

/* ---------- a phone ---------- */

.phone {
  position: relative;
  z-index: 1;
  display: block;
  padding: 0.34rem;
  border-radius: 15.5% / 7.25%;
  background: linear-gradient(160deg, #4b4b4f 0%, #18181a 20%, #0b0b0c 58%, #303033 100%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 30px 50px -26px rgba(12, 9, 4, 0.6), 0 10px 18px -10px rgba(12, 9, 4, 0.35);
}

.phone > img,
.phone > video,
.phone > .screen {
  display: block;
  width: 100%;
  border-radius: 13.8% / 6.35%;
}

.phone > video {
  background: #0c0c0b;
}

/* ---------- night sections: what I learned, all five ---------- */

.night {
  padding: clamp(4rem, 9vw, 7rem) 0;
  background: var(--night);
}

.night + .night {
  padding-top: 0;
}

.trailer {
  position: relative;
  overflow: hidden;
  border-radius: 1rem;
  aspect-ratio: 16 / 9;
  background: #0e0e0d;
  box-shadow: inset 0 0 0 1px var(--hair);
}

.trailer video {
  width: 100%;
  height: 100%;
}

/* One large play control over the poster; the browser's own controls take over once it runs. */
.trailer__play {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.js .trailer__play {
  display: grid;
}

.trailer__play span {
  display: grid;
  place-items: center;
  width: clamp(3.5rem, 9vw, 4.75rem);
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(243, 239, 230, 0.94);
  box-shadow: 0 18px 40px -16px rgba(0, 0, 0, 0.8);
  transition: transform 0.3s var(--ease);
}

.trailer__play span::before {
  content: "";
  margin-left: 0.3rem;
  border-style: solid;
  border-width: 0.7rem 0 0.7rem 1.15rem;
  border-color: transparent transparent transparent #14130f;
}

.trailer__play:hover span {
  transform: scale(1.06);
}

.trailer__play:focus-visible {
  outline-color: #f3efe6;
  outline-offset: -6px;
  border-radius: 1rem;
}

.trailer + .for {
  max-width: none;
  margin-top: 0.9rem;
  text-align: left;
}

.closing {
  margin-top: clamp(3rem, 8vw, 5.5rem);
  font: 340 clamp(2.25rem, 1.2rem + 4.6vw, 4.25rem) / 1.02 var(--serif);
  letter-spacing: -0.025em;
  text-wrap: balance;
}

.closing em {
  font-style: italic;
  font-weight: 300;
  color: #ffb42e;
}

/* ---------- about: the next morning ---------- */

.about {
  padding: 24rem 0 0;
  background: linear-gradient(to bottom, var(--night), #2b1f35 5rem, #8a5a7c 10rem, #e9a58a 14.5rem, #f8dcbc 19rem, var(--dawn) 24rem);
}

.prose > p:not(.big):not(.quiet):not(.contact-address) {
  font-size: 1.125rem;
}

.about .prose > p.more {
  font-size: 1rem;
}

.contact-address {
  font: 340 clamp(1.375rem, 1.1rem + 1.2vw, 1.75rem) / 1.2 var(--serif);
  letter-spacing: -0.01em;
}

.contact-address a {
  text-decoration-color: color-mix(in srgb, currentColor 30%, transparent);
}

/* Where a link to the contact details, the About text or the 36-second cut lands. */
#contact,
#about-h {
  scroll-margin-top: 5rem;
}

#all-five {
  scroll-margin-top: clamp(2.5rem, 7vw, 4.5rem);
}

.about .more {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.prose > * + * {
  margin-top: 1.1em;
}

.prose h3 {
  margin-top: 2.75rem;
  font: 500 0.75rem/1.3 var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.prose .big {
  font: 340 clamp(1.5rem, 1.1rem + 1.8vw, 2.125rem) / 1.2 var(--serif);
  letter-spacing: -0.015em;
}

.prose .quiet {
  font-size: 0.9375rem;
  color: var(--ink-2);
}

.foot {
  margin-top: clamp(4rem, 10vw, 6rem);
  padding-bottom: calc(3rem + env(safe-area-inset-bottom));
  font-size: 0.8125rem;
  color: var(--ink-3);
}

.foot p + p {
  margin-top: 0.3rem;
}

.credits {
  font-size: 0.875rem;
  color: var(--ink-2);
}

.credits summary {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.6rem 0;
  list-style: none;
  cursor: pointer;
  color: var(--ink);
}

.credits summary::-webkit-details-marker {
  display: none;
}

.credits summary::before {
  content: "+";
  content: "+" / "";
  font: 500 0.75rem/1 var(--mono);
  color: var(--ink-3);
}

.credits[open] summary::before {
  content: "–";
  content: "–" / "";
}

.credits ul {
  margin-top: 0.75rem;
  display: grid;
  gap: 0.4rem;
}

.credits p {
  margin-top: 0.75rem;
}

/* ==========================================================================
   Essay pages
   ========================================================================== */

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: calc(clamp(1.5rem, 4vw, 2.5rem) + env(safe-area-inset-top));
}

/* An element marked hidden stays hidden, whatever else styles it. */
[hidden] {
  display: none !important;
}

/* Scripts with no italic of their own: emphasis is carried by the words, not a slant. */
:lang(ja) em,
:lang(ko) em,
:lang(zh) em,
:lang(th) em,
:lang(hi) em {
  font-style: normal;
}

/* The headlines are set tight for Latin letters. These scripts fill the whole line, or stack
   marks above and below it, so their lines need room, and no tightening between letters. */
:is(:lang(ja), :lang(ko), :lang(zh), :lang(hi)) :is(.hero__say, .chapter__head h2, .closing, .essay-head h1) {
  line-height: 1.28;
  letter-spacing: 0;
}

:lang(th) :is(.hero__say, .chapter__head h2, .closing, .essay-head h1) {
  line-height: 1.42;
  letter-spacing: 0;
}

:is(:lang(ja), :lang(ko), :lang(zh), :lang(hi), :lang(th)) :is(.contact-address, .prose .big) {
  line-height: 1.45;
  letter-spacing: 0;
}

:is(:lang(ja), :lang(zh), :lang(th)) body {
  line-height: 1.75;
}

/* The small labels are spaced-out Latin capitals in a typewriter face. These scripts have no
   capitals, and Thai and Devanagari join their letters: spaced apart, the words fall to pieces. */
:is(:lang(ja), :lang(ko), :lang(zh), :lang(hi), :lang(th)) :is(.label, .for b, .beat h3, .prose h3, .stage figcaption b) {
  font-family: var(--sans);
  font-size: 0.8125rem;
}

:is(:lang(hi), :lang(th)) :is(.label, .for b, .beat h3, .prose h3, .stage figcaption b, .art-toggle) {
  letter-spacing: 0;
}

/* Where a line may break. Japanese: not before a small kana or a long-vowel mark, and in
   headlines only between phrases. Korean: only at a space, never inside a word. */
:lang(ja) {
  line-break: strict;
}

:lang(ja) :is(h1, h2, h3, .hero__say, .closing) {
  word-break: auto-phrase;
}

:lang(ko) {
  word-break: keep-all;
}

.cover {
  position: relative;
  width: var(--text);
  margin: 2.5rem auto 0;
  overflow: hidden;
  border-radius: 0.875rem;
  aspect-ratio: 2 / 1;
  background: var(--tint, #0c0c0b);
  isolation: isolate;
}

.cover::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--grain);
  mix-blend-mode: soft-light;
  opacity: 0.6;
  pointer-events: none;
}

.cover svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.essay-head {
  margin-top: 2.5rem;
}

.essay-head h1 {
  font: 340 clamp(2rem, 1.3rem + 3vw, 2.875rem) / 1.06 var(--serif);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.essay-head p {
  margin-top: 0.85rem;
  color: var(--ink-3);
}

.essay {
  margin-top: 2.5rem;
}

.essay > p {
  width: var(--text);
  margin-inline: auto;
  font-size: 1.0625rem;
  line-height: 1.62;
  text-wrap: pretty;
}

.essay > p + p {
  margin-top: 1.1em;
}

.essay strong {
  font-weight: 600;
}

.essay h2 {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: var(--text);
  margin: 4rem auto 1.4rem;
  font-size: 1rem;
  font-weight: 600;
  scroll-margin-top: 1.5rem;
}

.essay h2::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hair);
}

sup {
  font-size: 0.7em;
  line-height: 0;
}

sup a {
  margin: -0.6em -0.35em;
  padding: 0.6em 0.5em;
  color: var(--ink-3);
  text-decoration: none;
}

sup a:hover {
  color: var(--ink);
}

/* A note in the author's hand, under a drawn bracket. */
.essay > .hand {
  margin-top: 1.75rem;
  text-align: center;
  font: italic 340 1.1875rem/1.3 var(--serif);
  color: var(--amber);
}

.hand svg {
  width: 100%;
  height: 0.9rem;
  margin-bottom: 0.4rem;
  color: var(--ink-3);
}

.hand span {
  display: inline-block;
  max-width: 19rem;
  transform: rotate(-1.5deg);
  text-wrap: balance;
}

/* ---------- stages ---------- */

.stage {
  width: var(--stage-w, var(--text));
  margin: 2.5rem auto;
}

.stage--wide {
  --stage-w: min(46rem, 100vw - 2 * var(--gutter));
}

.stage__in {
  position: relative;
  display: flex;
  justify-content: center;
  overflow: hidden;
  border-radius: 0.875rem;
  background: var(--tint, var(--wash));
  box-shadow: inset 0 0 0 1px var(--hair);
  isolation: isolate;
}

.stage__in::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  background: var(--grain);
  mix-blend-mode: soft-light;
  opacity: 0.5;
  pointer-events: none;
}

.stage .phone {
  width: min(20rem, 74%);
}

.t-knowless {
  --tint: radial-gradient(90% 60% at 62% 104%, rgba(255, 180, 46, 0.26), transparent 70%), #0c0c0b;
}

.t-baseline {
  --tint: repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.035) 0 1px, transparent 1px 11px),
    radial-gradient(80% 60% at 20% 0%, rgba(239, 107, 51, 0.22), transparent 70%), #0b0b0b;
}

.t-lifelong {
  --tint: radial-gradient(70% 55% at 50% 30%, rgba(240, 80, 140, 0.3), transparent 70%), #17111c;
}

.t-lockeat {
  --tint: #5b7d8d;
}

.t-elsewhere {
  --tint: #f1ece2;
}

.t-mock {
  --tint: #100e0c;
}

/* Show the top of the phone, or the bottom: the card edge does the cropping. */
.stage--head .stage__in {
  align-items: flex-start;
  height: var(--h, 25rem);
  padding-top: 2.5rem;
}

.stage--foot .stage__in {
  align-items: flex-end;
  height: var(--h, 17rem);
  padding-bottom: 2.5rem;
}

/* A whole phone, top to bottom. Used for clips and for the playable recreations. */
.stage--whole .stage__in {
  padding: 2.25rem 0;
}

.stage--whole .phone {
  width: min(17.5rem, 68%);
}

/* On a phone the recreations get most of the width: they are there to be touched. */
@media (max-width: 39.99rem) {
  .stage--whole .phone {
    width: min(19rem, 82%);
  }
}

/* A plain crop of the interface, no phone around it. */
.stage--shot .stage__in {
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  padding: clamp(1.25rem, 5vw, 2.5rem);
}

.stage--shot img {
  position: relative;
  z-index: 1;
  width: 100%;
  border-radius: 0.5rem;
}

.stage--clip .stage__in {
  background: #0e0e0d;
  box-shadow: none;
}

.stage--clip .clip {
  position: relative;
  inset: auto;
  width: 100%;
  aspect-ratio: var(--ar);
}

/* A portrait clip keeps its own width and fades into the dark stage at the sides. */
.stage--clip .clip--portrait {
  width: min(100%, 20rem);
}

.clip--portrait video {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}

.stage--shot .sketch {
  position: relative;
  z-index: 1;
  width: min(100%, 17rem);
}

.stage--shot .hold {
  left: 0.75rem;
  bottom: 0.75rem;
}

.stage figcaption {
  margin-top: 0.8rem;
  text-align: center;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--ink-3);
  text-wrap: balance;
}

.stage figcaption b {
  margin-right: 0.4em;
  font: 500 0.6875rem/1 var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber);
}

/* ---------- clips ---------- */

.clip {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.clip video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #0c0c0b;
}

.phone > .clip {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 13.8% / 6.35%;
  aspect-ratio: 0.464;
}

.clip__btn {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: none;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.js .clip__btn {
  display: block;
}

.clip__btn:focus-visible {
  outline-color: #fff;
  outline-offset: -6px;
  border-radius: 0.625rem;
}

/* A small round play / pause control in the corner. */
.clip__pill {
  position: absolute;
  left: 0.75rem;
  bottom: 0.75rem;
  display: grid;
  place-items: center;
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 50%;
  background: rgba(28, 26, 22, 0.55);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  backdrop-filter: blur(10px) saturate(1.4);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
  transition: opacity 0.25s, transform 0.25s var(--ease);
}

.clip__pill::before {
  content: "";
  margin-left: 0.14rem;
  border-style: solid;
  border-width: 0.34rem 0 0.34rem 0.56rem;
  border-color: transparent transparent transparent #fff;
}

.clip.is-playing .clip__pill::before {
  width: 0.5rem;
  height: 0.62rem;
  margin-left: 0;
  border: solid #fff;
  border-width: 0 0.16rem;
}

.clip__btn:hover .clip__pill {
  transform: scale(1.08);
}

/* While a clip plays, the control stays out of the way until you reach for it. */
.clip.is-playing .clip__pill {
  opacity: 0;
}

.clip.is-playing .clip__btn:hover .clip__pill,
.clip.is-playing .clip__btn:focus-visible .clip__pill {
  opacity: 1;
}

.clip__speed {
  position: absolute;
  z-index: 5;
  right: 0.5rem;
  top: 0.5rem;
  display: none;
  padding: 0.75em 0.85em;
  border: 0;
  border-radius: 999px;
  background: rgba(12, 12, 11, 0.72);
  color: rgba(255, 255, 255, 0.85);
  font: 500 0.75rem/1 var(--mono);
  cursor: pointer;
}

.js .clip__speed {
  display: block;
}

.clip__speed:hover {
  color: #fff;
}

.clip__speed:focus-visible {
  outline-color: #fff;
  outline-offset: -4px;
}

/* Inside a phone the corner belongs to the status bar, so the toggle sits at the side instead. */
.phone > .clip .clip__speed {
  top: 44%;
}

/* ---------- essay endings ---------- */

.out {
  width: var(--text);
  margin: 2.5rem auto 0;
}

.out a {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.65em 1.1em;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  text-decoration: none;
  font-weight: 550;
}

.out a:hover {
  background: #33302a;
}

.out {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.out a.out__alt {
  background: none;
  color: var(--ink);
  box-shadow: inset 0 0 0 1px rgba(20, 19, 15, 0.3);
}

.out a.out__alt:hover {
  background: #fff;
}

.notes {
  width: var(--text);
  margin: 4rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hair);
  font-size: 0.875rem;
  color: var(--ink-2);
}

.notes ol {
  list-style: decimal;
  padding-left: 1.25rem;
}

.notes li + li {
  margin-top: 0.6rem;
}

.notes li:target {
  color: var(--ink);
}

.notes li > a:last-child {
  display: inline-block;
  margin: -0.4em -0.2em;
  padding: 0.4em 0.6em;
}

.next {
  width: var(--text);
  margin: 3.5rem auto 0;
}

.next a {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-inline: -0.75rem;
  padding: 0.75rem;
  border-radius: 0.75rem;
  text-decoration: none;
}

.next a:hover {
  background: #fff;
  box-shadow: 0 0 0 1px var(--hair), 0 8px 24px -12px rgba(20, 19, 15, 0.25);
}

.next img {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 0.6rem;
  box-shadow: 0 0 0 1px var(--hair);
}

.next span span {
  display: block;
  color: var(--ink-3);
}

/* ==========================================================================
   Living art: the app icons, redrawn in code
   ========================================================================== */

.art-toggle {
  position: absolute;
  z-index: 5;
  right: 0.5rem;
  bottom: 0.5rem;
  display: none;
  padding: 1em 0.9em;
  border: 0;
  border-radius: 999px;
  background: none;
  font: 500 0.75rem/1 var(--mono);
  letter-spacing: 0.06em;
  color: var(--art-ui, rgba(255, 255, 255, 0.5));
  cursor: pointer;
}

.art-toggle:hover {
  color: var(--art-ui-hover, #fff);
}

.art-toggle:focus-visible {
  outline-color: var(--art-ui-hover, #fff);
}

/* Play and pause marks are drawn with borders, which forced colours would repaint as blocks. */
@media (forced-colors: active) {
  .hold,
  .hold::before,
  .clip__pill,
  .clip__pill::before,
  .trailer__play span,
  .trailer__play span::before {
    forced-color-adjust: none;
  }
}

.art--light {
  --art-ui: rgba(20, 19, 15, 0.74);
  --art-ui-hover: #14130f;
}

.art--light .art-toggle {
  background: rgba(253, 252, 250, 0.6);
}

@media (prefers-reduced-motion: no-preference) {
  /* Smooth for jumps made from inside the page, not for arriving at one with a #fragment. */
  html:focus-within {
    scroll-behavior: smooth;
  }

  .js .art-toggle {
    display: block;
  }

  /* The first screen arrives in order: the sentence, the line under it, the sky. */
  .rise {
    animation: rise 0.9s var(--ease) both;
    animation-delay: calc(var(--i, 0) * 90ms);
  }

  .spin {
    animation: spin var(--t, 60s) linear infinite;
  }

  .spin--back {
    animation-direction: reverse;
  }

  .lift {
    animation: lift 5.5s cubic-bezier(0.45, 0, 0.25, 1) infinite;
  }

  .drift {
    animation: drift var(--t, 16s) linear infinite;
  }

  .bob {
    animation: bob 5s ease-in-out infinite;
  }

  /* A boat on the water: it rolls as well as rides up and down. */
  .rock {
    transform-box: fill-box;
    transform-origin: 50% 70%;
    animation: rock 3.4s ease-in-out infinite alternate;
  }

  .sway {
    transform-box: fill-box;
    transform-origin: center;
    animation: sway 9s ease-in-out infinite;
  }

  .breathe {
    animation: breathe 7s ease-in-out infinite;
  }

  /* A planet that keeps to one stretch of its orbit. */
  .arc {
    animation: arc 11s ease-in-out infinite alternate;
  }

  /* Paused by the visitor, or while off screen. */
  .is-paused .spin,
  .is-paused .lift,
  .is-paused .drift,
  .is-paused .bob,
  .is-paused .sway,
  .is-paused .breathe,
  .is-paused .arc,
  .is-paused .rock,
  .is-away .arc,
  .is-away .rock,
  .is-away .spin,
  .is-away .lift,
  .is-away .drift,
  .is-away .bob,
  .is-away .sway,
  .is-away .breathe {
    animation-play-state: paused;
  }

  @keyframes rise {
    from {
      opacity: 0;
      transform: translateY(0.9rem);
    }
  }

  @keyframes spin {
    to {
      transform: rotate(360deg);
    }
  }

  @keyframes lift {
    0%,
    100% {
      transform: translateY(0);
    }
    45%,
    55% {
      transform: translateY(var(--lift, -34px));
    }
  }

  @keyframes drift {
    to {
      transform: translateX(160px);
    }
  }

  @keyframes bob {
    0%,
    100% {
      transform: translateY(0);
    }
    50% {
      transform: translateY(var(--bob, 4px));
    }
  }

  @keyframes rock {
    from {
      transform: rotate(-8deg);
    }
    to {
      transform: rotate(8deg);
    }
  }

  @keyframes sway {
    0%,
    100% {
      transform: rotate(-5deg);
    }
    50% {
      transform: rotate(5deg);
    }
  }

  @keyframes arc {
    from {
      transform: rotate(-5deg);
    }
    to {
      transform: rotate(5deg);
    }
  }

  @keyframes breathe {
    0%,
    100% {
      opacity: 0.75;
    }
    50% {
      opacity: 1;
    }
  }

  @view-transition {
    navigation: auto;
  }

  /* ---- Scroll effects --------------------------------------------------
     Native scroll-driven animation, where the browser has it. Without it,
     everything is simply in place. Only transform and opacity move. */
  @supports (animation-timeline: view()) {
    /* Dawn: the sun climbs off the horizon and the pink leaves the sky
       as the first screen scrolls away. */
    .sky svg {
      animation: sunrise linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 38rem;
    }

    .dawn {
      animation: dawn linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 30rem;
    }

    /* Each chapter's little scene and its title arrive as you reach them. */
    .chapter__head > *,
    .spread figure,
    .lede > *,
    .trailer,
    .closing {
      animation: arrive linear both;
      animation-timeline: view();
      animation-range: entry 0 entry 14rem;
      animation-range: entry 0 entry min(14rem, 25svh);
    }

    /* In the plain story layout each picture arrives the same way. */
    @media (max-width: 43.99rem), (max-height: 39.99rem) {
      .shot {
        animation: arrive linear both;
        animation-timeline: view();
        animation-range: entry 0 entry 14rem;
        animation-range: entry 0 entry min(14rem, 25svh);
      }
    }

    /* Inside an essay stage, the phone glides a little against its card. */
    .stage--head .phone,
    .stage--foot .phone {
      animation: glide linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
      --p: 1.5rem;
    }

    .stage--shot img,
    .essay > .hand span {
      animation: arrive linear both;
      animation-timeline: view();
      animation-range: entry 0 entry 12rem;
      animation-range: entry 0 entry min(12rem, 25svh);
    }

    /* A hairline at the top of an essay shows how far through it you are. */
    .is-essay::before {
      content: "";
      position: fixed;
      inset: 0 0 auto;
      z-index: 30;
      height: 2px;
      background: var(--amber);
      transform-origin: 0 50%;
      animation: grow linear both;
      animation-timeline: scroll(root block);
    }
  }

  @keyframes sunrise {
    from {
      --rise: 34px;
    }
    to {
      --rise: -100px;
    }
  }

  @keyframes dawn {
    from {
      opacity: 0.85;
    }
    to {
      opacity: 0;
    }
  }

  @keyframes glide {
    from {
      transform: translateY(var(--p, 1rem));
    }
    to {
      transform: translateY(calc(var(--p, 1rem) * -1));
    }
  }

  @keyframes arrive {
    from {
      opacity: 0;
      transform: translateY(2rem);
    }
  }

  @keyframes grow {
    from {
      transform: scaleX(0);
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }
}
