/* yolfez.com, the frame every room stands in.

   The room's own stylesheet comes after this one and owns everything inside
   <main class="stage">, and may dress the door out as whatever the room's
   door is (a book, a curtain edge, a gate). What is here is the part every
   room shares: the stage, the brush y back to the street, the bell and the
   pennant, the two edges, the door out (and the quieter link beside it)
   when a room has not dressed them, the focus rings, and the veil the room
   fades in from and out through.

   Motion, for every room. The root carries data-motion:
     still  the reader asked the place to stop: every animation is paused
            here, room.js pauses the videos, and ctx.motion.loop stops.
     calm   the system asked for less motion: films and loops play, and
            anything twitchy (flicker, falling things, a sway) holds. Write
            twitchy motion under html[data-motion='full'] and it holds here
            by itself.
     full   everything runs.
   The head sets it before the first frame, so nothing starts and then stops.

   Nothing in this file knows a room. */

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

@font-face {
  font-family: Newsreader;
  src: url(/fonts/newsreader.woff2) format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: Newsreader;
  src: url(/fonts/newsreader-italic.woff2) format('woff2');
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Newsreader stand-in';
  src: local('Georgia'), local('Times New Roman');
  size-adjust: 98%; ascent-override: 75%; descent-override: 27%; line-gap-override: 0%;
}

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

:root {
  color-scheme: dark;

  /* --ground comes in on the root's own style, from the room's row */
  --ground: #0a0806;
  --cream: oklch(93% 0.028 84);
  --dim:   color-mix(in oklch, var(--cream) 68%, transparent);
  --flame: oklch(86% 0.125 78);
  --ember: oklch(64% 0.15 48);
  --brass-lo: oklch(48% 0.07 72);
  --brass-hi: oklch(86% 0.1 86);
  --iron: oklch(34% 0.012 60);
  --madder: oklch(46% 0.13 30);
  --ring: color-mix(in oklch, var(--flame) 70%, var(--cream));

  --serif: Newsreader, 'Newsreader stand-in', Georgia, 'Times New Roman', serif;
  /* the room's own lettering, from its row; the edge into the next room sets
     the same four for the next room's name */
  --face: var(--serif);
  --face-style: normal;
  --face-case: none;
  --face-track: 0;
  --face-variant: normal;

  --edge-w: clamp(14px, 1.5vw, 24px);
  --inset-t: calc(10px + env(safe-area-inset-top));
  --inset-r: calc(var(--edge-w) + 6px + env(safe-area-inset-right));
  --inset-l: calc(var(--edge-w) + 6px + env(safe-area-inset-left));

  /* written by room.js from two probes; these stand in until then */
  --lvh: 100lvh;
  --svh: 100svh;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--ground);
}
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--serif);
  font-size: clamp(1rem, 0.95rem + 0.2vw, 1.125rem);
  line-height: 1.45;
  color: var(--cream);
  -webkit-tap-highlight-color: transparent;
}

p { margin: 0; }
img, video { max-width: 100%; }
[hidden] { display: none !important; }

::selection { background: color-mix(in oklch, var(--flame) 45%, transparent); }

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

/* seen by a screen reader and nothing else */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

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

/* One screen, no page scroll. The stage is a size container, so a room can
   lay its place out in cqw and cqh of the stage itself. */
.room { position: fixed; inset: 0; overflow: clip; }
.stage {
  position: absolute;
  inset: 0;
  container: stage / size;
  overflow: clip;
}

/* ------------------------------------------------ the brush y, home */

.brush {
  position: fixed;
  z-index: 20;
  top: var(--inset-t);
  left: var(--inset-l);
  width: 60px;
  height: 66px;
  display: grid;
  place-items: center;
  color: var(--cream);
  opacity: 0.8;
  text-decoration: none;
  transition: opacity 220ms ease;
}
.brush:hover, .brush:focus-visible { opacity: 1; }
.mark {
  display: block;
  width: 56px;
  height: 64px;
  background: currentColor;
  -webkit-mask: url(/mark.svg) 50% 50% / contain no-repeat;
  mask: url(/mark.svg) 50% 50% / contain no-repeat;
  filter: drop-shadow(0 1px 2px oklch(0% 0 0 / 0.5));
}
.mark i { display: none; }
.mark.no-svg { background: none; -webkit-mask: none; mask: none; }
.mark.no-svg i { display: block; font: italic 2.1rem/1 var(--serif); text-align: center; }

/* --------------------------------------------- the bell and the pennant */

.tokens {
  position: fixed;
  z-index: 20;
  top: var(--inset-t);
  right: var(--inset-r);
  display: flex;
  gap: 2px;
}
.token {
  position: relative;
  appearance: none;
  border: 0;
  padding: 0;
  margin: 0;
  background: none;
  color: inherit;
  font: inherit;
  width: 44px;
  height: 52px;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.token svg { display: block; width: 34px; height: 44px; overflow: visible; }
/* a tag hung under the object on hover or focus, the only words it has */
.token__tag {
  position: absolute;
  top: 100%;
  right: 0;
  padding-top: 2px;
  white-space: nowrap;
  font-size: 0.8rem;
  font-style: italic;
  color: var(--dim);
  text-shadow: 0 1px 3px oklch(0% 0 0 / 0.8);
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
}
.token:hover .token__tag,
.token:focus-visible .token__tag { opacity: 1; }

/* The bell: brass, hung from a little iron bracket off the wall. Hushed, it
   is dull and its clapper is tied up inside; sounding, it is lit. */
.bell__bracket { fill: none; stroke: var(--iron); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.bell__rivet { fill: var(--iron); }
.bell__body {
  transform-box: view-box;
  transform-origin: 20px 10px;
  transition: opacity 300ms ease;
}
.bell__ring { fill: none; stroke: var(--brass-lo); stroke-width: 1.3; }
.bell__shell { fill: url(#brass); stroke: color-mix(in oklch, var(--brass-lo) 60%, black); stroke-width: 0.6; }
.bell__lip { fill: none; stroke: color-mix(in oklch, var(--brass-lo) 80%, black); stroke-width: 0.9; }
.bell__mouth { fill: oklch(14% 0.02 60); }
.bell__shine { fill: none; stroke: oklch(96% 0.04 90 / 0.55); stroke-width: 1.1; stroke-linecap: round; }
.bell__clapper { fill: var(--brass-lo); transition: opacity 300ms ease; }
.bell[data-state='off'] .bell__body { opacity: 0.66; }
.bell[data-state='off'] .bell__clapper { opacity: 0; }
.bell[data-state='on'] svg { filter: drop-shadow(0 0 6px color-mix(in oklch, var(--flame) 45%, transparent)); }

/* It swings once when sound starts, so the way to stop it has been seen. A
   system that asks for less motion still sees it swing, a little; a reader
   who stilled the place sees it simply lit. */
@keyframes swing {
  0%   { transform: rotate(0deg); }
  14%  { transform: rotate(calc(var(--amp, 1) * 16deg)); }
  34%  { transform: rotate(calc(var(--amp, 1) * -11deg)); }
  54%  { transform: rotate(calc(var(--amp, 1) * 6deg)); }
  74%  { transform: rotate(calc(var(--amp, 1) * -3deg)); }
  100% { transform: rotate(0deg); }
}
.bell.is-swinging .bell__body { animation: swing 1.6s cubic-bezier(0.3, 0.6, 0.4, 1); }
html[data-motion='calm'] .bell { --amp: 0.45; }
html[data-motion='still'] .bell.is-swinging .bell__body { animation: none; }

/* The pennant: a pole and a strip of cloth, slack against the pole in still
   water, half up while the system decides, flying when everything moves. */
.pennant__pole { fill: none; stroke: var(--iron); stroke-width: 1.8; stroke-linecap: round; }
.pennant__finial { fill: url(#brass); }
.pennant__flag {
  fill: url(#madder);
  stroke: color-mix(in oklch, var(--madder) 55%, black);
  stroke-width: 0.5;
  /* the cloth stays on the pole: it droops and shortens from its hoist
     rather than turning about a corner */
  transform-box: view-box;
  transform-origin: 10px 14px;
  transform: skewY(var(--lift, 28deg)) scaleX(var(--reach, 0.86));
  transition: transform 700ms cubic-bezier(0.3, 0.7, 0.3, 1);
}
/* read off the root, which the head writes before the first frame */
html[data-pennant='still']  .pennant { --lift: 68deg; --reach: 0.4; }
html[data-pennant='follow'] .pennant { --lift: 28deg; --reach: 0.86; }
html[data-pennant='move']   .pennant { --lift: 3deg;  --reach: 1; }
@keyframes flutter {
  0%, 100% { transform: skewY(var(--lift)) scaleX(var(--reach)); }
  50%      { transform: skewY(calc(var(--lift) + 5deg)) scaleX(calc(var(--reach) * 0.93)); }
}
/* in the wind it moves, and the wind is whatever the reader allowed */
html[data-motion='full'][data-pennant='follow'] .pennant__flag { animation: flutter 3.6s ease-in-out infinite; }
html[data-motion='full'][data-pennant='move'] .pennant__flag { animation: flutter 2.2s ease-in-out infinite; }

/* ------------------------------------------------------------- the edges */

.edge {
  position: fixed;
  z-index: 15;
  top: 0;
  bottom: 0;
  width: var(--edge-w);
  display: block;
  text-decoration: none;
  color: var(--cream);
  transition: width 360ms cubic-bezier(0.3, 0.7, 0.3, 1);
}
/* a hand's width to aim at on a phone, whatever the strip itself shows */
.edge::after { content: ''; position: absolute; inset: 0 -8px; }
.edge--back { left: 0; }
.edge--back::after { inset: 0 -12px 0 0; }
.edge--next { right: 0; }
.edge--next::after { inset: 0 0 0 -12px; }

/* The right edge is warm because the next room is lit: its light spills
   under the wall, and breathes as a lamp does. */
.edge--next {
  background: linear-gradient(to left,
    color-mix(in oklch, var(--flame) 26%, transparent),
    color-mix(in oklch, var(--flame) 8%, transparent) 55%,
    transparent);
}
.edge--next::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to left, color-mix(in oklch, var(--flame) 30%, transparent), transparent 70%);
  animation: spill 6.5s ease-in-out infinite alternate;
}
@keyframes spill { from { opacity: 0.45; } to { opacity: 1; } }
.edge--next:hover,
.edge--next:focus-visible { width: clamp(26px, 2.6vw, 44px); }
.edge--next:focus-visible { outline-offset: -4px; }

/* the name of the next room, in that room's own lettering, as the hand
   reaches for it */
.edge__name {
  position: absolute;
  right: calc(100% + 12px);
  top: 50%;
  translate: 6px -50%;
  white-space: nowrap;
  pointer-events: none;
  font-family: var(--face);
  font-style: var(--face-style);
  text-transform: var(--face-case);
  letter-spacing: var(--face-track);
  font-variant: var(--face-variant);
  font-size: clamp(1.15rem, 1.5vw, 1.6rem);
  line-height: 1;
  color: var(--cream);
  text-shadow: 0 1px 12px oklch(0% 0 0 / 0.85), 0 0 2px oklch(0% 0 0 / 0.9);
  opacity: 0;
  transition: opacity 280ms ease, translate 380ms cubic-bezier(0.3, 0.7, 0.3, 1);
}
.edge--next:hover .edge__name,
.edge--next:focus-visible .edge__name { opacity: 1; translate: 0 -50%; }
html:not([data-motion='full']) .edge__name { translate: 0 -50%; }

/* The left edge is the dark street outside, and only shows itself to a
   hand that finds it. */
.edge--back {
  background: linear-gradient(to right, oklch(70% 0.03 250 / 0.07), transparent);
  transition: background 300ms ease, width 360ms cubic-bezier(0.3, 0.7, 0.3, 1);
}
.edge--back:hover {
  width: clamp(20px, 2vw, 32px);
  background: linear-gradient(to right, oklch(75% 0.04 250 / 0.2), transparent);
}

/* ----------------------------------------------------------- the door out */

/* A room dresses its own door. Until it does, a door is a tall arch of warm
   light at the right of the room with its name under it, and a shut one is
   the same arch dark with a seam of light down it. Shut, the words stay for a
   screen reader and leave the screen. */
.door-out {
  position: fixed;
  z-index: 12;
  right: calc(var(--edge-w) + clamp(20px, 5vw, 80px));
  bottom: clamp(48px, 12vh, 140px);
  width: clamp(48px, 4.6vw, 76px);
  aspect-ratio: 1 / 2.1;
  border-radius: 999px 999px 3px 3px;
  display: block;
  color: var(--cream);
  text-decoration: none;
  background:
    radial-gradient(90% 60% at 50% 70%, color-mix(in oklch, var(--flame) 85%, white), var(--flame) 45%, var(--ember));
  box-shadow:
    0 0 24px 4px color-mix(in oklch, var(--flame) 40%, transparent),
    0 0 90px 20px color-mix(in oklch, var(--flame) 14%, transparent);
  transition: box-shadow 400ms ease, filter 400ms ease;
}
a.door-out:hover, a.door-out:focus-visible {
  filter: brightness(1.12);
  box-shadow:
    0 0 30px 8px color-mix(in oklch, var(--flame) 55%, transparent),
    0 0 120px 30px color-mix(in oklch, var(--flame) 20%, transparent);
}
/* hung from the door's right-hand post, so it can never run off the room */
.door-out__label {
  position: absolute;
  top: calc(100% + 12px);
  right: 0;
  white-space: nowrap;
  text-align: right;
  font-style: italic;
  font-size: 1rem;
  color: var(--cream);
  text-shadow: 0 1px 8px oklch(0% 0 0 / 0.9);
  opacity: 0.85;
  transition: opacity 240ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .door-out__label { opacity: 0; }
  a.door-out:hover .door-out__label,
  a.door-out:focus-visible .door-out__label { opacity: 1; }
}
.door-out[data-shut] {
  background: linear-gradient(to right, transparent 49%, color-mix(in oklch, var(--flame) 70%, transparent) 50%, transparent 51%),
    color-mix(in oklch, var(--ground) 70%, black);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--flame) 14%, transparent);
  cursor: default;
}
.door-out[data-shut] .door-out__label {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
}

/* ------------------------------------------------ the quieter way out */

/* Where a row has one, a second way out sits after the door: to the pages
   about the thing, not the thing. Until a room dresses it, it is a line of
   small dim words at the foot of the room under the door, underlined and
   never lit, so the door stays the way on. Its hit area is taller than its
   words, for a thumb. Shut, it goes with the door and leaves the screen and
   the reading order both, since words that open nothing are no help. */
.more-out {
  position: fixed;
  z-index: 12;
  right: calc(var(--edge-w) + clamp(20px, 5vw, 80px));
  bottom: calc(6px + env(safe-area-inset-bottom));
  min-height: 32px;
  display: flex;
  align-items: center;
  padding: 0 4px;
  font-size: 0.85rem;
  font-style: italic;
  color: var(--dim);
  text-decoration: none;
  text-shadow: 0 1px 6px oklch(0% 0 0 / 0.9);
  transition: color 200ms ease;
}
/* the line is drawn under the words themselves: WebKit does not carry an
   underline down from a flex box to what is in it */
.more-out__label {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in oklch, var(--cream) 30%, transparent);
  text-underline-offset: 0.22em;
  transition: text-decoration-color 200ms ease;
}
a.more-out:hover, a.more-out:focus-visible { color: var(--cream); }
a.more-out:hover .more-out__label,
a.more-out:focus-visible .more-out__label { text-decoration-color: currentColor; }
.more-out[data-shut] { display: none; }

/* --------------------------------------------------------------- the veil */

/* The room comes up out of its own ground colour, and goes out into the
   ground of wherever the reader is going. A page with no script has no veil
   at all; a page whose script never runs lifts it after 2.5 s anyway. */
.veil {
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
  background: var(--veil, var(--ground));
  opacity: 0;
  transition: opacity 420ms ease;
}
html.js:not([data-shown]) .veil { opacity: 1; transition: none; }
html[data-leaving] .veil { opacity: 1; }
html[data-motion='calm'] .veil { transition-duration: 220ms; }
html[data-motion='still'] .veil { transition: none; }

/* ----------------------------------------------------------- motion held */

/* the pause a reader is owed: whatever is animating stops where it is */
html[data-motion='still'] *,
html[data-motion='still'] *::before,
html[data-motion='still'] *::after { animation-play-state: paused !important; }

/* ------------------------------------------------------------- the phone */

@media (max-width: 560px) {
  .token { width: 40px; }
  .token svg { width: 30px; height: 39px; }
  .brush { width: 50px; height: 56px; }
  .mark { width: 46px; height: 52px; }
}
