/* Gravitational Waves
   One typeface, ink on paper. Meaning is carried by position and spacing. */

/* Each face in two parts: the common letters first, the rest only when a word needs them.
   tools/newsreader.py splits the original without losing any characters or variable axes.
   Each page preloads only the upright common part; unicode-range asks for the rest. */

@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader/Newsreader-Site.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6-02C7, U+02D8-02DD, U+2010, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2044, U+2052, U+20AC, U+2122, U+2212, U+FB01-FB02;
}

@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader/Newsreader-Site-Ext.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-0130, U+0134-0148, U+014A-0151, U+0154-017E, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01C4-01CC, U+01E6-01E7, U+01EA-01EB, U+01FA-021B, U+022A-022D, U+0230-0233, U+0237, U+0259, U+02B9-02BA, U+02BC, U+0300-0304, U+0306-030C, U+030F, U+0311-0312, U+031B, U+0323-0324, U+0326-0328, U+032E, U+0331, U+0335, U+1E80-1E85, U+1E9E, U+1EA0-1EF9, U+2070, U+2074, U+20A1, U+20A3-20A4, U+20A6-20A7, U+20A9, U+20AB, U+20AD, U+20B1-20B2, U+20B5, U+20B9-20BA, U+20BC-20BD, U+2116, U+2215, U+2219, U+2248, U+2260, U+2264-2265, U+27E8-27E9, U+FB00, U+FB03-FB04;
}

@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader/Newsreader-Italic-Site.woff2") format("woff2");
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6-02C7, U+02D8-02DD, U+2010, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2044, U+2052, U+20AC, U+2122, U+2212, U+FB01-FB02;
}

@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader/Newsreader-Italic-Site-Ext.woff2") format("woff2");
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0100-0130, U+0134-0148, U+014A-0151, U+0154-017E, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01C4-01CC, U+01E6-01E7, U+01EA-01EB, U+01FA-021B, U+022A-022D, U+0230-0233, U+0237, U+0259, U+02B9-02BA, U+02BC, U+0300-0304, U+0306-030C, U+030F, U+0311-0312, U+031B, U+0323-0324, U+0326-0328, U+032E, U+0331, U+0335, U+1E80-1E85, U+1E9E, U+1EA0-1EF9, U+2070, U+2074, U+20A1, U+20A3-20A4, U+20A6-20A7, U+20A9, U+20AB, U+20AD, U+20B1-20B2, U+20B5, U+20B9-20BA, U+20BC-20BD, U+2116, U+2215, U+2219, U+2248, U+2260, U+2264-2265, U+27E8-27E9, U+FB00, U+FB03-FB04;
}

/* The Chinese beside the English: Noto Serif TC, a Ming face, cut down to the characters the site uses
   (tools/han.py makes it; run it again when any Chinese changes). Before, it was whatever the device had,
   which on Android is a sans. A character not in the cut still comes from the device's own face */
@font-face {
  font-family: "GW Han";
  src: url("../fonts/NotoSerifTC/NotoSerifTC-Site.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* One character of it asked for before Newsreader: the dash. Newsreader's is narrower and sits lower than
   a Chinese line wants; this one is a full square wide and at mid-height, so two of them join into the long
   dash Chinese uses. Where the cut cannot be had, the device's own Song face lends it, as it used to.
   The tone marks of zhuyin come the same way: Newsreader has two of the five, drawn for Latin, and a
   syllable would otherwise take its tone from another face than its own */
@font-face {
  font-family: "GW Han Dash";
  src: url("../fonts/NotoSerifTC/NotoSerifTC-Site.woff2") format("woff2"),
       local("Songti TC Regular"), local("STSongti-TC-Regular"), local("Noto Serif TC"), local("NotoSerifTC-Regular"),
       local("Noto Serif CJK TC"), local("NotoSerifCJKtc-Regular"), local("Source Han Serif TC"), local("PMingLiU"),
       local("Songti SC Regular"), local("STSongti-SC-Regular");
  unicode-range: U+2014, U+02C7, U+02C9-02CB, U+02D9;
}

/* Japanese and Korean have their own shapes for the words beside the English. The whole faces stay
   in design; tools/han.py leaves only the characters that are read on these pages. */
@font-face {
  font-family: "GW Mincho";
  src: url("../fonts/NotoSerifJP/NotoSerifJP-Site.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "GW Mincho Dash";
  src: url("../fonts/NotoSerifJP/NotoSerifJP-Site.woff2") format("woff2"),
       local("Hiragino Mincho ProN"), local("Yu Mincho"), local("Noto Serif CJK JP");
  font-weight: 400;
  font-style: normal;
  unicode-range: U+2014, U+2026;
}
@font-face {
  font-family: "GW Myeongjo";
  src: url("../fonts/NotoSerifKR/NotoSerifKR-Site.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "GW Myeongjo Dash";
  src: url("../fonts/NotoSerifKR/NotoSerifKR-Site.woff2") format("woff2"),
       local("AppleMyungjo"), local("Noto Serif CJK KR"), local("Batang");
  font-weight: 400;
  font-style: normal;
  unicode-range: U+2014, U+2026;
}

:root {
  --paper: #f6f4ee;
  --ink: #1c1b19;
  --faint: #9a948a;
  /* the small hand where it can be pressed: a shade nearer the ink than the faint words beside it,
     so it can be read on a phone in daylight (about 5:1 on the paper, against 2.7:1) and told apart */
  --press: color-mix(in srgb, var(--faint), var(--ink) 35%);

  --serif: "Newsreader", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  /* what others write may not be in Latin letters: after Newsreader, whatever serif the device has for them */
  --serif-wide: "Newsreader", "Songti TC", "Noto Serif TC", "Noto Serif CJK TC", "Source Han Serif TC", "PMingLiU", "Songti SC", "Noto Serif SC", "Iowan Old Style", Georgia, serif;
  --fs: clamp(1.125rem, 0.95rem + 0.75vw, 1.625rem);
  --edge: clamp(1.25rem, 3vw, 2.5rem);            /* inset of the frame */
  --col: clamp(1.5rem, 22vw - 3.5rem, 20rem);     /* left edge of the poem */
  --vh: 1vh;

  /* a phone's screen has edges of its own: a notch at one end, a bar at the foot.
     The frame keeps clear of them; left and right give way by the same amount, so the page stays centred */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --edge-t: calc(var(--edge) + var(--safe-t));
  --edge-b: max(var(--edge), calc(var(--safe-b) + 0.25rem));
  --edge-x: max(var(--edge), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
  --fade: min(5.5rem, calc(var(--vh) * 16));      /* how far in from top and bottom the page fades */

  /* the one way anything appears on this page: it comes into focus */
  --focus: 1.3s cubic-bezier(0.33, 0, 0.2, 1);
  --blur: 0.4em;
  /* and the one way anything leaves: out of focus, more quickly than it came */
  --leave: 0.4s cubic-bezier(0.33, 0, 0.2, 1);

  /* the words set beside the English (see near the end). Latin letters and figures inside it
     stay Newsreader's; the characters are the site's own Ming face, and the device's after that */
  --han: "GW Han Dash", "Newsreader", "GW Han", var(--serif-wide);
  --mincho: "GW Mincho Dash", "Newsreader", "GW Mincho", "Hiragino Mincho ProN", "Yu Mincho", "Noto Serif CJK JP", serif;
  --myeongjo: "GW Myeongjo Dash", "Newsreader", "GW Myeongjo", "AppleMyungjo", "Noto Serif CJK KR", "Batang", serif;
  --beside-ink: color-mix(in srgb, var(--ink) 58%, var(--paper));
  --after: 0.45s;                                 /* how long it trails its English */
}

@supports (height: 1svh) {
  :root { --vh: 1svh; }
}

/* on a phone there is no margin to stand in: the text takes the frame's own left edge */
@media (max-width: 30rem) {
  :root { --col: var(--edge-x); }
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #0f0f10;
    --ink: #e8e4dc;
    --faint: #6d6962;
  }
}

/* Going from one page to another, the paper stays and the ink changes. What is on the page goes out of
   focus behind a sheet of the same paper (body::before, below; turn.js says when), the next page is asked
   for, and what is on it comes into focus the way it always does. The browser's own fade between the two
   pages then has two blank sheets to join, and is only seen where the paper itself changes colour */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) { animation-duration: 0.6s; }

html {
  background: var(--paper);
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;       /* a touch is answered the way a hover is, in ink */
}


body {
  margin: 0;
  color: var(--ink);
  font: 400 var(--fs) / 1.75 var(--serif);
  font-optical-sizing: auto;
  font-variant-numeric: lining-nums;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: color-mix(in srgb, var(--ink) 14%, transparent); }

/* paper grain: the same speckle over everything, ink included, as on a printed sheet */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0.13;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.3 0 0 0 -0.52'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark) {
  body::after { filter: invert(1); opacity: 0.09; }
}

/* The edges of an iPhone's screen. Safari keeps a band above the page (the status bar) and one below (the
   address bar) and draws in them itself. It will draw there either what scrolls with the page, or one flat
   colour, never anything fixed to the screen: not the grain, and not the paper the lines fade into at the
   top and bottom, so a band that shows the page shows the lines going on under the bar after the paper has
   hidden them, and a band in flat colour has no grain. (Tried in the iOS simulator, Safari 26: fixed, sticky,
   fixed and stretched past the edges, the page scrolling inside a fixed sheet; the page's own ground with
   the grain on it does show, and so do the lines.)
   So the bands are flat, and the page meets them without a seam. Where an element with a colour of its own
   lies along an edge, Safari fills the band with that colour: two strips (beside.js lays them down, before
   anything is drawn) in the paper as the grain leaves it on average (--bar, the same as the theme-color in
   each page's head). Then over the last --feather at each edge the grain thins out and the paper under it
   turns to that same colour, so the tone holds and only the speckle goes, gradually, before the bar begins.
   Two things would put the paper's own lighter colour into the bands for a moment: the page's ground, which
   Safari falls back on before the strips are there; and the sheet a page goes out of focus behind
   (body::before), which is paper-coloured and covers the screen while a page is left or the index is open.
   So here the ground is --bar and the paper is laid over it, and the sheet keeps clear of the edges.
   Only on WebKit for iOS (-webkit-touch-callout is its alone); everywhere else nothing here applies.
   Measured there: a strip 6px tall is taken, 1px is not, and nor is anything on html::before; and Safari
   takes the colour once, when the strip appears, so new ones are laid when the light changes */
:root {
  --bar: #f1efe9;
  --feather: 2rem;
}

@media (prefers-color-scheme: dark) {
  :root { --bar: #121213; }
}

.band { display: none; }

@supports (-webkit-touch-callout: none) {
  html:not(.night) .band {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    height: 6px;
    z-index: 4;
    pointer-events: none;
    background-color: var(--bar);
  }

  .band--top { top: 0; }
  .band--bottom { bottom: 0; }

  html:not(.night) body::after {
    -webkit-mask-image: linear-gradient(transparent, #000 var(--feather), #000 calc(100% - var(--feather)), transparent);
            mask-image: linear-gradient(transparent, #000 var(--feather), #000 calc(100% - var(--feather)), transparent);
  }

  /* the ground, in the bars' colour; the paper is laid over it, under everything, turning to that colour at
     the edges (on the pages with nothing over their edges; on the others the frame's paper does the same,
     see "Frame"). An image, not a colour, so that Safari does not take it for its bars */
  html:not(.night) { background-color: var(--bar); }

  html:not(.night)::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image: linear-gradient(var(--bar), var(--paper) var(--feather), var(--paper) calc(100% - var(--feather)), var(--bar));
  }

  /* the sheet stops short of the strips, and turns to their colour at its own edges like the rest */
  html:not(.night) body::before {
    inset: 6px 0;
    background-image: linear-gradient(var(--bar), transparent calc(var(--feather) - 6px), transparent calc(100% - var(--feather) + 6px), var(--bar));
  }
}

/* The sheet that a page goes out of focus behind: over what is on the page, under the frame and the grain,
   so the corners stay sharp and the paper is not seen to change. It is clear until the page is being left
   or the index is open (turn.js says when, for both), and clears a moment after whatever stood on it has gone */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-color: transparent;
  transition: background-color var(--leave) 0.15s, -webkit-backdrop-filter var(--leave) 0.15s, backdrop-filter var(--leave) 0.15s;
}

:is(.is-leaving, .is-index) body::before {
  background-color: var(--paper);
  -webkit-backdrop-filter: blur(var(--blur));
  backdrop-filter: blur(var(--blur));
  transition-delay: 0s;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ── Frame ─────────────────────────────────────────────── */

.frame {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  color: var(--faint);
  font-size: 0.8125rem;
  line-height: 1;
  letter-spacing: 0.04em;
}

/* lines surface from the paper at the bottom edge and sink back into it at the top */
.frame::before,
.frame::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
}

.frame::before {
  top: 0;
  height: calc(var(--fade) + var(--deep, 0px) + var(--safe-t));
  background: linear-gradient(var(--paper) calc(var(--fade) * 0.48 + var(--deep, 0px) + var(--safe-t)), transparent);
}

.frame::after {
  bottom: 0;
  height: calc(var(--fade) + var(--low, 0px) + var(--edge-b) - var(--edge));
  background: linear-gradient(transparent, var(--paper) calc(var(--fade) * 0.52));
}

/* on an iPhone the paper at the very edge turns to the colour of Safari's bars (see "The edges of an
   iPhone's screen", near the top) */
@supports (-webkit-touch-callout: none) {
  html:not(.night) .frame::before {
    background: linear-gradient(var(--bar), var(--paper) var(--feather), var(--paper) calc(var(--fade) * 0.48 + var(--deep, 0px) + var(--safe-t)), transparent);
  }

  html:not(.night) .frame::after {
    background: linear-gradient(transparent, var(--paper) calc(var(--fade) * 0.52), var(--paper) calc(100% - var(--feather)), var(--bar));
  }
}

.frame > * { position: absolute; z-index: 1; }

.frame__id { top: var(--edge-t); left: var(--edge-x); letter-spacing: 0.14em; }
.frame__status { bottom: var(--edge-b); left: var(--edge-x); font-style: italic; }
.frame__names { bottom: var(--edge-b); right: var(--edge-x); }

/* the only things in the frame that can be touched; the padding is only there for the finger */
.frame__nav {
  --pad: 0.75rem;
  --pad-y: var(--pad);
  top: calc(var(--edge-t) - var(--pad-y));
  right: calc(var(--edge-x) - var(--pad));
  display: flex;
}

/* a fingertip is taller than a cursor */
@media (pointer: coarse) {
  .frame__nav { --pad-y: 1rem; }
}

.frame__link {
  padding: var(--pad-y) var(--pad);
  color: var(--press);
  text-decoration: none;
  pointer-events: auto;
  touch-action: manipulation;
  transition: color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

/* one of them is not a link but something to press, and is set no differently */
button.frame__link {
  margin: 0;
  border: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}

.frame__link:focus-visible,
.frame__link:active { color: var(--ink); }

/* only where there is something to hover with: on glass a hover never leaves */
@media (hover: hover) {
  .frame__link:hover { color: var(--ink); }
}

.frame__link:focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: -0.35rem;
}

/* Leaving a page, the words in this corner go out of focus with it; arriving, they come into focus with it.
   The other three corners are the same on every page, and do neither: they are seen to stay */
.js.is-leaving .frame__link {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--leave), filter var(--leave);
}

@keyframes arrive {
  from { opacity: 0; filter: blur(var(--blur)); }
}

@media (prefers-reduced-motion: no-preference) {
  .js .frame:not(.frame--late) .frame__link,
  .is-back .frame__link { animation: arrive var(--focus) backwards; }
}

/* Seven names do not fit across a phone. One word stands in the corner for them there, and on nothing wider.
   Pressed, the page goes out of focus and the names are set out on it (see "Index"); the word that takes
   its place leads back, as the word in this corner does on every other page */
.frame__turn { display: none; }

@media (max-width: 44rem) {
  .frame--home a.frame__link { display: none; }

  .js .frame__turn {
    display: grid;
    justify-items: end;
  }
}

/* Every other page has the index too, at any width, and goes to any of the seven from where it is.
   The word for it stands beside Back, and stays in the ink while the index is open */
html:not(.js) .frame__index { display: none; }

.frame__index[aria-expanded="true"] { color: var(--ink); }

/* two words in one place, as under the Inspiral plate: one leaves, out of focus, before the other arrives */
.frame__word {
  grid-area: 1 / 1;
  position: relative;
  opacity: 0;
  filter: blur(var(--blur));
  visibility: hidden;
  transition: opacity var(--leave), filter var(--leave), visibility 0s 0.4s;
}

html:not(.is-index) .frame__word:first-child,
.is-index .frame__word:last-child {
  opacity: 1;
  filter: none;
  visibility: visible;
  transition: opacity var(--focus) 0.25s, filter var(--focus) 0.25s, visibility 0s;
}

/* "Still Arriving." On the homepage, every night at 22:25 in Taipei and for that minute only, it reads
   "Arrived." (home.js says when). The two stand in one place; one leaves, out of focus, before the other comes */
.frame__status { display: grid; }

.frame__still,
.frame__now {
  grid-area: 1 / 1;
  transition: opacity var(--focus) 0.3s, filter var(--focus) 0.3s;
}

.frame__now,
.is-arrival .frame__still {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--leave), filter var(--leave);
}

.is-arrival .frame__now {
  opacity: 1;
  filter: none;
  transition: opacity var(--focus) 0.3s, filter var(--focus) 0.3s;
}

/* a page that does not scroll has nothing to fade at its edges */
.frame--bare::before,
.frame--bare::after { content: none; }

/* On the homepage the frame waits for the title, and is part of what it tells. An event is named for the
   day it is detected, so the name comes when the v lands; then "Still Arriving." and the two names;
   last of all, the way on. The times are counted from when the page opens; see "The two crosses" */
.js .frame--late > * {
  --at: 4.6s;
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus) var(--at), filter var(--focus) var(--at), color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.js .frame--late > .frame__id { --at: 4s; }
.js .frame--late > .frame__nav { --at: 5s; }

.js.is-live .frame--late > * { opacity: 1; filter: blur(0); }

/* that wait is kept once. Coming back from another page the frame has not moved, as on any other page */
.is-back .frame--late > * {
  opacity: 1;
  filter: none;
  transition: color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}


/* ── Space ─────────────────────────────────────────────── */

.cycle { position: relative; }


/* ── The two crosses ───────────────────────────────────── */

.hero {
  height: calc(var(--vh) * 100);
  display: grid;
  place-items: center;
}

.cross--title {
  --pitch: min(5.6vw, calc(var(--vh) * 13));
  position: relative;
  width: 0;
  height: 0;
  font-size: calc(var(--pitch) * 0.74);
  font-weight: 300;
  translate: calc(var(--pitch) * -3) 0;       /* centres the long word, not the crossing */
}

.arm {
  position: absolute;
  left: 0;
  top: 0;
}

.arm i {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--pitch);
  height: var(--pitch);
  margin: calc(var(--pitch) / -2) 0 0 calc(var(--pitch) / -2);
  line-height: var(--pitch);
  text-align: center;
  font-style: normal;
}

.arm--g i { transform: translateX(calc(var(--n) * var(--pitch))); }
.arm--w i { transform: translateY(calc(var(--n) * var(--pitch))); }

/* The opening, the first time the page is seen. "Gravitational" comes first, whole but for its v, its letters
   close together and in no order (home.js scatters them over half a second). Then it stands a moment as it
   is, a gap in it, so the gap can be seen; "Waves" sets out at 2.2s and lands, v in the gap, at about 4s */
.js .arm--g i {
  opacity: 0;
  filter: blur(0.16em);
  transition: opacity var(--focus) var(--delay, 0s), filter var(--focus) var(--delay, 0s);
}

.js.is-live .arm--g i { opacity: 1; filter: blur(0); }

/* "Waves" is still arriving when the page opens. It settles from just above, so nothing
   passes through anything, and its v fills the gap left in the other word */
.js .arm--w {
  opacity: 0;
  filter: blur(0.16em);
  translate: 0 calc(var(--pitch) * -0.75);
  transition: opacity 2.6s ease 2.2s, filter 2.6s ease 2.2s, translate 3.6s cubic-bezier(0.22, 1, 0.36, 1) 2.2s;
}

.js.is-live .arm--w { opacity: 1; filter: blur(0); translate: 0 0; }

/* "Waves" arrives once. Coming back from another page it is already in place, and the two words
   come into focus together, at once, the way anything else does */
.is-back .arm--g i,
.is-back .arm--w { transition: opacity var(--focus), filter var(--focus); }

.js.is-back .arm--w { translate: 0 0; }

/* The v is where the two words meet, and so is the moment they did. Rest a hand on it, or touch it (home.js),
   and the moment comes into focus in the empty square beside the crossing; take the hand away and it goes.
   Nothing marks the v as something to touch. Whoever finds it, finds it */
.moment {
  position: absolute;
  left: calc(var(--pitch) * 0.62);
  top: calc(var(--pitch) * 0.62);
  color: var(--faint);
  font-size: 0.8125rem;
  font-weight: 400;
  line-height: 1.6;
  letter-spacing: 0.04em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--leave), filter var(--leave);
}

.moment__beside {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
}

.cross--title.is-met .moment {
  opacity: 1;
  filter: blur(0);
  transition: opacity var(--focus), filter var(--focus);
}

@media (hover: hover) {
  .cross--title:has(.v:hover) .moment {
    opacity: 1;
    filter: blur(0);
    transition: opacity var(--focus), filter var(--focus);
  }
}

/* on a tall screen the detector turns: the long arm stands up */
@media (orientation: portrait) {
  .cross--title {
    --pitch: min(calc(var(--vh) * 5.4), 15vw);
    translate: 0 calc(var(--pitch) * -3);
  }

  .arm--g i { transform: translateY(calc(var(--n) * var(--pitch))); }
  .arm--w i { transform: translateX(calc(var(--n) * var(--pitch))); }

  .js .arm--w { translate: calc(var(--pitch) * -0.75) 0; }
}


@media (prefers-reduced-motion: reduce) {
  .js .arm--w { translate: 0 0; }
}


/* ── Poem ──────────────────────────────────────────────── */

.poem {
  --step: clamp(1.25em, 3vw, 2em);
  position: relative;
  padding-bottom: calc(var(--vh) * 45);
  /* The poem does not wait out of sight below the first screen. Its first line stands at the foot of it, in
     the paper that covers the bottom edge (.frame::after, the same sizes): the row of English ends just where
     that paper turns solid, so the line is there and not quite there, and what is under it (its other
     language, the rest) is not yet. It is what says there is more. home.js counts it in from the start */
  margin-top: calc(var(--fade) * 0.52 - 1.75em - (var(--fade) + var(--low, 0px) + var(--edge-b) - var(--edge)));
}

/* in the opening it is the last thing to arrive, after the way on, and the first screen ends on it.
   (Coming back, it is there with the title; a touch while the opening plays brings it on, see home.js) */
@media (prefers-reduced-motion: no-preference) {
  .js:not(.is-back) .stanza--fleeting > .line:first-child { transition-delay: 5.5s; }
  .js:not(.is-back) .stanza--fleeting > .line:first-child > [lang] { transition-delay: calc(5.5s + var(--after)); }
}

.line {
  margin: 0;
  width: max-content;
  white-space: nowrap;
}

.stanza,
.poem > .line { margin-left: var(--col); }

/* Coming into focus. home.js adds .is-in once, as a line enters the screen from below.
   Three lines hand the effect down to their parts instead, noted where they are set. */
.js :is(.line, .sig, .x__col, .t, .star) {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus), filter var(--focus);
}

.js :is(.line, .sig, .x__col).is-in,
.js .is-in > :is(.t, .star) {
  opacity: 1;
  filter: blur(0);
}

.stanza--seed { margin-top: calc(var(--vh) * 26); }
.line--seed { margin-top: calc(var(--vh) * 34); }       /* what is planted stays out of sight for a while */
.quiet { opacity: 0.5; }

.stanza--belief { margin-top: calc(var(--vh) * 30); }

.stanza--some { margin-top: calc(var(--vh) * 16); }
.stanza--some .line { margin-top: calc(var(--vh) * 24); }

/* Each of the four lines is also a way in: its noun leads to the page that is that thing. As everywhere on the
   site, a hairline is all that says so. The finger is given a little more than the word to land on */
.door {
  padding: 0.35em 0.2em;
  margin: -0.35em -0.2em;
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 0.28em;
  touch-action: manipulation;
  transition: text-decoration-color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.door:active { text-decoration-color: currentColor; }

@media (hover: hover) {
  .door:hover { text-decoration-color: currentColor; }
}

[data-fx="way"].way--italic { font-style: italic; }
[data-fx="way"].way--vertical { writing-mode: vertical-rl; }
/* standing up, the Chinese is on the left of the line, where a hairline under the word would fall:
   the door's hairline goes to the right instead, on the side with nothing beside it */
[data-fx="way"].way--vertical .door { text-underline-position: right; }
[data-fx="way"].way--stairs .w { display: block; margin-left: calc(var(--l) * 1em); }

.stanza--perhaps { margin-top: calc(var(--vh) * 44); }
.stanza--perhaps .line:nth-child(2) { margin-left: var(--step); }
.stanza--perhaps .line:nth-child(3) { margin-left: calc(var(--step) * 2); }

.poem > .line--boundless { margin-top: calc(var(--vh) * 7); }

.js .poem > .line--boundless {
  display: flex;
  justify-content: space-between;
  width: auto;
  margin-inline: var(--edge-x);
}

.line--boundless .sp { width: 0.25em; }

/* the letters come out one at a time, in no order, the way stars do at dusk */
.js .line--boundless { opacity: 1; filter: none; }
.js .line--boundless .star { transition-delay: var(--d, 0s); }

.pair {
  margin: calc(var(--vh) * 30) 0 0 var(--col);
}

.js .pair { margin-inline: var(--edge-x); }

.poem > .line--again { margin-top: calc(var(--vh) * 40); }

.poem > .line--found {
  margin-top: calc(var(--vh) * 32);
  margin-left: calc(var(--col) + 3em);
}

.x {
  position: relative;
  display: inline-block;
}

/* "each" keeps a place for its h, but the letter itself comes with "other".
   "other" has to be seen waiting before the rest of its line arrives, so the line
   itself stays clear and only its words (.t) come into focus */
.x__gap { visibility: hidden; }
.js .line--found { opacity: 1; filter: none; }
.t { display: inline-block; white-space: pre; }

.x__col {
  position: absolute;
  inset: 0;
  transform: translateY(var(--off, 0px));
}

.x__col i {
  position: absolute;
  left: 50%;
  top: 0;
  font-style: normal;
  transform: translate(-50%, calc(var(--n) * 1.5em));
}

.poem > .line--joy { margin-top: calc(var(--vh) * 26); }

.dash {
  display: inline-block;
  transform-origin: 0 50%;          /* home.js moves this to the start of the stroke */
  transform: scaleX(var(--len, 1));
}

.sig {
  width: max-content;
  margin: 3.5em var(--edge-x) 0 auto;
}


/* ── Index ─────────────────────────────────────────────── */

/* The seven names, set in the title's hand down the left edge of the poem, on the sheet the page has gone
   out of focus behind (body::before, near the top). turn.js opens and shuts it.
   On the homepage it is for a screen too narrow to carry the names in its corner; a wider one never has it.
   Every other page has it at any width, opened by the word beside Back, and its own name there is faint */
.index {
  --row: calc(var(--vh) * 8.6);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  padding: 0 var(--edge-x) 0 calc(var(--col) - 0.75rem);
}

@media not all and (max-width: 44rem) {
  .index--home { display: none; }
}

/* without turn.js nothing can open it. The homepage's stands at the foot of the poem instead;
   on the other pages there is still Back */
html:not(.js) .index--home { padding-bottom: calc(var(--vh) * 30); }
html:not(.js) .index:not(.index--home) { display: none; }

/* under the frame, so the words in the corner can still be pressed; a touch anywhere else on it
   is a touch on the index, and does not move the page behind */
.js .index {
  position: fixed;
  inset: 0;
  z-index: 1;
  visibility: hidden;
  touch-action: none;
  transition: visibility 0s 0.4s;
}

.js.is-index .index {
  visibility: visible;
  transition-delay: 0s;
}

/* the padding is only there for the finger, and is taken back from the left so the names keep the poem's edge */
.index__link {
  --d: calc(0.2s + var(--i) * 0.05s);
  padding: 0 0.75rem;
  color: inherit;
  font-size: min(8vw, calc(var(--vh) * 4.4));
  font-weight: 300;
  line-height: var(--row);
  letter-spacing: 0.06em;
  text-decoration: none;
  white-space: nowrap;
}

.index__link:focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: -0.5rem;
}

/* the page it was opened on: there, in its place, but faint. Pressed, it only shuts the index.
   (Mixed from the ink and the paper rather than the faint ink itself, which on Afterglow is nearly the ink) */
.index__link[aria-current] { color: color-mix(in srgb, var(--ink) 45%, var(--paper)); }

/* they come into focus one after another, down the page, once the page has begun to go.
   Shut, or left for one of the pages, they all go at once */
.js .index__link {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--leave), filter var(--leave);
}

.js.is-index .index__link {
  opacity: 1;
  filter: blur(0);
  transition: opacity var(--focus) var(--d), filter var(--focus) var(--d);
}

.js.is-leaving .index__link {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--leave), filter var(--leave);
}


/* ── About ─────────────────────────────────────────────── */

.about {
  --measure: 31em;
  --gutter: max(var(--edge-x), 1.5rem);
  box-sizing: border-box;
  min-height: calc(var(--vh) * 100);
  padding: calc(var(--vh) * 24) var(--gutter) calc(var(--vh) * 30) var(--col);
}

/* and its right edge too */
@media (max-width: 30rem) {
  .about { --gutter: var(--edge-x); }
}

.entry + .entry { margin-top: 5.5em; }

/* the term is set in the same small hand as the frame */
.entry__term {
  margin: 0 0 1.1rem;
  color: var(--faint);
  font-size: 0.8125rem;
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0.04em;
}

.entry__text { max-width: var(--measure); }

.entry__text p {
  margin: 0;
  hyphens: manual;
  text-wrap: pretty;
}

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

/* a figure is not parted from its unit at the end of a narrow line */
.nb { white-space: nowrap; }

.entry__text sup {
  font-size: 0.62em;
  line-height: 0;
  vertical-align: 0.62em;
}

/* the text keeps the left edge of the poem; with room, each term steps out into the margin */
@media (min-width: 60rem) {
  .about { padding-left: 0; }

  .entry {
    display: grid;
    grid-template-columns: var(--col) minmax(0, var(--measure));
    align-items: baseline;
  }

  .entry__term { margin: 0 0 0 var(--edge-x); }
}

.entry__text .entry__event {
  margin-top: 1.5em;
  font-size: clamp(2.25rem, 1.2rem + 4.2vw, 4.5rem);
  font-weight: 300;
  line-height: 1.1;
  letter-spacing: 0.06em;
}

.js .about :is(.entry__term, p) {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus) var(--d, 0s), filter var(--focus) var(--d, 0s);
}

.js .about .is-in {
  opacity: 1;
  filter: blur(0);
}


/* ── Inspiral ──────────────────────────────────────────── */

.inspiral {
  position: fixed;
  inset: 0;
  overflow: hidden;
}

/* this page is one sheet: a finger drawn across it parts the letters and does not drag the paper.
   Ringdown and Afterglow are single sheets too; on a phone a pull at the top would otherwise reload them */
html:has(.inspiral, .home, .road) { overscroll-behavior: none; }

/* the sky gathers into the number once a visit. Coming back, the number comes into focus where it stands */
@media (prefers-reduced-motion: no-preference) {
  .is-back .sky { animation: arrive var(--focus) backwards; }
}

.sky {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* the caption under the plate, in the same small hand as the frame.
   inspiral.js sets its top, just under the horizon */
.plate {
  position: absolute;
  top: 80%;
  left: 0;
  right: 0;
  padding-inline: max(var(--edge-x), 1.5rem);
  color: var(--faint);
  font-size: 0.8125rem;
  line-height: 1.75;
  letter-spacing: 0.04em;
  font-variant-numeric: lining-nums tabular-nums;
  text-align: center;
  pointer-events: none;
}

.plate p {
  margin: 0;
  text-wrap: balance;
}

/* two captions in one place: one leaves, out of focus, before the other arrives */
.js .plate > * {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  padding-inline: inherit;
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus), filter var(--focus);
}

.js .plate > .is-in {
  opacity: 1;
  filter: blur(0);
}

/* on a phone held sideways there is no room under the plate: the caption stands beside it,
   level with its middle. inspiral.js decides when, and sets its left edge and width */
.plate--beside {
  right: auto;
  padding-inline: 0;
  text-align: left;
}

.js .plate--beside > * { transform: translateY(-50%); }
.plate--beside .plate__turn { margin-left: -0.75rem; }

/* where the caption's second line is too long for one row, it breaks at its dot, and the dot goes */
.plate--beside .plate__dot {
  display: block;
  height: 0;
  overflow: hidden;
}

@media (max-width: 30rem) {
  .plate__dot {
    display: block;
    height: 0;
    overflow: hidden;
  }
}

/* the name of the other picture. A hairline under it is all that says it can be pressed */
.plate__turn {
  display: none;
  margin-top: 0.35rem;
  padding: 0.75rem;
  border: 0;
  background: none;
  color: var(--press);
  font: inherit;
  letter-spacing: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
  text-underline-offset: 0.38em;
  cursor: pointer;
  touch-action: manipulation;
  transition: color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.js .plate__turn { display: inline-block; }
.js .plate > .is-in .plate__turn { pointer-events: auto; }

.plate__turn:focus-visible,
.plate__turn:active { color: var(--ink); }

@media (hover: hover) {
  .plate__turn:hover { color: var(--ink); }
}

.plate__turn:focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: -0.35rem;
}


/* ── Ringdown ──────────────────────────────────────────────── */

.home {
  position: fixed;
  inset: 0;
  overflow: hidden;
}

/* The floor is the end of the signal, ringing down to a straight line; ringdown.js draws it, as faint as the
   lines under the words that can be pressed. It comes into focus with the cats */
.floor {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  fill: none;
  stroke: color-mix(in srgb, var(--ink) 40%, transparent);
  stroke-width: 1px;
  stroke-linejoin: round;
}

.js .floor {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus), filter var(--focus);
}

.js .floor.is-in {
  opacity: 1;
  filter: blur(0);
}

/* The light from a window that is not drawn: the sun's, or after dark the moon's. ringdown.js says where it
   lies and at what angle. On paper light is seen as warmth, so it is laid on the paper (multiply) as a tint;
   on the dark paper it is laid on as light (screen). Its edges are soft, as light through a window is */
.light {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  mix-blend-mode: multiply;
  filter: blur(var(--soft, 0.75rem));
  --sun: #f3a24e;
  --sun-a: 0.5;
  --moon: #7d98c6;
  --moon-a: 0.42;
}

@media (prefers-color-scheme: dark) {
  .light {
    mix-blend-mode: screen;
    --sun: #ffc27a;
    --sun-a: 0.16;
    --moon: #b9cdee;
    --moon-a: 0.12;
  }
}

.light--sun stop { stop-color: var(--sun); }
.light--moon stop { stop-color: var(--moon); }

.js .light {
  opacity: 0;
  transition: opacity var(--focus);
}

.js .light--sun.is-in { opacity: var(--sun-a); }
.js .light--moon.is-in { opacity: var(--moon-a); }

/* The cats stand on it. ringdown.js says where along it, and how large.
   A cat can be picked up and carried (ringdown.js); a finger on it moves the cat, not the page */
.cat {
  position: absolute;
  bottom: calc(var(--vh) * 30);
  height: auto;
  image-rendering: pixelated;
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.cat.is-held { cursor: grabbing; }

.cat--turned { transform: scaleX(-1); }

/* a picture is larger than a line of type, and needs more blur to be as far out of focus */
.js .cat {
  opacity: 0;
  filter: blur(calc(var(--blur) * 2.5));
  transition: opacity var(--focus), filter var(--focus);
}

.js .cat.is-in {
  opacity: 1;
  filter: blur(0);
}


/* ── Signal ────────────────────────────────────────────── */

/* the first screen is the tape and its caption, held clear of the frame's faded edges */
.trace {
  box-sizing: border-box;
  height: calc(var(--vh) * 100);
  overflow-x: clip;            /* the sheet signal.js draws on reaches past the screen's sides; on a phone that
                                  would widen the page and push the frame's right-hand corners off the glass */
  display: flex;
  flex-direction: column;
  padding: calc(var(--fade) + var(--deep, 0px) + var(--safe-t)) var(--edge-x) calc(var(--fade) + var(--low, 0px) + var(--edge-b) - var(--edge));
}

/* signal.js draws in whatever room the caption leaves */
.trace__plot {
  position: relative;
  flex: 1;
  min-height: 0;
}

/* signal.js lets the sheet reach a little past the tape on every side, for the glass near its edge.
   It only draws; the pointer is listened for on the window, so it never stands in the way of the caption */
.trace__tape {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* the event's name, in the frame's own hand. signal.js puts it at the head of its hairline,
   and it keeps to the side of the quiet */
.trace__event {
  position: absolute;
  padding-right: 0.75em;
  color: var(--faint);
  font-size: 0.8125rem;
  line-height: 1;
  letter-spacing: 0.14em;
  white-space: nowrap;
  translate: -100% 0;
}

.trace__note {
  margin-top: 1.6rem;
  color: var(--faint);
  font-size: 0.8125rem;
  line-height: 1.75;
  letter-spacing: 0.04em;
  text-align: center;
}

.trace__note p { margin: 0; }
.trace__tall { display: none; }

@supports not (overflow-x: clip) {
  .trace { overflow: hidden; }
}

/* on a tall screen the tape turns, and so does what is said of it. A narrow sheet has no room
   for the crowded end, so the tape is nearly two screens long: the quiet is scrolled through,
   and the caption is read before setting out */
@media (orientation: portrait) {
  .trace { height: calc(var(--vh) * 180); }

  .trace__note {
    order: -1;
    margin: 0 0 1.6rem;
  }

  .trace__event {
    padding: 0 0 0.7em;
    translate: 0 -100%;
  }

  .trace__wide { display: none; }
  .trace__tall { display: block; }
}

.js :is(.trace__event, .trace__note) {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus), filter var(--focus);
}

.js :is(.trace__event, .trace__note).is-in {
  opacity: 1;
  filter: blur(0);
}

/* the entries under the tape are set as the About page's are; they only begin sooner */
.log {
  min-height: 0;
  padding-top: calc(var(--vh) * 12);
}


/* ── Counterpart ───────────────────────────────────────── */

/* a way out of the site. As on the Inspiral plate, a hairline is all that says so */
.out {
  padding: 0.35em 0.2em;
  margin: -0.35em -0.2em;
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
  text-underline-offset: 0.3em;
  transition: text-decoration-color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.out:focus-visible,
.out:active { text-decoration-color: currentColor; }

@media (hover: hover) {
  .out:hover { text-decoration-color: currentColor; }
}

.out:focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: 0.2em;
}


/* ── Circulars ─────────────────────────────────────────── */

/* What visitors write may be in any language. Chinese, Japanese and Korean come from whole Noto Serif faces
   in pieces (assets/css/circulars.css, made by tools/circulars_face.py), so that a phone, which has no Song
   face of its own, sets them as a Mac does; a browser asks only for the pieces a page uses */
.circulars { font-family: "Newsreader", "GW Circulars", var(--serif-wide); }

/* they are shorter than the entries of a reference book, and stand closer */
.entry + .circular { margin-top: 4em; }

/* whoever signed stands where the term does. A name may be long, and may turn the line */
.circular .entry__term {
  line-height: 1.5;
  overflow-wrap: anywhere;
}

@media (min-width: 60rem) {
  .circular .entry__term { margin-right: 1.5rem; }
}

.circular__text {
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* when it was sent, and when it arrived, in the frame's small hand */
.entry__text .circular__dates,
.write__note,
.review__group {
  color: var(--faint);
  font-size: 0.8125rem;
  line-height: 1.75;
  letter-spacing: 0.04em;
}

.entry__text .circular__dates { margin-top: 0.8rem; }

/* one that has been sent and not yet kept, seen only where it was written */
.circular--arriving .circular__text { color: var(--faint); }

/* the place to write one: no box, a hairline to write on, darker while it is being written on */
html:not(.js) .write,
.write[hidden] { display: none; }

.write__field {
  display: block;
  box-sizing: border-box;
  width: 100%;
  min-height: calc(1.75em + 0.3em + 1px);
  margin: 0;
  padding: 0 0 0.3em;
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--faint) 55%, transparent);
  border-radius: 0;
  background: none;
  color: inherit;
  font: inherit;
  -webkit-appearance: none;
  appearance: none;
  outline: none;
  resize: none;
  overflow: hidden;
  field-sizing: content;
  transition: border-color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.write__field::placeholder {
  color: var(--faint);
  opacity: 1;
}

.write__field:focus { border-bottom-color: var(--ink); }

/* a browser that fills a field in for you paints it; this keeps it paper */
.write__field:-webkit-autofill {
  -webkit-text-fill-color: var(--ink);
  box-shadow: 0 0 0 100em var(--paper) inset;
}

.write__short .write__field { width: min(13em, 100%); }

.entry__text .write__foot {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

/* a word to press, set as the one under the Inspiral plate is: small, with a hairline under it.
   The padding is for the finger, and is taken back from the left so the word keeps the text's edge */
.press {
  margin: 0 0 0 -0.75rem;
  padding: 0.75rem;
  border: 0;
  background: none;
  color: var(--press);
  font: inherit;
  font-size: 0.8125rem;
  line-height: 1;
  letter-spacing: 0.04em;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
  text-underline-offset: 0.38em;
  cursor: pointer;
  touch-action: manipulation;
  transition: color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.press + .press { margin-left: 0; }

.press:focus-visible,
.press:active { color: var(--ink); }

@media (hover: hover) {
  .press:hover { color: var(--ink); }
}

.press:focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: -0.35rem;
}

.press:disabled {
  color: var(--faint);
  cursor: default;
}

/* With many circulars the place to write is a long way down. While it is out of sight a word stands
   beside Back and leads to it. The word takes its place and gives it up without moving */
.frame__link--foot {
  opacity: 0;
  visibility: hidden;
  transition: color 0.6s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s 0.6s;
}

.frame__link--foot.is-on {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

/* it is drawn round with a hairline, in its own ink, so it reads as something to press and not as a place
   to go. The line is laid over the padding, which stays the finger's */
.frame__link--foot::before {
  content: "";
  position: absolute;
  inset: calc(var(--pad-y) - 0.55rem) calc(var(--pad) - 0.65rem);
  border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
  pointer-events: none;
}

/* Pressed, nothing is scrolled past: the page goes out of focus where it is, more quickly than it came,
   and circulars.js brings it back into focus at its foot */
.js .about.is-away :is(.entry__term, p) {
  opacity: 0;
  filter: blur(var(--blur));
  transition-duration: 0.4s;
  transition-delay: 0s;
}

/* once sent, the form leaves the one way anything leaves: out of focus */
.js .about .write.is-out :is(.entry__term, p) {
  opacity: 0;
  filter: blur(var(--blur));
  transition-delay: 0s;
}


/* ── Review ────────────────────────────────────────────── */

/* a desk, not a page of the site: everything is simply there */
.js .review :is(.entry__term, p) {
  opacity: 1;
  filter: none;
  transition: none;
}

.review__group { margin: 6em 0 2.5em; }
.review__group:first-child { margin-top: 0; }
.review__group + .circular { margin-top: 0; }

@media (min-width: 60rem) {
  .review__group { margin-left: var(--col); }
}


/* ── Afterglow ─────────────────────────────────────────── */

/* The one page that is not paper. A road at dusk fills the window, whatever the system's light or dark,
   and the frame is laid over it. */
.night {
  --paper: #0a0d0d;
  --ink: #f4f1ea;
  --faint: #d6d2c8;
  color-scheme: dark;
}

/* the picture has its own grain */
.night body::after { content: none; }

/* it comes up out of the dark once its first frame is drawn */
.road {
  position: fixed;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 2.4s cubic-bezier(0.33, 0, 0.2, 1);
}

.is-arrived .road { opacity: 1; }

/* The frame's four words lie on forest one moment and on sky the next. A wide, soft shade under each
   keeps them readable on either, without drawing a box round them. */
.frame--over { text-shadow: 0 0 0.9em rgb(0 0 0 / 0.55), 0 0 0.2em rgb(0 0 0 / 0.35); }

/* where the road cannot be drawn, one line says so */
.road__none:not([hidden]) {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  margin: 0;
  padding: var(--edge-x);
  color: var(--faint);
  font-style: italic;
  text-align: center;
}


/* Tune: the scene's numbers, for whoever wants to turn them. Shut until its word in the frame is pressed.
   It is set the way the frame is (small, spaced, in the faint ink) on a pane of darkened glass, so the
   road can still be watched through and beside it while it is being changed. */
.tune {
  position: fixed;
  top: calc(var(--edge-t) + 2.25rem);
  right: var(--edge-x);
  z-index: 1;
  box-sizing: border-box;
  width: min(19rem, calc(100vw - 2 * var(--edge-x)));
  max-height: calc(100vh - var(--edge-t) - var(--edge-b) - 5.5rem);
  max-height: calc(100dvh - var(--edge-t) - var(--edge-b) - 5.5rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgb(255 255 255 / 0.2) transparent;
  padding: 0.125rem 1rem 0.875rem;
  border: 1px solid rgb(255 255 255 / 0.1);
  border-radius: 0.25rem;
  background: rgb(8 11 11 / 0.6);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  color: var(--faint);
  font-size: 0.8125rem;
  line-height: 1.3;
  letter-spacing: 0.04em;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s ease, visibility 0s 0.5s;
}

.tune.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.5s ease;
}

/* it lies over the sheet the page leaves behind, so it is taken away with the page.
   (Opening the index shuts it first: turn.js) */
.is-leaving .tune.is-open {
  opacity: 0;
  transition: opacity var(--leave);
}

.tune fieldset {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.tune legend {
  padding: 0.875rem 0 0.125rem;
  font-style: italic;
  opacity: 0.7;
}

/* a name, a way back (only once it has been moved), its number; under them the thing that moves it */
.tune__row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  column-gap: 0.625rem;
  padding-top: 0.5rem;
}

.tune__row label { grid-column: 1; }
.tune__undo { grid-column: 2; }

.tune__row output,
.tune input[type="color"] { grid-column: 3; }

.tune__row output {
  color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums;
  text-align: right;
}

.tune [lang] {
  margin-left: 0.6em;
  font-size: 0.6875rem;
  opacity: 0.7;
}

.tune button {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  touch-action: manipulation;
  transition: color 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.tune button:focus-visible,
.tune button:active { color: var(--ink); }

@media (hover: hover) {
  .tune button:hover { color: var(--ink); }
}

.tune__undo {
  font-style: italic;
  opacity: 0.7;
}

/* a hair for the track and a dot of ink to move along it */
.tune input[type="range"] {
  grid-column: 1 / -1;
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 1.25rem;
  margin: 0;
  background: none;
  cursor: pointer;
}

.tune input[type="range"]::-webkit-slider-runnable-track {
  height: 1px;
  background: rgb(255 255 255 / 0.28);
}

.tune input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 0.6875rem;
  height: 0.6875rem;
  margin-top: calc(0.5px - 0.34375rem);
  border: 0;
  border-radius: 50%;
  background: var(--ink);
}

.tune input[type="range"]::-moz-range-track {
  height: 1px;
  background: rgb(255 255 255 / 0.28);
}

.tune input[type="range"]::-moz-range-thumb {
  width: 0.6875rem;
  height: 0.6875rem;
  border: 0;
  border-radius: 50%;
  background: var(--ink);
}

/* a colour is a small pane of itself */
.tune input[type="color"] {
  -webkit-appearance: none;
  appearance: none;
  width: 2.25rem;
  height: 1.125rem;
  margin: 0.125rem 0;
  padding: 0;
  border: 1px solid rgb(255 255 255 / 0.3);
  border-radius: 2px;
  background: none;
  cursor: pointer;
}

.tune input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.tune input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 1px; }
.tune input[type="color"]::-moz-color-swatch { border: 0; }

.tune__check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding-top: 1rem;
  cursor: pointer;
}

.tune__check input {
  margin: 0;
  accent-color: var(--faint);
}

.tune__check [lang] { margin-left: 0.6em; }

.tune__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  margin-top: 0.875rem;
  padding-top: 0.75rem;
  border-top: 1px solid rgb(255 255 255 / 0.1);
}

.tune :focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: 0.2rem;
}

/* a fingertip needs more than a dot of ink and a line of small type. The dot stays the size it is;
   only the ground around it that can be pressed grows */
@media (pointer: coarse) {
  .tune input[type="range"] { height: 2rem; }

  .tune input[type="range"]::-webkit-slider-thumb {
    box-sizing: content-box;
    margin-top: calc(0.5px - 0.34375rem - 0.5rem);
    border: 0.5rem solid transparent;
    background-clip: content-box;
  }

  .tune input[type="range"]::-moz-range-thumb {
    box-sizing: content-box;
    border: 0.5rem solid transparent;
    background-clip: content-box;
  }

  .tune__undo {
    padding: 0.5rem 0.25rem;
    margin: -0.5rem -0.25rem;
  }

  .tune__actions { gap: 0 1.25rem; }
  .tune__actions button { padding: 0.75rem 0 0.5rem; }
}

/* with another language under the words in the frame, it starts a line lower */
[data-beside] .tune { top: calc(var(--edge-t) + 3.25rem); }

/* on a phone it comes up from the foot instead, and leaves the upper half of the road to be seen */
@media (max-width: 44rem) {
  .tune,
  [data-beside] .tune {
    top: auto;
    bottom: calc(var(--edge-b) + 2.25rem);
    left: var(--edge-x);
    width: auto;
    max-height: 48vh;
    max-height: 48dvh;
  }

  [data-beside] .tune { bottom: calc(var(--edge-b) + 3.25rem); }
}


/* ── Nothing here ──────────────────────────────────────── */

/* What is sent for an address the site does not have (404.html): the frame, and in the middle of the
   paper one line in the poem's hand. It comes into focus once, as the word in the corner does */
.nothing {
  box-sizing: border-box;
  display: grid;
  place-items: center;
  min-height: calc(var(--vh) * 100);
  padding-inline: var(--edge-x);
  text-align: center;
}

.nothing__line {
  margin: 0;
  font: inherit;
  text-wrap: balance;
}

@media (prefers-reduced-motion: no-preference) {
  .js .nothing__line { animation: arrive var(--focus) backwards; }
}


/* ── The language beside the English ───────────────────── */

/* The site is written in English. Chinese, Japanese, Korean, Spanish or French can stand beside it:
   smaller, in lighter ink, and a moment later, the way one event is seen again in another band.
   It takes no part in what the English does; it only goes where its English goes. lang.js chooses
   one language; beside.js leaves only that one before the page is measured or taken apart. */

html:not([data-beside="zh"]) [lang|="zh"] { display: none !important; }
html:not([data-beside="ja"]) [lang|="ja"] { display: none !important; }
html:not([data-beside="ko"]) [lang|="ko"] { display: none !important; }
html:not([data-beside="es"]) [lang|="es"] { display: none !important; }
html:not([data-beside="fr"]) [lang|="fr"] { display: none !important; }

[lang|="zh"] {
  font-family: var(--han);
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0.08em;
  line-break: strict;
  text-autospace: normal;
}

/* the long dash is two dashes with nothing between them, however the line around them is spaced */
[lang|="zh"] .dashes { letter-spacing: 0; }

/* In the frame it stands on the inner side of its English: under what is at the top of the page,
   over what is at the foot. The English keeps the corner, to the pixel */
.frame [lang] {
  position: absolute;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  white-space: nowrap;
}

.frame__status [lang] { left: 0; bottom: calc(100% + 0.5rem); }

/* under each name in the frame, centred on it, the way a gloss is set under a word */
.frame__link { position: relative; }

/* It is part of what can be pressed: its own padding reaches up to the English and as far round it as
   the English's does, so a finger that lands on the words beside it, or between the two, still lands on the link */
.frame__link [lang] {
  top: calc(100% - var(--pad-y));
  left: 50%;
  padding: 0.45rem var(--pad) var(--pad-y) calc(var(--pad) + 0.14em);
  translate: -50% 0;
}

/* the hairline round Write takes in the words under it as well */
[data-beside] .frame__link--foot::before { bottom: calc(var(--pad-y) - 0.55rem - 1.25rem); }

/* the paper at the top and the foot stays solid a line further in, so nothing is read through it */
[data-beside] { --deep: 1.25rem; --low: 1.25rem; }

/* the two words that share the corner of the homepage on a phone: each has its own, under itself */
.frame__word [lang] { top: 100%; }

/* in the index it stands beside its name, on the name's own line, and trails it as it does everywhere */
.index__link [lang] {
  margin-left: 1.2em;
  color: var(--faint);
  font-size: 0.75rem;
  line-height: 1;                 /* it stands on the name's line and does not make the line taller */
  letter-spacing: 0.14em;
}

.js .index__link [lang] {
  opacity: 0;
  transition: opacity var(--leave);
}

.js.is-index .index__link [lang] {
  opacity: 1;
  transition: opacity var(--focus) calc(var(--d) + var(--after));
}

/* the title: at the end of the long word, under it. It arrives last, after "Waves" has settled */
.cross > [lang] {
  position: absolute;
  left: calc(var(--pitch) * 9.08 + 0.5em);
  top: calc(var(--pitch) * 0.6);
  translate: -100% 0;
  color: var(--faint);
  font-size: max(0.75rem, calc(var(--pitch) * 0.22));
  letter-spacing: 0.5em;
  white-space: nowrap;
}

.js .cross > [lang] {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus) calc(4s + var(--after)), filter var(--focus) calc(4s + var(--after));
}

.js.is-live .cross > [lang] { opacity: 1; filter: blur(0); }
.is-back .cross > [lang] { transition: opacity var(--focus) var(--after), filter var(--focus) var(--after); }

/* when the cross turns, so does it: beside the end of the long word, and set the way Chinese stands up */
@media (orientation: portrait) {
  .cross > [lang] {
    left: calc(var(--pitch) * 0.72);
    top: calc(var(--pitch) * 9.3 + 0.5em);
    translate: 0 -100%;
    writing-mode: vertical-rl;
  }
}

/* the poem: under each line, and part of it, so it is carried wherever the line is */
.line > [lang] {
  display: block;
  margin-block-start: -0.4em;
  padding-block-end: 0.8em;
  color: var(--beside-ink);
  font-size: max(0.75rem, 0.64em);
  line-height: 1.5;
}

.js .line > [lang] {
  opacity: 0;
  filter: blur(var(--blur));
  transition: opacity var(--focus) var(--after), filter var(--focus) var(--after);
}

.js .is-in > [lang] {
  opacity: 1;
  filter: blur(0);
}

/* "in some way": upright when the line is italic; a column of its own when the line stands up
   (the margins above are logical, and turn with it); under the last step of the stairs */
[data-fx="way"].way--stairs > [lang] { margin-left: var(--end, 0px); }

/* "boundless": a second, fainter row of the same sky, across the same width */
.js .poem > .line--boundless { position: relative; }

.js .line--boundless > [lang] {
  position: absolute;
  inset: 100% 0 auto;
  display: flex;
  justify-content: space-between;
  margin: 0;
  padding: 0;
  opacity: 1;
  filter: none;
  letter-spacing: 0;
}

.js .line--boundless.is-in > [lang] > .star {
  opacity: 1;
  filter: blur(0);
}

/* the two signals stay next to each other: the first has its other language above it, the second below */
[data-beside] .pair__a {
  display: flex;
  flex-direction: column-reverse;
}

.pair__a > [lang] {
  margin-block: 0 -0.4em;
  padding-block: 0;
}

/* the entries: beside the term on a narrow page, under it where the term stands in the margin */
.entry__term [lang] {
  display: inline-block;
  margin-left: 0.8em;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
}

@media (min-width: 60rem) {
  .entry__term [lang] {
    display: block;
    margin: 0.55rem 0 0;
  }
}

/* each paragraph is followed closely by its other language, and the two stand a little apart from the next two */
.entry__text p[lang] {
  margin-top: 0.5em;
  color: var(--beside-ink);
  font-size: 0.82em;
  line-height: 1.85;
  letter-spacing: 0.04em;
}

.entry__text p[lang] + p:not(.entry__event),
html:not([data-beside]) .entry__text p[data-after-beside]:not(.entry__event) { margin-top: 1.5em; }

/* the small hand: captions, dates, and the words that can be pressed */
:is(.trace__note, .plate) p[lang],
:is(.circular__dates, .write__note, .press, .plate__turn) [lang] {
  font-size: 0.75rem;
  letter-spacing: 0.1em;
}

:is(.trace__note, .plate) p:not([lang]) + p[lang] { margin-top: 0.35rem; }

.circular__dates [lang] { display: block; }

/* beside a word to press. It is not under the word's hairline: only the English is the handle */
:is(.press, .write__note) [lang] {
  display: inline-block;
  margin-left: 0.7em;
}

/* under the name of the other picture, in room the name makes for it */
.plate__turn { position: relative; }
[data-beside] .plate__turn { padding-bottom: 1.8rem; }

.plate__turn [lang] {
  position: absolute;
  left: 50%;
  bottom: 0.6rem;
  padding-left: 0.1em;
  translate: -50% 0;
  white-space: nowrap;
}

.plate--beside .plate__turn [lang] {
  left: 0.75rem;
  padding-left: 0;
  translate: none;
}

[data-beside] .road__none { align-content: center; }

.road__none [lang] {
  display: block;
  margin-top: 0.5em;
  font-size: 0.82em;
}

/* under the one line of the page for an address the site does not have, as under a line of the poem */
.nothing__line > [lang] {
  display: block;
  margin-top: -0.2em;
  color: var(--beside-ink);
  font-size: max(0.75rem, 0.64em);
  line-height: 1.5;
}

@media (prefers-reduced-motion: no-preference) {
  .js .nothing__line > [lang] { animation: arrive var(--focus) var(--after) backwards; }
}



/* Each face keeps its own spacing. Latin glosses are italic, as in a book of paired readings. */
[lang|="ja"] {
  font-family: var(--mincho);
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0.08em;
  line-break: strict;
  text-autospace: normal;
  --track: 1;
  --scale: 1;
}
[lang|="ko"] {
  font-family: var(--myeongjo);
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0.04em;
  word-break: keep-all;
  --track: 0.5;
  --scale: 1;
}
:is([lang|="es"], [lang|="fr"]) {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0;
  --track: 0;
  --scale: 1.12;
}
:is([lang|="ja"], [lang|="ko"]) .dashes { letter-spacing: 0; }

/* The Chinese values above stay as they were; the other readings use the same measures with their own
   spacing. Small captions stay small, even where a paragraph can bear a slightly larger Latin face. */
:is(.frame, .index__link, .entry__term) [lang]:not([lang|="zh"]) {
  letter-spacing: calc(0.14em * var(--track));
}
:is(.trace__note, .plate) p[lang]:not([lang|="zh"]),
:is(.circular__dates, .write__note, .press, .plate__turn) [lang]:not([lang|="zh"]),
.moment__beside:not([lang|="zh"]) { letter-spacing: calc(0.1em * var(--track)); }
.cross > [lang]:not([lang|="zh"]) { letter-spacing: calc(0.5em * var(--track)); }
.line > [lang]:not([lang|="zh"]) {
  font-size: max(calc(0.75rem * var(--scale)), calc(0.64em * var(--scale)));
  max-width: calc(100vw - var(--col) - var(--edge-x));
  white-space: normal;
}
.nothing__line > [lang]:not([lang|="zh"]) {
  font-size: max(calc(0.75rem * var(--scale)), calc(0.64em * var(--scale)));
}
.entry__text p[lang]:not([lang|="zh"]) {
  font-size: calc(0.82em * var(--scale));
  letter-spacing: calc(0.04em * var(--track));
}
.entry__text :is(p[lang|="es"], p[lang|="fr"]) { line-height: 1.6; }

/* Where the English is italic, the Latin reading beside it stands upright. */
:is(.frame__status, .road__none) :is([lang|="es"], [lang|="fr"]),
.way--italic > :is([lang|="es"], [lang|="fr"]),
.tune :is(legend, .tune__undo) :is([lang|="es"], [lang|="fr"]) { font-style: normal; }

/* A changed value keeps its long label beside the number, with reset below the slider. */
html[data-beside]:not([data-beside="zh"]) .tune__undo {
  grid-column: 1 / -1;
  grid-row: 3;
  justify-self: end;
  margin-block: 0;
}

/* The extra letters in a name keep clear of the next name and the edge of the screen. */
.frame__link [lang]:not([lang|="zh"]) { padding-inline: 0; }
/* katakana names are long: set close, on the narrower widths the face has for them */
.frame__nav .frame__link [lang|="ja"] { font-size: 0.65625rem; letter-spacing: 0; font-feature-settings: "palt"; }
.frame :is([lang|="es"], [lang|="fr"]) { font-size: 0.6875rem; }
.frame__nav > .frame__link:last-child [lang]:not([lang|="zh"]) { left: auto; right: var(--pad); translate: none; }

/* A Latin title still reads across the page when the English cross stands up. */
@media (orientation: portrait) {
  .cross > :is([lang|="es"], [lang|="fr"]) {
    writing-mode: horizontal-tb;
    max-width: calc(50vw - var(--edge-x) - var(--pitch) * 0.72);
    white-space: normal;
    line-height: 1.3;
  }
}
.stanza--perhaps .line:nth-child(2) > [lang]:not([lang|="zh"]) {
  max-width: calc(100vw - var(--col) - var(--edge-x) - var(--step));
}
.stanza--perhaps .line:nth-child(3) > [lang]:not([lang|="zh"]) {
  max-width: calc(100vw - var(--col) - var(--edge-x) - var(--step) * 2);
}
/* The column of "other" continues below its English line. A longer reading stays on its left,
   rather than running through the next letter in that column. */
.line--found > [lang]:not([lang|="zh"]) {
  max-width: min(9.5em, calc(100vw - var(--col) - var(--edge-x) - 3em));
}
/* Under the last step there may be little room left on a phone: the reading folds there rather than
   running off the page. With room to spare, as on any wider screen, it stays on one line.
   (As specific as the rule for every line's reading above, and after it, so that this width is the one kept) */
[data-fx="way"].way--stairs > [lang] {
  max-width: calc(100vw - var(--col) - var(--edge-x) - var(--end, 0px));
  white-space: normal;
}
.tune [lang]:not([lang|="zh"]) { white-space: nowrap; }

/* ── For those who ask for less motion ─────────────────── */

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }

  .js :is(.line, .sig, .x__col, .t, .star, .frame--late > *, .arm--w, .arm--g i, .entry__term, .entry__text p, .plate > *, .cat, .floor, .trace__event, .trace__note, [lang]) {
    filter: none;
    transition-duration: 0.01s;
    transition-delay: 0s;
  }

  .js .light { transition-duration: 0.01s; }

  /* the sheet, the index and the word in its corner: there or not there, with nothing in between */
  body::before,
  :is(.is-leaving, .is-index) body::before,
  .js.is-index .index__link,
  .js.is-index .index__link [lang],
  .js .index__link,
  .js .index,
  .frame__word,
  html:not(.is-index) .frame__word:first-child,
  .is-index .frame__word:last-child {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    filter: none;
    transition-duration: 0.01s;
    transition-delay: 0s;
  }
}
