/* The first screen: the app itself, its window rising out of the fold under the bar, with one line
   under it. The claim is said once, in the opening, with the logo (js/intro.js, css/intro.css), and
   never drawn with the window (his word, 1 October 2026). Sizes follow the window's height as well as
   its width. */
.hero { padding: clamp(76px, 10.5vh, 104px) 0 clamp(24px, 4vh, 48px); text-align: center; overflow: clip; }
.hero-copy { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; }
/* The claim, kept in the page for readers and search engines and not drawn: it is said in the
   opening, and over the window it was the same words twice (his words, 1 October 2026: "the words do
   not appear when the screen is showing"). */
.hero h1 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* Under the window, one line, what it is, and its quiet half, when. It stood between the claim and
   the window until 30 September 2026, when the window became the trailer and it pushed it off the
   first screen. */
.hero-side { margin-top: clamp(40px, 6vh, 72px); display: flex; flex-direction: column; align-items: center; }
.hero .lede { max-width: 640px; }
.hero .lede.when { color: var(--fg-3); }
/* The first chapter stands closer under the hero than a chapter does under a scene: with a chapter's
   whole opening space on top of the hero's own, the page showed a quarter of a screen of nothing
   between the line and the chapter's name, and the scroll to it read as too long (his word,
   1 October 2026). */
.hero + .chapter { padding-top: clamp(40px, 7vh, 80px); }
/* The line waits for the window: the opening ends on the bar alone, then the window comes up and the
   line with it (his word, 1 October 2026: "then we have our toolbar, and then this thing comes up...
   that's all. It should be clean looking"). */
.app-stage:not(.up) ~ .hero-side { opacity: 0; }

/* ── the window on its stage ── */
/* The spacing above it was taken in on 30 September 2026, when the window became the trailer: on a
   Mac browser not in full screen only its title bar reached the first screen, and the trailer played
   out of sight. */
/* The window is wide, as wide as the page less its gutters and never past 1360 px, and on most
   screens its foot runs past the fold. It was once fitted whole to the first screen, less the nav,
   and the page then read as finished there; the owner missed the wide window that slid up out of
   the fold and said there was more below (27 September 2026). It arrives on its own, below. */
.app-stage {
  position: relative; z-index: 1; width: min(1360px, 100% - var(--gutter) * 2); margin: 0 auto;
  perspective: 2200px; perspective-origin: 50% 0%;
}
/* Pinned under the bar while the hero passes, and pushed smaller from its foot by the line and the
   form coming up under it (--push, js/app.js), its top held: the page's first scroll shows there is
   more under the window. A scale and nothing else, so nothing in it is drawn through a tilt. */
.app-stage { position: sticky; top: clamp(76px, 10.5vh, 104px); transform: scale(var(--push, 1)); transform-origin: 50% 0; }
@media (prefers-reduced-motion: reduce) { .app-stage { position: relative; top: auto; transform: none; } }
/* The frame keeps the window's shape before any script runs: when app.js set its height, everything
   under the hero dropped by the window's height a third of a second after the page drew.
   It is not there when the page opens. As the opening ends it comes up from below, lying back from
   its foot and rising flat to face the visitor (.up, from app.js; his word, 30 September 2026: "come
   up from the bottom... a nice intro"). The rise is long and settles softly, and its backlight comes
   on behind it as it lands (below). Nothing inside the window moves until it is flat (.flat, from app.js), when the transform
   is dropped altogether: in a real Chrome window, the drawing laid in through the perspective left
   whole regions of the screen black. */
.app-frame, .app-film {
  position: relative; transform-origin: 50% 100%;
  opacity: 0; transform: translateY(clamp(120px, 22vh, 220px)) rotateX(18deg) scale(0.94);
  transition: transform 1.3s cubic-bezier(0.2, 0.75, 0.15, 1), opacity 0.8s cubic-bezier(0.3, 0, 0.2, 1);
}
.app-frame { aspect-ratio: 1600 / 800; }
.app-stage.up :is(.app-frame, .app-film) { opacity: 1; transform: none; }
/* No will-change on the frame: a layer of its own made a phone drawing on its graphics card show
   black squares in the window while the drawing was laid in (filmed 27 September 2026). */
/* The backlight: the window lit from behind, as a television with lights along its back throws its
   light on the wall, or a projector's picture colours the wall across the room (his words, 1 October
   2026: "an aurora effect behind the video... this purple playing almost like a backlight, so it has a
   bit of contrast"). The whole light is one colour, --amb, set by js/app.js from the paper on the
   screen, so the light on the wall follows the picture as the trailer changes it ("make it change
   based on the colour of the paper"); a dark paper throws the logo's own colour ("the darker ones
   should be our logo colour"). The patches differ only in how deep or pale they take it, which is
   what makes it read as light and not a flat wash. This is the one glow on the page, by his word; the
   rule that nothing glows (css/base.css) stands everywhere else. Each patch is a soft radial light
   rather than a blurred box, and only its place and size move, so the drift costs the graphics card
   nothing to repaint; it holds still for a visitor who asked for less motion. */
/* The colour is a registered colour so the light on the wall turns to a new paper over a second, as a
   room does when the picture changes, instead of jumping. It starts as the logo's. It is set on the
   light itself (js/app.js), not on the stage round the window: a colour every element under it
   inherits, turning a frame at a time, had the page work out the style of the whole window, its
   drawing's thousands of lines among it, every frame for the second after each paper the trailer
   picked, and the window ran at a few frames a second through them (measured 4 October 2026). */
@property --amb { syntax: "<color>"; inherits: true; initial-value: #b3a1ef; }
.app-glow {
  --deep: color-mix(in oklab, var(--amb) 70%, #1a1040); --pale: color-mix(in oklab, var(--amb) 72%, #fff);
  position: absolute; inset: -10% -6%; z-index: -1; pointer-events: none; opacity: 0; transition: opacity 1.8s ease 0.5s, --amb 1.2s ease;
}
.app-stage.up .app-glow { opacity: 1; }
.app-glow i { position: absolute; border-radius: 50%; animation: aurora var(--t, 18s) ease-in-out infinite alternate; }
.app-glow i:nth-child(1) { left: 0; top: 16%; width: 34%; height: 66%; --t: 17s; background: radial-gradient(closest-side, color-mix(in oklab, var(--amb) 55%, transparent), transparent); }
.app-glow i:nth-child(2) { right: 0; top: 10%; width: 34%; height: 72%; --t: 21s; animation-delay: -6s; background: radial-gradient(closest-side, color-mix(in oklab, var(--pale) 42%, transparent), transparent); }
.app-glow i:nth-child(3) { left: 22%; top: 0; width: 56%; height: 30%; --t: 15s; animation-delay: -3s; background: radial-gradient(closest-side, color-mix(in oklab, var(--amb) 50%, transparent), transparent); }
.app-glow i:nth-child(4) { left: 6%; top: 0; width: 40%; height: 34%; --t: 19s; animation-delay: -11s; background: radial-gradient(closest-side, color-mix(in oklab, var(--deep) 45%, transparent), transparent); }
.app-glow i:nth-child(5) { right: 8%; bottom: 0; width: 46%; height: 34%; --t: 23s; animation-delay: -8s; background: radial-gradient(closest-side, color-mix(in oklab, var(--deep) 38%, transparent), transparent); }
/* The light strip itself: a rim of the light hugging the window's edge, under the wider wash. It
   stands where the window stands inside the glow's box (the box is 12% wider and 20% taller), with
   the window's corners as they are drawn at the size the page shows it (--k, js/app.js). */
.app-glow::before {
  content: ""; position: absolute; inset: 8.33% 5.36%; border-radius: calc(34px * var(--k, 0.83));
  box-shadow: 0 0 48px 4px color-mix(in oklab, var(--amb) 32%, transparent), 0 0 140px 24px color-mix(in oklab, var(--deep) 16%, transparent);
}
@keyframes aurora {
  0% { translate: 0 0; scale: 1; }
  50% { translate: 3% -4%; scale: 1.12; }
  100% { translate: -3% 3%; scale: 0.92; }
}
@media (prefers-reduced-motion: reduce) {
  .app-glow { transition: none; }
  .app-glow i { animation: none; }
}
/* A phone shows the whole window, as a desktop does, rather than the drafting canvas cropped out of
   it: the owner could not see what the app looks like from the part (27 September 2026). The stage
   takes the screen's width less a narrow margin so the window is as large as the phone allows, and
   the film below comes up and goes down the way the live window does on a desktop. */
@media (max-width: 600px) {
  .app-stage { width: calc(100% - 12px); perspective: 900px; }
}
/* A visitor who asked for less motion is shown the window standing, at once. */
@media (prefers-reduced-motion: reduce) {
  .app-frame, .app-film { opacity: 1; transform: none; transition: none; }
}
/* A phone plays a recording of the window instead of drawing it live (js/film.js). Drawn live, a
   phone's graphics chip could not keep up with the drawing laid in line by line and the window
   flickered, on Android and in Chrome filmed at phone size (27 September 2026); a video cannot. */
.app-film { display: none; width: 100%; aspect-ratio: 2 / 1; background: transparent; }
@media (max-width: 600px) {
  .app-frame { display: none; }
  .app-film { display: block; border-radius: 6px; }
}
/* ── the email form: joining the list, at the page's end ── */
.signup label { display: block; font: 13px/1.5 var(--mono); color: var(--fg-2); margin-bottom: 12px; }
/* One pill, the button docked inside its end: an address and the one thing to do with it. As two
   pills side by side, the white button was the largest bright shape on the first screen, louder
   than the claim. */
.signup .field {
  display: flex; align-items: center; gap: 6px; max-width: 480px; margin: 0 auto; padding: 5px;
  border-radius: 999px; background: var(--card); box-shadow: inset 0 0 0 1px var(--hair-2); transition: box-shadow 0.2s;
}
.signup input {
  flex: 1; min-width: 0; height: 44px; padding: 0 8px 0 17px; border: 0; border-radius: 999px;
  background: transparent; color: var(--fg); font: 15px var(--sans); outline: none;
}
.signup .field .btn { height: 44px; padding: 0 20px; font-size: 15px; box-shadow: none; flex: none; }
.signup .field:focus-within { box-shadow: inset 0 0 0 1px var(--accent); }
/* A wrong address is said in words under the field; the rim only brightens, since red means nothing
   else on this page. */
.signup .field:has(input[aria-invalid="true"]) { box-shadow: inset 0 0 0 1px var(--fg-2); }
.signup .said { font: 13px var(--mono); color: var(--fg-2); margin: 10px 0 0; min-height: 1.5em; }
.signup .said.ok { color: var(--accent); }
.signup.done .field { display: none; }

/* ── the scroll cue ── */
/* The first screen at rest ends in a fade to the page's ground over the window's foot, and a line
   asking for the scroll with a mark that nods down: the window filled the screen and the page did not
   look as if it went on (his word, 1 October 2026). Gone at the first scroll (js/app.js). */
.scroll-cue {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; height: clamp(110px, 19vh, 180px);
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 8px; padding-bottom: clamp(14px, 2.8vh, 28px);
  background: linear-gradient(to bottom, rgba(10, 11, 13, 0), var(--ground) 80%);
  font: 500 14px/1 var(--sans); letter-spacing: 0.01em; color: var(--fg);
  opacity: 0; transition: opacity 0.5s ease; pointer-events: none;
}
.scroll-cue.on { opacity: 1; }
.scroll-cue svg { display: block; animation: cue-nod 1.7s cubic-bezier(0.45, 0, 0.55, 1) infinite; }
@keyframes cue-nod { 0%, 100% { transform: translateY(0); opacity: 0.55; } 50% { transform: translateY(5px); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .scroll-cue svg { animation: none; } }
