/* =============================================================================
   site.css — Sunshine. The shared layer.

   This is not a design system and it does not want to be one. It is the room the
   book is read in, and the seven parts every page shares: the paper, the light,
   the air, the type, the spine, the door, and the way one page becomes the next.

   Read assets/DESIGN-LANGUAGE.md before adding anything here. If a class is not
   in it, it does not exist.

   Rules this file exists to enforce:
     - the five colours are atmosphere, never specimens: no swatch, no chip, no
       name, no code is renderable from this stylesheet
     - the light is computed by daylight.js and written into custom properties;
       there is no hardcoded "cosy" filter anywhere
     - the book has three voices (Garamond, Garamond pressing, Kalam) and the
       mono stack is not one of them
   ========================================================================== */

/* --- fonts ---------------------------------------------------------------- */
/* EB Garamond — the book. Kalam — the hand. Both OFL, both local, both beside
   their licence in this folder. Nothing is fetched at runtime. */

@font-face { font-family:'EB Garamond'; src:url('../fonts/EBGaramond-Regular.woff2') format('woff2'); font-weight:400; font-style:normal; font-display:swap; }
@font-face { font-family:'EB Garamond'; src:url('../fonts/EBGaramond-Italic.woff2') format('woff2'); font-weight:400; font-style:italic; font-display:swap; }
@font-face { font-family:'EB Garamond'; src:url('../fonts/EBGaramond-Medium.woff2') format('woff2'); font-weight:500; font-style:normal; font-display:swap; }
@font-face { font-family:'EB Garamond'; src:url('../fonts/EBGaramond-SemiBold.woff2') format('woff2'); font-weight:600; font-style:normal; font-display:swap; }

@font-face { font-family:'Kalam'; src:url('../fonts/Kalam-300.woff2') format('woff2'); font-weight:300; font-style:normal; font-display:swap; }
@font-face { font-family:'Kalam'; src:url('../fonts/Kalam-400.woff2') format('woff2'); font-weight:400; font-style:normal; font-display:swap; }
@font-face { font-family:'Caveat'; src:url('../fonts/Caveat-400.woff2') format('woff2'); font-weight:400; font-style:normal; font-display:swap; }

/* --- tokens --------------------------------------------------------------- */

:root {
  /* Her five. They are declared once and never named in the interface. Two are
     structural — ink and paper. Three are light: the beam, its warm edge, and
     the sky-lit side that arrives when the sun leaves. */
  --five-ink:      #1C1815;   /* black   — the ink, and the room after dark  */
  --five-pink:     #E7B3B9;   /* pink    — the warm edge of the light       */
  --five-lavender: #AEA2D8;   /* lavender— the sky-lit side, shade, dusk    */
  --five-butter:   #F0CE72;   /* butter  — the direct beam itself           */
  --five-beige:    #E6D9C0;   /* beige   — the paper, and the room           */

  /* Job bindings. One role each, everywhere, for the whole book. */
  --ink:    var(--five-ink);
  --paper:  var(--five-beige);
  --beam:   var(--five-butter);
  --glow-warm: var(--five-pink);
  --glow-cool: var(--five-lavender);

  /* --- light written by daylight.js ---------------------------------------
     These defaults are what the room looks like before the engine answers, and
     what a page whose subject is not daylight falls back to. The engine
     overwrites --light-wall / --light-shade / --light-beam / --light-sky /
     --light-angle / --light-reach / --light-night on :root. It is never
     filtered on top of a hardcoded grade. */
  --light-wall:  var(--five-beige);
  --light-shade: #BFB2A0;
  --light-beam:  var(--five-butter);
  --light-sky:   var(--five-lavender);
  --light-angle: 118deg;
  --light-reach: .62;
  --light-night: 0;

  /* The derived surfaces a page actually paints with. */
  --room:     color-mix(in srgb, var(--light-wall) 76%, var(--paper));
  --room-far: color-mix(in srgb, var(--light-shade) 84%, var(--glow-cool));
  /* The dark of the room, used when something must be *less* lit than its
     surroundings. It is not the ink — the ink flips after dark — and it does
     not change at night, so mixing toward it always reads as "into shade". */
  --deep: #14110F;

  /* Paper. The wall colour on its own is a desaturated, tinted answer — the
     sun through October atmosphere is honestly a muddy pink. Paint on it and
     the page turns mauve, which is not what paper looks like under any light.
     So the sheet is her beige carrying the light, never the light carrying the
     paper: the hue stays the paper's and the engine only changes how much of it
     is lit and how warm the lit side runs. */
  --sheet-lit:   color-mix(in srgb, var(--light-wall) 58%, var(--five-beige));
  --sheet-shade: color-mix(in srgb, var(--light-shade) 26%, var(--five-beige));

  /* Type. Three voices. See DESIGN-LANGUAGE.md §2. */
  --serif: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --hand:  'Kalam', 'Segoe Print', 'Bradley Hand', cursive;
  --scrawl:'Caveat', 'Segoe Print', 'Bradley Hand', cursive;

  --type-page:   clamp(2.75rem, 1.4rem + 5.2vw, 5.25rem);
  --type-lead:   clamp(1.3rem, 1.1rem + .8vw, 1.6rem);
  --type-body:   clamp(1.0625rem, 1rem + .25vw, 1.1875rem);
  --type-small:  clamp(.9375rem, .9rem + .15vw, 1.0625rem);
  --type-hand:   clamp(1.125rem, 1rem + .5vw, 1.5rem);
  --type-folio:  .8125rem;

  /* Space. One rhythm, 8/16/24/32/48/72/112/160. */
  --s1: 8px;  --s2: 16px; --s3: 24px; --s4: 32px;
  --s5: 48px; --s6: 72px; --s7: 112px;

  --measure: 34rem;
  --pad: clamp(1.5rem, 4.5vw, 3.25rem);        /* narrow screens */
  --margin-l: clamp(3rem, 9vw, 9.5rem);
  --margin-t: clamp(2.5rem, 7vh, 6rem);
  --spine-w: clamp(1.5rem, 3vw, 3.5rem);

  /* Motion. Six verbs, six durations. Nothing outside them. */
  --ease-settle: cubic-bezier(.2,.7,.3,1);
  --ease-absorb: cubic-bezier(.16,1,.3,1);
  --ease-write:  cubic-bezier(.4,0,.2,1);
  --t-lift:   260ms;
  --t-settle: 900ms;
  --t-absorb: 1100ms;
  --t-write:  2000ms;
  --t-unfold: 1100ms;

  /* Hairlines are ink at low density — never grey, never blue. */
  --hair:      color-mix(in srgb, var(--ink) 34%, transparent);
  --hair-soft: color-mix(in srgb, var(--ink) 15%, transparent);

  color-scheme: light;
}

/* After dark the room inverts: the ink becomes a warm off-white and the paper
   becomes the dark. Set by Path.light() off the real solar elevation — never by
   a clock the reader can see. */
body.night {
  --ink:   #EFE6D6;
  --paper: #14110F;
  --room:     color-mix(in srgb, var(--light-wall) 55%, #14110F);
  --room-far: color-mix(in srgb, var(--light-shade) 60%, #0B0908);
  --glow-warm: color-mix(in srgb, var(--five-pink) 62%, #14110F);
  --glow-cool: color-mix(in srgb, var(--five-lavender) 40%, #14110F);
  /* after dark the sheet is not beige, but it is still paper, and still warm */
  --sheet-lit:   color-mix(in srgb, var(--light-wall) 74%, var(--five-ink));
  --sheet-shade: color-mix(in srgb, var(--light-shade) 60%, var(--five-ink));
  color-scheme: dark;
}

/* --- reset ---------------------------------------------------------------- */

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

html, body { height: 100%; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--serif);
  font-size: var(--type-body);
  line-height: 1.62;
  color: var(--ink);
  background: var(--room);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  transition: background var(--t-absorb) var(--ease-absorb),
              color var(--t-absorb) var(--ease-absorb);
}

img, svg, canvas, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; }
button { font: inherit; color: inherit; }
p, h1, h2, h3, h4, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }

::selection { background: color-mix(in srgb, var(--glow-warm) 46%, transparent); }

/* --- the room: paper, light, air ------------------------------------------
   Three layers, always in this order, always all three. A page is not finished
   until the grain, the wash and the dust are all present. */

.room {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  background:
    linear-gradient(var(--light-angle),
      color-mix(in srgb, var(--room) 86%, var(--glow-warm)) 0%,
      var(--room) 40%,
      color-mix(in srgb, var(--room-far) 72%, var(--room)) 100%);
  transition: background var(--t-absorb) var(--ease-absorb);
}

/* Paper fibre. Real fibre is fractal noise, so it is generated as fractal
   noise — two octaves of it, one fine and one slow, because one looks like a
   filter and two look like paper. 400 bytes, no image file, nothing in img/. */
.room::before {
  content: "";
  position: absolute; inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23f)'/%3E%3C/svg%3E");
  opacity: .055;
  mix-blend-mode: multiply;
}
/* the cloudiness of a real sheet — low frequency, soft light */
.room::after {
  content: "";
  position: absolute; inset: -10%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.011' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='600' height='600' filter='url(%23c)'/%3E%3C/svg%3E");
  opacity: .05;
  mix-blend-mode: soft-light;
  animation: cloud-drift 190s ease-in-out infinite alternate;
}
@keyframes cloud-drift {
  from { transform: translate3d(-1.5%, -1%, 0) scale(1.04); }
  to   { transform: translate3d(1.5%, 1%, 0) scale(1.1); }
}

/* The window's light across the room. Angle and colour are the real sun's. */
.lightwash {
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(120% 90% at 22% 8%,
      color-mix(in srgb, var(--light-beam) calc(var(--light-reach) * 34%), transparent) 0%,
      transparent 62%),
    radial-gradient(140% 110% at 84% 92%,
      color-mix(in srgb, var(--light-sky) calc(var(--light-reach) * 16%), transparent) 0%,
      transparent 58%);
  mix-blend-mode: soft-light;
  animation: wash-drift 96s ease-in-out infinite alternate;
  transition: background var(--t-absorb) var(--ease-absorb);
}
@keyframes wash-drift {
  from { transform: translate3d(-2%, -1%, 0); opacity: .84; }
  to   { transform: translate3d(2%, 1.5%, 0); opacity: 1; }
}

/* The narrow core of the wash: the patch of sun the window throws. */
.sunbeam {
  position: fixed;
  inset: -20% -30%;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(var(--light-angle),
    transparent 34%,
    color-mix(in srgb, var(--light-beam) calc(var(--light-reach) * 20%), transparent) 50%,
    transparent 66%);
  mix-blend-mode: soft-light;
  animation: beam-drift 74s ease-in-out infinite alternate;
}
@keyframes beam-drift {
  from { transform: translate3d(-3%, 0, 0) skewX(-1.2deg); }
  to   { transform: translate3d(3%, -1%, 0) skewX(1.2deg); }
}

/* Dust. A canvas, or any element with .motes inside it. Opacity follows the
   real beam: almost none at night, a glittering room at low sun. */
.motes {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: calc(.14 + var(--light-reach) * .5);
  transition: opacity var(--t-absorb) var(--ease-absorb);
}
.motes canvas { width: 100%; height: 100%; }

/* --- type ----------------------------------------------------------------- */

.t-page  { font-size: var(--type-page);  line-height: 1.02; letter-spacing: -.015em; font-weight: 400; }
.t-lead  { font-size: var(--type-lead);  line-height: 1.45; font-weight: 400; }
.t-body  { font-size: var(--type-body);  line-height: 1.62; font-weight: 400; }
.t-small { font-size: var(--type-small); line-height: 1.5;  font-weight: 400; }
.t-hand  { font-family: var(--hand); font-size: var(--type-hand); line-height: 1.36; font-weight: 300; }
.t-folio { font-family: var(--serif); font-size: var(--type-folio); letter-spacing: .08em; }

/* The hand. Always his, always outside the measure, always oblique, always
   more upright than it wants to be so it looks written rather than printed. */
.hand {
  font-family: var(--hand);
  font-size: var(--type-hand);
  line-height: 1.36;
  font-weight: 300;
  max-width: 17rem;
  transform: rotate(-2.4deg);
  transform-origin: left center;
}
.hand + .hand, .hand:nth-of-type(even) { transform: rotate(1.8deg); }
.hand.right { margin-left: auto; transform-origin: right center; transform: rotate(2.2deg); }

/* A hurried scrawl. Same hand, faster. Never used for more than three words. */
.scrawl {
  font-family: var(--scrawl);
  font-size: calc(var(--type-hand) * .95);
  line-height: 1.2;
  transform: rotate(-3deg);
}

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

.page {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  padding: var(--margin-t) 0 6rem;
  padding-left: var(--margin-l);
  padding-right: calc(var(--spine-w) + var(--s6));
}
.page > * { max-width: var(--measure); }
.page > .wide { max-width: none; }

.page-title {
  margin: 0 0 var(--s5);
  max-width: 16ch;
  animation: settle var(--t-settle) var(--ease-settle) both;
}
.lead {
  margin: 0 0 var(--s6);
  color: color-mix(in srgb, var(--ink) 86%, transparent);
  animation: absorb var(--t-absorb) var(--ease-absorb) 160ms both;
}
.passage { margin: 0 0 var(--s3); max-width: var(--measure); }
.passage + .passage { margin-top: var(--s3); }

/* stagger — but never past 600ms, so nobody is made to wait for a page */
.passage, .lead, .discovery, .blank { animation: absorb var(--t-absorb) var(--ease-absorb) both; }
.passage:nth-of-type(1) { animation-delay: 200ms; }
.passage:nth-of-type(2) { animation-delay: 260ms; }
.passage:nth-of-type(3) { animation-delay: 320ms; }
.passage:nth-of-type(4) { animation-delay: 380ms; }
.passage:nth-of-type(n+5) { animation-delay: 420ms; }

@keyframes settle {
  from { opacity: 0; transform: translate3d(0, 6px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes absorb {
  from { opacity: 0; filter: blur(3px); transform: translate3d(0, 3px, 0); }
  to   { opacity: 1; filter: blur(0);    transform: none; }
}
@keyframes write {
  from { clip-path: inset(0 100% 0 0); opacity: .2; }
  to   { clip-path: inset(0 0 0 0);    opacity: 1; }
}
.hand.writing { animation: write var(--t-write) var(--ease-write) both; }

/* --- the blank ------------------------------------------------------------ */
/* The blank lines are his. They are never filled, never labelled, never
   framed. An empty region under a light is the whole idea. */
.blank { margin: var(--s5) 0; }
.blank .rule-line {
  height: 0;
  border-bottom: 1px solid var(--hair-soft);
}
.blank .rule-line + .rule-line { margin-top: var(--s4); }

/* --- the discovery -------------------------------------------------------- */
/* One per page. Unannounced. Found by looking or by coming near it. */
.discovery {
  position: relative;
  opacity: .34;
  transition: opacity var(--t-absorb) var(--ease-absorb),
              filter var(--t-absorb) var(--ease-absorb);
}
.discovery.found {
  opacity: 1;
  filter: drop-shadow(0 0 14px color-mix(in srgb, var(--glow-warm) 55%, transparent));
}
.discovery::after {
  content: "";
  position: absolute; inset: -18px -22px;
  border-radius: 50%;
}

/* --- photo slots ---------------------------------------------------------- */
/* Structural only. Empty and invisible — no border, no dashed outline, no
   caption, no "coming soon". A page with a slot must be complete without it. */
.photo-slot { display: block; width: 100%; aspect-ratio: 4 / 5; }
.photo { width: 100%; height: auto; object-fit: cover; }

/* =============================================================================
   THE SPINE — chapter navigation.

   Not a list. A book has a spine and it shows you how far you have come without
   enumerating anything. Each chapter is a notch cut into one vertical rule.

   Built chapters occupy the top 78% of the rule. The remaining 22% is the tail:
   thinner, fading out, with an open notch at the end. That tail is not
   decoration — it is the book visibly continuing past what exists today, and it
   is why seven chapters read as a beginning rather than as a finished set.
   ========================================================================== */

.spine {
  position: fixed;
  top: var(--margin-t);
  right: 0;
  bottom: var(--s6);
  width: var(--spine-w);
  z-index: 30;
  display: block;
}
.spine:focus { outline: none; }

.spine-rule {
  position: absolute;
  top: 0; bottom: 0;
  right: calc(var(--spine-w) * .42);
  width: 1px;
  background: var(--hair);
}
.spine-tail {
  position: absolute;
  right: calc(var(--spine-w) * .42);
  top: var(--tail-at, 78%);
  height: calc((100% - var(--tail-at, 78%)) * -1 + 22%);
  width: 1px;
  transform-origin: top center;
  background: linear-gradient(to bottom, var(--hair), transparent 88%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 92%);
  mask-image: linear-gradient(to bottom, #000 0%, transparent 92%);
}
/* the open notch at the far end — where the book is going */
.spine-tail::after {
  content: "";
  position: absolute;
  bottom: 0; right: -2px;
  width: 5px; height: 1px;
  background: var(--hair);
}

.spine-notch {
  position: absolute;
  top: var(--at, 0%);
  right: 0;
  width: 100%;
  height: 14px;
  margin-top: -7px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  text-decoration: none;
  color: inherit;
}
.spine-notch::before {
  content: "";
  display: block;
  height: 1px;
  width: var(--notch, 9px);
  margin-right: calc(var(--spine-w) * .42 - .5px);
  background: var(--hair);
  transition: width var(--t-lift) var(--ease-settle),
              background var(--t-lift) var(--ease-settle),
              box-shadow var(--t-lift) var(--ease-settle);
}

/* where you have been: shorter, warm, nothing else */
.spine-notch.is-done::before { background: color-mix(in srgb, var(--ink) 46%, var(--glow-warm)); --notch: 8px; }
/* where you are: long, bright, and named */
.spine-notch.is-here::before {
  width: 20px;
  background: var(--ink);
  box-shadow: 0 0 12px color-mix(in srgb, var(--glow-warm) 60%, transparent);
  --notch: 20px;
}
/* where you have not been */
.spine-notch.is-future::before { opacity: .3; --notch: 7px; }

.spine-notch.is-here .spine-name,
.spine-notch:hover .spine-name,
.spine-notch:focus-visible .spine-name {
  opacity: 1;
  transform: none;
}
.spine-name {
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--type-small);
  line-height: 1;
  max-width: 15rem;
  margin-right: 26px;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(6px);
  transition: opacity var(--t-lift) var(--ease-settle), transform var(--t-lift) var(--ease-settle);
  pointer-events: none;
}

.spine-notch:hover::before,
.spine-notch:focus-visible::before { width: 20px; background: var(--ink); }

/* density — the same component holds 7 and holds 100 */
.spine[data-density="close"] .spine-notch::before { --notch: 6px; width: 6px; }
.spine[data-density="close"] .spine-notch.is-here::before { --notch: 18px; width: 18px; }
.spine[data-density="close"] .spine-notch.is-done::before { --notch: 5px; }
.spine[data-density="dense"] .spine-notch { height: 8px; margin-top: -4px; }
.spine[data-density="dense"] .spine-notch::before { --notch: 4px; width: 4px; }
.spine[data-density="dense"] .spine-notch.is-here::before { --notch: 16px; width: 16px; }
.spine[data-density="dense"] .spine-notch.is-done::before { --notch: 4px; }

.spine-notch:focus-visible { outline: none; }
.spine-notch:focus-visible::before {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 22%, transparent);
}

/* On the cover the spine is what the light reveals, so until the shaft has
   passed a notch it is barely there. Scoped by an attribute the cover sets, so
   this never dims the spine on a page she is actually reading. */
.spine[data-lit="shaft"] .spine-notch { opacity: .18; transition: opacity var(--t-absorb) var(--ease-absorb); }
.spine[data-lit="shaft"] .spine-notch.is-lit { opacity: 1; }
.spine[data-lit="shaft"] .spine-name { opacity: 0; }

/* --- the masthead --------------------------------------------------------- */
/* Kept because pages 01–07 carry it. It is no longer chrome: it sits in the
   page instead of over it, so it scrolls away the way a running head does. */
.masthead {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 20;
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  padding: var(--s2) 0 0 var(--margin-l);
  pointer-events: none;
}
.masthead > * { pointer-events: auto; }
.masthead .name {
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--type-small);
  letter-spacing: .02em;
  text-decoration: none;
  white-space: nowrap;
}
.masthead .thread { flex: 1 1 auto; min-width: 2rem; }
.masthead .spacer { flex: 0 0 var(--s4); }
.masthead .to-index { text-decoration: none; }
.masthead .to-index .label {
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--type-small);
  letter-spacing: .01em;
  opacity: .6;
}
.masthead .to-index:hover .label { text-decoration: underline; text-underline-offset: 5px; }

/* The progress thread. The reader's place in the book, as a lit rule with one
   tick per chapter — same data as the spine, at masthead scale. Past the last
   chapter built today it keeps going and fades, because the book goes on. */
.thread-line {
  position: relative;
  height: 1px;
  width: 100%;
  background: var(--hair-soft);
}
/* the part of the book that is not written yet: the rule thins out and goes */
.thread-tail {
  position: absolute;
  top: -1px;
  bottom: -1px;
  right: 0;
  width: 12%;
  background: linear-gradient(to right, var(--hair-soft), transparent);
}
.thread-tail::after {
  content: "";
  position: absolute;
  right: 0; top: -3px;
  width: 1px; height: 7px;
  background: var(--hair);
}
.thread-tick {
  position: absolute;
  top: 50%;
  width: 1px;
  height: 7px;
  transform: translate(-50%, -50%);
  background: var(--hair);
  text-decoration: none;
}
.thread-tick.done { background: color-mix(in srgb, var(--ink) 50%, var(--glow-warm)); height: 9px; }
.thread-tick.here { background: var(--ink); height: 13px; width: 2px; }
.thread-tick.future { opacity: .34; }
.thread-tick:hover { background: var(--ink); height: 13px; }

/* --- the stage ------------------------------------------------------------

   `--measure` is the width a column of readable text should occupy. It is the
   right width for a title, a lead and a passage and the wrong width for the one
   thing a chapter is *about*. Every chapter puts its object in the flow of the
   page, so the rule that hands `--measure` to every direct child
   (`.page > *`, and the same line spelled `.sheet > *` on the pages whose main
   element is a sheet) was quietly capping the object as well: at half the wall,
   hard against the left margin, with the rest of the room dead.

   `.stage` is the way out, and it is also a statement about what a stage is. A
   stage takes the whole wall — everything between this page's own left margin
   and the spine — because a window, a shelf, a sheet and a tide are not
   measured in characters. Its children are not measured either: the ruled lines
   on a wet sheet and the pages of a spread are set by the page that owns them,
   not by this rule.

   What a stage must not become is a panel. Two things put a rectangle on a wall
   and make it read as a panel rather than as part of the room: an edge drawn
   around it, and light that stops at its sides. The edge is each chapter's own
   to remove — only the chapter knows whether its object is paper, glass or
   water — but the light is not, and every stage from 12 on carries the beam at
   `--light-angle` right across it, because the object is standing in this room
   and the light in this room does not stop where the object begins.

   `.wide` is the older, blunter escape and still works; `.stage` is the same
   idea under the name the chapters actually use, and it says what the extra
   width is *for*. */
.stage,
.wide { max-width: none; }
.stage > * { max-width: none; }

/* --- the foot ------------------------------------------------------------- */
.foot {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s2);
  padding: var(--s2) 0 var(--s5) var(--margin-l);
  max-width: none;
}
.foot a { text-decoration: none; }
.foot a:hover { text-decoration: underline; text-underline-offset: 5px; }

/* --- rules and small marks ------------------------------------------------ */
.rule { border: 0; border-top: 1px solid var(--hair-soft); margin: var(--s4) 0; }

/* --- the gate: the trigger ------------------------------------------------ */
/* Unchanged in mechanic — Path.mountGate still builds this exact DOM — but it
   is a door in a wall, never a button. No radius, no fill of its own, no icon
   unless the page supplies one. */
.gate {
  display: block;
  width: 100%;
  max-width: var(--measure);
  border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; text-align: inherit;
  background: none; cursor: pointer;
  text-decoration: none;
}
.gate[aria-disabled="true"] { cursor: default; pointer-events: none; }
.gate .door {
  position: relative;
  display: block;
  overflow: hidden;
  border-top: 1px solid var(--hair);
  transition: border-color var(--t-lift) var(--ease-settle);
}
.gate[aria-disabled="true"] .door { opacity: .5; }
.gate .door-inner {
  position: relative;
  z-index: 2;
  padding: var(--s3) 2px var(--s4);
}
.gate .door-label, .gate .door-inner .label { display: block; }
.gate .door-label .t, .gate .door-inner .t {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.1rem);
  line-height: 1.1;
  display: block;
}
/* the where-line is the page speaking about its own verb, in his hand */
.gate .door-where, .gate .band-where, .gate .door-inner .band-where {
  display: block;
  margin-top: var(--s1);
  font-family: var(--hand);
  font-weight: 300;
  font-size: var(--type-small);
  line-height: 1.3;
  opacity: .72;
  transform: rotate(-.8deg);
  transform-origin: left center;
}
.gate .door-icon { display: none; }
.gate[aria-disabled="false"] .door { border-top-color: var(--ink); }
/* the open door fills with the light that is actually on this page. The glow
   lives on ::after of the inner block, because ::before of the door belongs to
   the pages that draw a lintel on it. */
.gate[aria-disabled="false"] .door-inner::after {
  content: "";
  position: absolute;
  inset: -1px 0 0 0;
  z-index: -1;
  background: var(--light-beam);
  opacity: calc(var(--light-reach) * .22);
  animation: door-breathe 9s ease-in-out infinite alternate;
  pointer-events: none;
}
@keyframes door-breathe {
  from { opacity: calc(var(--light-reach) * .16); }
  to   { opacity: calc(var(--light-reach) * .3); }
}
.gate[aria-disabled="false"] .door-inner > * {
  animation: absorb var(--t-absorb) var(--ease-absorb) both;
}

/* --- stickers on the wall -------------------------------------------------
   A sticker is a printed thing a couple of millimetres proud of the plaster,
   so its shadow is a short offset in the direction the sun throws it. Both the
   offset and the softness come from wall.js and the computed elevation. */
.sticker {
  position: absolute;
  z-index: 6;
  line-height: 0;
  pointer-events: none;
  filter: drop-shadow(
    var(--sticker-dx, 2px) var(--sticker-dy, 3px)
    var(--sticker-blur, 4px)
    color-mix(in srgb, var(--ink) calc(var(--sticker-a, .22) * 100%), transparent)
  );
}
.sticker img { filter: brightness(var(--sticker-lum, 1)) saturate(var(--sticker-sat, 1)); }

/* --- shared canvas host --------------------------------------------------- */
/* Pages 01, 03, 04 and 06 paint the wall on a canvas. They each size their own;
   this is the floor, so a page that forgets still gets a full-bleed wall
   instead of a 300x150 default box. */
canvas.wall, .wall-canvas {
  display: block;
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  z-index: -4;
}

/* --- focus ---------------------------------------------------------------- */
/* Visible, never removed, never a bright UI blue. */
:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 5px;
}

/* =============================================================================
   PAGE TRANSITIONS

   One page becoming the next. A sheet of paper lifts off the one below it and
   settles into place. Never a slide, never a fade-to-black, never a wipe.

   Where the browser supports cross-document view transitions the browser does
   this natively and none of the JS runs. Where it does not, Path.transition()
   runs the same motion by hand.
   ========================================================================== */

@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: page-lift 420ms var(--ease-settle) both;
}
::view-transition-new(root) {
  animation: page-set 520ms var(--ease-absorb) both;
}
@keyframes page-lift {
  to { opacity: 0; transform: scale(.988) translateY(-8px); filter: blur(4px); }
}
@keyframes page-set {
  from { opacity: 0; transform: scale(1.006) translateY(10px); filter: blur(4px); }
}

/* the manual path, for browsers without view transitions */
html.is-leaving .page,
html.is-leaving .room,
html.is-leaving .lightwash {
  opacity: 0;
  filter: blur(4px);
  transition: opacity 300ms var(--ease-settle), filter 300ms var(--ease-settle);
}
html.is-leaving .page { transform: scale(.988) translateY(-8px); }

/* --- narrow screens ------------------------------------------------------- */

@media (max-width: 720px) {
  :root {
    --margin-l: clamp(1.5rem, 7vw, 2.5rem);
    --spine-w: 1.5rem;
    --measure: 100%;
  }
  .page { padding-right: calc(var(--spine-w) + var(--s2)); }
  .stage { padding-left: var(--margin-l); }
  /* the hand stops living in a margin it no longer has — it moves inline,
     still oblique, still unmistakably his */
  .hand, .hand.right {
    max-width: 100%;
    margin-left: 0;
    transform: rotate(-1.4deg);
    transform-origin: left center;
  }
  .spine-name { display: none; }
  .masthead { padding-left: var(--margin-l); }
  .foot { padding-left: var(--margin-l); }
}

/* --- reduced motion ------------------------------------------------------- */
/* Not a preference to be acknowledged. The page must be complete without it. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  .room::after, .lightwash, .sunbeam { animation: none !important; }
  .discovery { opacity: 1; }
  .motes { opacity: .3; }
}
/* ===========================================================================
   THE WAY ON — the card that comes up when a page is finished.

   Not a dialog. There is no title bar, no close box in the corner, no pair of
   buttons. It is a loose leaf laid over the page she has just finished, with
   the next chapter written on it and one quiet way to put it down again. She
   is never moved anywhere by it: nothing here takes her anywhere unless she
   asks, and putting it down puts her back exactly where she was.
   =========================================================================== */
.wayon {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s5) var(--s4);
  background: color-mix(in srgb, var(--ink) 34%, transparent);
  opacity: 0;
  transition: opacity 420ms var(--ease-settle);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  /* A card with a letter on it can be taller than the window it rises into.
     Without this the foot of it — the way onward's quiet word, and the way to
     put it down — is simply below the fold of a fixed layer and no finger can
     ever reach it. The veil scrolls; the page underneath does not move. */
  overflow-y: auto;
}
.wayon.is-up { opacity: 1; }
.wayon.is-going { opacity: 0; }

.wayon-leaf {
  position: relative;
  /* auto rather than align-items: center — centring an item taller than its
     container cuts the top off it and leaves it unscrollable back into view.
     Auto margins centre it when there is room and drop it to the top when
     there is not, so the whole leaf is always reachable. */
  margin: auto;
  width: 100%;
  max-width: 26rem;
  padding: var(--s6) var(--s5) var(--s5);
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--hair);
  box-shadow: 0 18px 44px color-mix(in srgb, var(--ink) 26%, transparent);
  text-align: center;
  transform: translateY(14px) rotate(-.4deg);
  opacity: 0;
  transition: transform 520ms var(--ease-settle), opacity 420ms var(--ease-settle);
}
.wayon.is-up .wayon-leaf { transform: translateY(0) rotate(-.4deg); opacity: 1; }

/* The chapter that comes next, in the book's own serif. */
.wayon-name {
  display: block;
  font-family: var(--serif);
  font-size: clamp(1.55rem, 1.2rem + 1.4vw, 2rem);
  line-height: 1.12;
}

/* What the page said about itself as it finished, in his hand. */
.wayon-where {
  display: block;
  margin-top: var(--s2);
  font-family: var(--hand);
  font-size: 1.02rem;
  line-height: 1.5;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
}
.wayon-where:empty { display: none; }

/* The second page, when it lands on the meaning line itself. Paragraphs set
   with the same margins the letter's own paragraphs have, so both pages of
   one leaf are written the same way — same hand, same centring, only the
   words have changed. */
.wayon-where .wayon-p { margin: 0 0 var(--s3); }
.wayon-where .wayon-p:last-child { margin-bottom: 0; }

/* A page that has something longer to say gets a wider leaf and a proper
   column of prose — this is a passage, not a dialog box with a sentence in it. */
.wayon-leaf:has(.wayon-letter) {
  max-width: 38rem;
  padding: var(--s6) var(--s6) var(--s5);
  text-align: left;
}
.wayon-where.wayon-long {
  margin-top: var(--s4);
  font-family: var(--serif);
  font-size: 1.06rem;
  line-height: 1.62;
  color: var(--ink);
}
.wayon-where.wayon-long .wayon-p { margin: 0 0 var(--s3); }
.wayon-where.wayon-long .wayon-p:last-child { margin-bottom: 0; }

/* The one quiet way into the rest of it. Not a button in any way that matters:
   a word, in his hand, with a hairline under it. It says there is more of it
   without saying "read more", and it is the only thing on the card to press. */
.wayon-more {
  display: block;
  margin: var(--s4) auto 0;
  font-family: var(--hand);
  font-size: 1.05rem;
  color: var(--ink);
  background: none;
  padding: 0;
  cursor: pointer;
  border: 0;
  border-bottom: 1px solid var(--hair);
}
.wayon-more:hover { border-bottom-color: var(--ink); }
.wayon-more:focus { outline: none; }
.wayon-more:focus-visible { border-bottom-color: var(--ink); }
.wayon-more[hidden] { display: none; }

/* The letter, once she has asked for it. Prose on the same leaf, in the book's
   own serif, left-aligned the moment it opens — a column of paragraphs centred
   on a card reads as a notification, not as writing. */
.wayon-letter {
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px solid var(--hair);
  text-align: left;
  font-family: var(--serif);
  font-size: 1.06rem;
  line-height: 1.62;
  color: var(--ink);
  animation: absorb 620ms var(--ease-settle) both;
}
.wayon-letter .wayon-p { margin: 0 0 var(--s3); }
.wayon-letter .wayon-p:last-child { margin-bottom: 0; }

/* The letter's own paragraphs are set in his hand on the page's card, so the
   whole note reads as written rather than typeset. The header above it stays
   the book's serif, because the chapter's name is not his. */
.wayon-leaf:has(.wayon-letter) .wayon-name { font-family: var(--x) }

/* The way on, and the way not to take it. Both are the same kind of thing: no
   filled button, no border-radius, nothing that reads as software. */
.wayon-go,
.wayon-stay {
  display: block;
  margin-top: var(--s4);
  font-family: var(--serif);
  font-size: 1.1rem;
  color: var(--ink);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.wayon-go { border-bottom-color: var(--hair); }
.wayon-go:hover { border-bottom-color: var(--ink); }
.wayon-stay {
  margin-top: var(--s2);
  font-family: var(--hand);
  font-size: .95rem;
  color: color-mix(in srgb, var(--ink) 62%, transparent);
  border-bottom: 1px solid transparent;
}
.wayon-stay:hover { color: var(--ink); border-bottom-color: var(--hair); }
/* No focus ring. A dialog that draws a box around its own button is a dialog
   from a product; the underline is the whole affordance. */
.wayon-go:focus, .wayon-stay:focus { outline: none; }
.wayon-go:focus-visible { border-bottom-color: var(--ink); }
.wayon-stay:focus-visible { color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .wayon, .wayon-leaf { transition: none; }
  .wayon-leaf { transform: none; }
  .wayon.is-up .wayon-leaf { transform: none; }
}
