/* ==========================================================================
   NUMEN, the tent.
   Cold blue night outside. Warm candlelight the moment the curtain parts.
   ========================================================================== */

:root {
  --night: #070b18;
  --night-2: #0d1426;
  --umber: #170d09;
  --umber-2: #2a160e;
  --velvet: #5a1220;
  --velvet-2: #7c1e2c;
  --candle: #e8b457;
  --candle-hi: #ffd489;
  --ember: #c9642b;
  --cream: #f0e2c8;
  --ash: #c0ab8d;
  /* Quiet, not faint. This is the colour of everything that is offered without
     being pressed on you — "back", "say more", the secondary half of a choice —
     and it was set purely by eye at #8d7c66, which measures 4.0:1 against the
     brightest wall it can land on: under AA, on text you are meant to press.
     Lifted until the worst case clears with room to spare. It still reads as the
     quieter voice, because quiet here is carried by size and weight and the
     handwriting, not by being hard to see. */
  --ash-dim: #9c8a72;

  --voice: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --hand: "Caveat", "Segoe Script", cursive;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--night);
  color: var(--cream);
  font-family: var(--voice);
  /* Cormorant defaults to old-style figures, which turn "11.1%" into
     something that reads like roman numerals. Numbers are the whole point
     here, so they stay lining. */
  font-variant-numeric: lining-nums;
  font-size: 18px;
  line-height: 1.6;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body.is-inside {
  background: var(--umber);
}

::selection {
  background: rgba(232, 180, 87, 0.32);
  color: var(--cream);
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

img {
  max-width: 100%;
  display: block;
}

/* ── Stage ─────────────────────────────────────────────────────────────── */

#tent {
  position: fixed;
  inset: 0;
  overflow: hidden;
}

.scene {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 1s var(--ease),
    visibility 1s;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
}

.scene.is-on {
  opacity: 1;
  visibility: visible;
}

/* Fixed, not absolute. The scene scrolls internally, and an absolute
   background scrolls away with it: past one screen height the tent simply
   stopped existing and everything below sat in an empty brown field. The room
   has to still be there however far down the reading goes. */
/* The bleed is a SCALE, not a negative inset.
 *
 * At inset:-4% the backdrop's box hangs 4% past the scene on every side, and a
 * scrolling scene counts that as something to scroll to: every screen in the
 * tent had about 55px of phantom scroll under it, with the content pinned and
 * only the painting moving. Scaling instead gives exactly the same picture — a
 * cover fit of a box 8% larger, centred, is a cover fit of the box scaled by
 * 1.08 — while the layout box stays exactly the viewport.
 *
 * .artwork still mirrors the old 108% geometry and still lands on the doorway,
 * because the two constructions are the same rectangle. */
.scene__bg {
  position: fixed;
  inset: 0;
  --bleed: 1.08;
  transform: scale(var(--bleed));
  background-size: cover;
  background-position: center;
  will-change: transform;
  transition: filter 1.4s ease;
}

/* When the reading is done she turns the lamp down so you can read in peace. */
#scene-table.is-settled .scene__bg {
  filter: brightness(0.38) saturate(0.86);
}

/* Optional motion. A dropped-in video fades over its still; if the file is not
   there the element removes itself and the still simply stays. */
.scene__bg video,
.slot__face--front video,
.card-detail video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.4s ease;
  pointer-events: none;
}

.scene__bg video.is-on,
.slot__face--front video.is-on,
.card-detail video.is-on {
  opacity: 1;
}

.card-detail {
  position: relative;
}

.curtain-half video {
  position: absolute;
  top: 0;
  height: 100%;
  width: 200%;
  max-width: none;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.8s ease;
}

.curtain-half video.is-on {
  opacity: 1;
}

.curtain-half--l video { left: 0; }
.curtain-half--r video { right: 0; }

.curtain-half {
  overflow: hidden;
}

/* Everything that is not the backdrop sits above the light and smoke. */
.scene__inner {
  position: relative;
  z-index: 4;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(24px, 5vw, 72px);
}

/* ── Atmosphere: vignette, grain, candlelight ──────────────────────────── */

.vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  background:
    radial-gradient(ellipse at 50% 55%, transparent 34%, rgba(8, 4, 2, 0.72) 100%);
}

.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 60;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-shift 6s steps(6) infinite;
}

@keyframes grain-shift {
  0% { transform: translate(0, 0); }
  20% { transform: translate(-3%, 2%); }
  40% { transform: translate(2%, -3%); }
  60% { transform: translate(-2%, -2%); }
  80% { transform: translate(3%, 1%); }
  100% { transform: translate(0, 0); }
}

/* The candle: a warm light that never sits still. */
.candlelight {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  background:
    radial-gradient(circle at 26% 28%, rgba(255, 196, 110, 0.3), transparent 46%),
    radial-gradient(circle at 74% 26%, rgba(255, 178, 92, 0.26), transparent 44%);
  animation: flicker 3.7s ease-in-out infinite;
  mix-blend-mode: screen;
}

@keyframes flicker {
  0%, 100% { opacity: 0.86; transform: scale(1); }
  12% { opacity: 1; transform: scale(1.012); }
  23% { opacity: 0.78; }
  41% { opacity: 0.97; transform: scale(1.006); }
  57% { opacity: 0.83; }
  68% { opacity: 1; transform: scale(1.014); }
  84% { opacity: 0.88; }
}

/* A little of the candle travels with the visitor. */
#glow {
  position: fixed;
  left: 0;
  top: 0;
  width: 460px;
  height: 460px;
  z-index: 7;
  pointer-events: none;
  opacity: 0;
  border-radius: 50%;
  mix-blend-mode: screen;
  background: radial-gradient(
    circle,
    rgba(255, 196, 110, 0.16) 0%,
    rgba(255, 168, 80, 0.07) 34%,
    transparent 68%
  );
  transition: opacity 1.8s ease;
  will-change: transform;
}

body.is-inside #glow {
  opacity: 1;
}

@media (hover: none), (prefers-reduced-motion: reduce) {
  #glow {
    display: none;
  }
}

/* A card already face up can be picked up and read again. */
.card.is-readable {
  cursor: pointer;
}

.card.is-readable:hover .slot {
  transform: rotateY(180deg) translateY(-6px);
}

#embers {
  position: fixed;
  inset: 0;
  z-index: 8;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.6s ease;
}

body.is-inside #embers {
  opacity: 1;
}

/* ── The reader's voice ────────────────────────────────────────────────── */

.voice {
  font-family: var(--voice);
  font-weight: 300;
  font-size: clamp(1.4rem, 1rem + 1.7vw, 2.5rem);
  line-height: 1.34;
  color: var(--cream);
  text-shadow: 0 2px 26px rgba(0, 0, 0, 0.6);
  max-width: 22ch;
}

.voice--wide {
  max-width: 30ch;
}

.voice em {
  font-style: italic;
  color: var(--candle-hi);
}

.said {
  opacity: 0;
  transform: translateY(12px);
  animation: said 1.45s var(--ease) forwards;
}

@keyframes said {
  to { opacity: 1; transform: none; }
}

.label {
  font-family: var(--voice);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--candle);
  opacity: 0.85;
}

.hand {
  font-family: var(--hand);
  font-size: 1.5rem;
  color: var(--candle-hi);
  letter-spacing: 0.02em;
}

.dim {
  color: var(--ash-dim);
  font-size: 0.98rem;
  letter-spacing: 0.01em;
}

/* ── Offers (the things you can say back) ──────────────────────────────── */

/* ── Things you can do ──────────────────────────────────────────────────────
   Not buttons. A pill with a border and letterspaced caps is chrome, and chrome
   is the one thing in here that is obviously software. These are written in her
   hand, sitting on the table, with a small mark that draws itself as you come
   near. They are still real <button> elements so a keyboard still works; only
   the appearance stops being a button. */
.offer {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 0.55em;
  padding: 0.35em 0;
  border: none;
  background: none;
  font-family: var(--hand);
  font-size: clamp(1.15rem, 2.2vh, 1.45rem);
  font-weight: 400;
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--candle);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
  cursor: pointer;
  transition: color 0.4s var(--ease);
}

/* A pool of shade the words carry with them.
 *
 * Losing the button did not lose the problem it was solving: a bordered pill had
 * a dark fill under the text, and once that went, "go on" was gold sitting
 * directly on an oil painting. On the tablecloth, where the candles pool on the
 * cream, that measured 1.40:1 — the words were genuinely close to invisible, and
 * only in the places the painting happens to be bright, which is why it read as
 * intermittent rather than broken.
 *
 * So the same device the rooms use, at the scale of a line: shade, feathered out
 * to nothing well before it has an edge you could point at. It deepens the dark
 * where the painting is already dark and cuts the glare where it is bright, and
 * it never becomes a shape. Measured worst case afterwards: 5.4:1.
 *
 * Behind the text but inside .offer's own box, so it travels with the words
 * wherever the flow puts them. Where a real plate already exists (the reading
 * sits on one) it is painted under that plate and simply does not show, which is
 * exactly right — the shade is only wanted where nothing else is providing it. */
.offer::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -0.7em -1.5em;
  background: radial-gradient(
    ellipse at 50% 50%,
    rgba(9, 5, 3, 0.88) 0%,
    rgba(9, 5, 3, 0.72) 44%,
    rgba(9, 5, 3, 0) 76%
  );
  filter: blur(5px);
  pointer-events: none;
}

/* The mark: a short rule that grows toward the words when you approach. */
.offer::before {
  content: "";
  width: 0.9em;
  height: 1px;
  background: currentColor;
  opacity: 0.55;
  transform-origin: left center;
  transition:
    width 0.45s var(--ease),
    opacity 0.45s var(--ease);
}

.offer:hover,
.offer:focus-visible {
  color: var(--candle-hi);
  outline: none;
}

.offer:hover::before,
.offer:focus-visible::before {
  width: 2em;
  opacity: 1;
}

.offer:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.3em;
  text-decoration-thickness: 1px;
}

.offer:active {
  opacity: 0.75;
}

/* The quieter half of a choice: the one you take by not taking the other. */
.offer--quiet {
  font-family: var(--voice);
  font-size: clamp(0.78rem, 1.5vh, 0.9rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ash-dim);
}

.offer--quiet::before {
  width: 0.6em;
  opacity: 0.35;
}

.offer--quiet:hover,
.offer--quiet:focus-visible {
  color: var(--ash);
}

.offer--quiet:hover::before,
.offer--quiet:focus-visible::before {
  width: 1.2em;
  opacity: 0.7;
}

/* ── 1. The threshold ──────────────────────────────────────────────────── */

/* Where the painting is pinned.
 *
 * The doorway sits at 69.6% across and 54.6% down the plate. Setting
 * background-position to exactly those numbers means the doorway lands on the
 * same fraction of the frame at EVERY window shape, because background-position
 * aligns the image's p point with the container's p point. Centre it instead and
 * the doorway wanders as the cover crop changes: a couple of hundred pixels
 * between a 16:9 window and a square one, and clean off the right edge on a
 * phone, where the whole tent used to be cropped away.
 *
 * One pair of numbers drives the still, the video and the hotspot, so they
 * cannot drift apart. */
#scene-threshold {
  --plate-x: 69.2%;
  --plate-y: 54%;
}

#scene-threshold .scene__bg {
  background-position: var(--plate-x) var(--plate-y);
}

#scene-threshold .scene__bg video {
  object-position: var(--plate-x) var(--plate-y);
}

/* The tent glows on the right of the plate, so the visitor's side of the
   conversation sits in the dark on the left, with the night pulled darker
   behind it so every word stays readable. */
#scene-threshold .vignette {
  background:
    linear-gradient(
      100deg,
      rgba(4, 7, 16, 0.96) 0%,
      rgba(4, 7, 16, 0.9) 26%,
      rgba(4, 7, 16, 0.42) 52%,
      transparent 72%
    ),
    radial-gradient(ellipse at 60% 55%, transparent 30%, rgba(8, 4, 2, 0.7) 100%);
}

#scene-threshold .scene__inner {
  align-items: flex-start;
  text-align: left;
  width: min(1400px, 100%);
  margin: 0 auto;
}

#scene-threshold .scene__inner > * {
  width: min(540px, 100%);
}

.sign {
  margin-bottom: auto;
  padding-top: clamp(12px, 5vh, 60px);
}

.sign__name {
  text-align: left;
}

.sign__rule {
  margin-left: 0;
}

@media (max-width: 860px) {
  #scene-threshold .vignette {
    background:
      linear-gradient(to bottom, rgba(4, 7, 16, 0.6) 0%, rgba(4, 7, 16, 0.9) 62%, rgba(4, 7, 16, 0.97) 100%),
      radial-gradient(ellipse at 50% 40%, transparent 30%, rgba(8, 4, 2, 0.7) 100%);
  }
  #scene-threshold .scene__inner {
    align-items: center;
    text-align: center;
    justify-content: flex-end;
  }
  .sign__name,
  .sign__rule {
    text-align: center;
    margin-left: auto;
    margin-right: auto;
  }
}

.sign__name {
  font-family: var(--voice);
  font-weight: 300;
  font-size: clamp(2.6rem, 1.4rem + 4.6vw, 5.4rem);
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  color: var(--candle-hi);
  text-shadow: 0 0 44px rgba(232, 180, 87, 0.5);
  animation: sign-glow 5s ease-in-out infinite;
}

@keyframes sign-glow {
  0%, 100% { text-shadow: 0 0 40px rgba(232, 180, 87, 0.42); }
  50% { text-shadow: 0 0 62px rgba(255, 212, 137, 0.72); }
}

.sign__rule {
  width: min(320px, 60vw);
  height: 1px;
  margin: 1.4rem auto;
  background: linear-gradient(90deg, transparent, rgba(232, 180, 87, 0.7), transparent);
}

.threshold__call {
  margin: auto 0 2.2rem;
  font-style: italic;
}

.threshold__foot {
  margin-top: auto;
  padding-bottom: clamp(8px, 3vh, 32px);
}

/* ── 2. The curtain ────────────────────────────────────────────────────── */

#scene-curtain {
  z-index: 40;
  pointer-events: none;
}

.curtain-half {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 50.6%;
  background-image: url("../assets/scene/curtain.webp");
  background-size: 200% 100%;
  will-change: transform;
  transition: transform 1.9s cubic-bezier(0.7, 0, 0.24, 1);
}

.curtain-half--l {
  left: 0;
  background-position: left center;
  box-shadow: 26px 0 60px rgba(0, 0, 0, 0.6);
}

.curtain-half--r {
  right: 0;
  background-position: right center;
  box-shadow: -26px 0 60px rgba(0, 0, 0, 0.6);
}

#scene-curtain.is-parting .curtain-half--l {
  transform: translateX(-101%);
}

#scene-curtain.is-parting .curtain-half--r {
  transform: translateX(101%);
}

.curtain-glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 50%, rgba(255, 196, 110, 0.9), transparent 58%);
  opacity: 0;
  transition: opacity 1.5s ease;
}

#scene-curtain.is-parting .curtain-glow {
  opacity: 1;
}

/* The light behind the curtain has to go out on its own, before the curtain is
   taken off the screen. It used to fade up over a second and a half, sit at full
   brightness, and then be cut mid-glow when the element was hidden, which read
   as a chop rather than a room being revealed. */
#scene-curtain.is-parting.is-passing .curtain-glow {
  opacity: 0;
  transition: opacity 1.15s ease;
}

/* ── 3. The table ──────────────────────────────────────────────────────── */

.table__grid {
  display: grid;
  gap: clamp(20px, 4vw, 56px);
  align-items: center;
  width: min(1180px, 100%);
  margin: 0 auto;
}

@media (min-width: 900px) {
  .table__grid--ask {
    grid-template-columns: 1.05fr 0.95fr;
  }
}

.ask {
  border: 1px solid rgba(232, 180, 87, 0.3);
  background: rgba(18, 10, 7, 0.82);
  backdrop-filter: blur(9px);
  padding: clamp(22px, 3.4vw, 40px);
  box-shadow: 0 40px 90px -40px rgba(0, 0, 0, 0.98);
}

/* Her voice falls across a lit table, so it carries its own shadow with it. */
.table__grid--ask .stack {
  position: relative;
  isolation: isolate;
}

.table__grid--ask .stack::before {
  content: "";
  position: absolute;
  inset: -14% -18%;
  z-index: -1;
  background: radial-gradient(
    ellipse at 42% 50%,
    rgba(9, 5, 3, 0.88) 0%,
    rgba(9, 5, 3, 0.62) 46%,
    transparent 74%
  );
  pointer-events: none;
}

.field + .field {
  margin-top: 1.6rem;
}

.field__label {
  display: block;
  font-family: var(--voice);
  font-size: 1.16rem;
  color: var(--cream);
  margin-bottom: 0.15rem;
}

.field__hint {
  display: block;
  color: var(--ash-dim);
  font-size: 0.92rem;
  font-style: italic;
  margin-bottom: 0.5rem;
}

.field input {
  width: 100%;
  padding: 0.6rem 0 0.5rem;
  font-family: var(--hand);
  font-size: 1.9rem;
  color: var(--candle-hi);
  background: transparent;
  border: none;
  border-bottom: 1px solid rgba(232, 180, 87, 0.34);
  outline: none;
  transition: border-color 0.35s ease;
}

.field input::placeholder {
  color: rgba(141, 124, 102, 0.6);
}

.field input:focus {
  border-bottom-color: var(--candle-hi);
}

/* The date control needs its own coaxing to look like ink. */
.field input[type="date"] {
  color-scheme: dark;
  font-size: 1.5rem;
}

.err {
  margin-top: 1rem;
  color: #ff9d7a;
  font-size: 0.96rem;
  font-style: italic;
}

/* ── 4. The spread ─────────────────────────────────────────────────────── */

.spread {
  display: flex;
  justify-content: center;
  gap: clamp(8px, 1.6vw, 22px);
  margin: 0 auto;
  perspective: 1600px;
}

/* The card that is dealt and the card that turns are deliberately two
   different elements. A filter or a fractional opacity on the turning element
   flattens its 3D context, which quietly breaks backface culling and leaves
   the back of the card facing you forever. */
.card {
  width: clamp(88px, 13vw, 176px);
  opacity: 0;
  transform: translateY(30px) scale(0.94);
  transition:
    opacity 0.85s var(--ease),
    transform 1.05s var(--ease);
  perspective: 1400px;
}

.card.is-dealt {
  opacity: 1;
  transform: none;
}

.slot {
  position: relative;
  width: 100%;
  aspect-ratio: 2 / 3;
  transform-style: preserve-3d;
  transition:
    transform 1.15s var(--ease),
    box-shadow 0.7s ease;
}

.slot.is-turned {
  transform: rotateY(180deg);
}

.card.is-current .slot {
  box-shadow:
    0 0 0 1px rgba(255, 212, 137, 0.8),
    0 26px 64px -18px rgba(232, 180, 87, 0.72);
}

.slot__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  border: 1px solid rgba(232, 180, 87, 0.3);
  box-shadow: 0 24px 50px -26px rgba(0, 0, 0, 0.95);
  overflow: hidden;
}

.slot__face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.slot__face--back {
  background-image: url("../assets/scene/card-back.webp");
  background-size: cover;
  background-position: center;
}

.slot__face--front {
  transform: rotateY(180deg);
}

.slot__num {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.5rem 0.6rem;
  background: linear-gradient(to top, rgba(10, 6, 4, 0.95), transparent);
  font-family: var(--voice);
  font-size: 1.5rem;
  color: var(--candle-hi);
  text-align: center;
}

.slot__caption {
  margin-top: 0.45rem;
  text-align: center;
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ash-dim);
  min-height: 2.2em;
  line-height: 1.3;
}

.card.is-current .slot__caption {
  color: var(--candle);
}

/* The reading that belongs to the card just turned. */
.telling {
  width: min(760px, 100%);
  margin: clamp(16px, 2.6vh, 30px) auto 0;
  padding: clamp(20px, 3vw, 36px) clamp(20px, 3.4vw, 40px);
  text-align: center;
  border: 1px solid rgba(232, 180, 87, 0.2);
  /* The reading's own plate. At 0.84 the candlelit cream of the tablecloth came
     through it far enough to cost the quieter lines their contrast. */
  background: rgba(12, 6, 4, 0.9);
  backdrop-filter: blur(9px);
  box-shadow: 0 40px 100px -50px rgba(0, 0, 0, 0.98);
}

.telling__line {
  font-family: var(--voice);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(1.5rem, 1.05rem + 1.9vw, 2.6rem);
  line-height: 1.28;
  color: var(--cream);
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.7);
}

.telling__body {
  margin-top: 1.1rem;
  color: var(--ash);
  font-size: 1.06rem;
  line-height: 1.72;
}

.telling__marks {
  margin-top: 1rem;
  display: flex;
  gap: 0.5rem;
  justify-content: center;
  flex-wrap: wrap;
}

.mark {
  font-family: var(--voice);
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.36em 0.9em;
  border: 1px solid rgba(232, 180, 87, 0.4);
  color: var(--candle);
}

.mark--debt {
  border-color: rgba(201, 100, 43, 0.6);
  color: var(--ember);
}

/* ── The workings (her handwriting) ────────────────────────────────────── */

.workings {
  text-shadow: 0 2px 18px rgba(8, 4, 2, 0.95), 0 0 40px rgba(8, 4, 2, 0.8);
  font-family: var(--hand);
  font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2.2rem);
  color: var(--candle-hi);
  opacity: 0.92;
  letter-spacing: 0.03em;
}

.workings span {
  opacity: 0;
  animation: said 0.6s ease forwards;
}

/* ── 5. What is left on the table ──────────────────────────────────────── */

.after {
  width: min(1080px, 100%);
  margin: 0 auto;
  padding: clamp(60px, 10vh, 120px) 0;
}

.plate {
  border: 1px solid rgba(232, 180, 87, 0.22);
  background: rgba(13, 7, 5, 0.9);
  backdrop-filter: blur(7px);
  padding: clamp(20px, 3vw, 34px);
}

.stat-row {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  margin-top: 1.6rem;
}

.stat__big {
  font-family: var(--voice);
  font-size: 2.6rem;
  color: var(--candle-hi);
  line-height: 1;
}

.stat__note {
  color: var(--ash);
  margin-top: 0.5rem;
  font-size: 0.98rem;
}

.kin {
  margin-top: 1.2rem;
  border-top: 1px solid rgba(232, 180, 87, 0.18);
}

.kin__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.2rem;
  justify-content: space-between;
  align-items: baseline;
  padding: 0.62rem 0;
  border-bottom: 1px solid rgba(232, 180, 87, 0.12);
}

.kin__name {
  font-size: 1.12rem;
  color: var(--cream);
}

.kin__note {
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-dim);
}

/* Objects on the table (the rest of the tent). */
.objects {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  margin-top: 2rem;
}

.object {
  text-align: left;
  border: 1px solid rgba(232, 180, 87, 0.2);
  background: rgba(30, 15, 10, 0.86);
  padding: 1.5rem;
  transition:
    border-color 0.45s var(--ease),
    background 0.45s var(--ease),
    transform 0.45s var(--ease);
}

.object:hover {
  border-color: rgba(232, 180, 87, 0.62);
  background: rgba(90, 18, 32, 0.42);
  transform: translateY(-4px);
}

.object__name {
  font-size: 1.34rem;
  color: var(--candle-hi);
}

.object__line {
  margin-top: 0.5rem;
  color: var(--ash);
  font-size: 0.99rem;
  line-height: 1.6;
}

/* ── Deck / ledger / seasons drawers ───────────────────────────────────── */

/* ── The other rooms ────────────────────────────────────────────────────────
   These used to be a panel that slid over the table with a blurred slab behind
   it, which is the shape of a dialog box and reads as one. Each is now a scene
   in its own right, with its own painted wall, so going to one feels like
   turning to face another part of the tent rather than opening a window. */
.scene--room .scene__inner {
  width: min(1120px, 100%);
  margin: 0 auto;
  justify-content: flex-start;
  padding: clamp(20px, 3.5vh, 44px) clamp(20px, 5vw, 48px);
}

/* Rooms hold long readings, so this is the one place that may scroll, and it
   scrolls inside the room rather than dragging the whole tent with it. */
.room {
  min-height: 0;
}

/* The way back. Small, always in the same corner, never a "close" X. */
.room__back {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: clamp(0.9rem, 2.2vh, 1.8rem);
  padding: 0.45rem 0.2rem;
  border: none;
  background: none;
  color: var(--ash-dim);
  font-family: var(--voice);
  font-size: 0.74rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.4s var(--ease);
}

.room__back:hover,
.room__back:focus-visible {
  color: var(--candle);
}

.room__back-mark {
  width: 18px;
  height: 1px;
  background: currentColor;
  position: relative;
}

.room__back-mark::before {
  content: "";
  position: absolute;
  left: 0;
  top: -3px;
  width: 7px;
  height: 7px;
  border-left: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: rotate(45deg);
}

/* The lamp belongs to the table; a room has its own way out. This has to be
   more specific than `.lamp.is-on` or the corner controls follow you in. */
body.in-room .lamp.is-on {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Twelve cards, all of them, on one screen.
 *
 * auto-fill at 148px gave five across and three rows, and the room came to 1028px
 * in a 742px window — you met the deck by scrolling down it. This is a contact
 * sheet: the whole point is seeing the twelve together and picking one, so the
 * answer is to fit them, not to deal them out over two pages. */
.deck-grid {
  display: grid;
  gap: clamp(8px, 1.2vw, 16px);
  grid-template-columns: repeat(6, minmax(0, 1fr));
  margin-top: 1.4rem;
}

@media (max-width: 760px) {
  .deck-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 460px) {
  .deck-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.deck-card {
  position: relative;
  display: block;
  /* so the caption can be sized from the card rather than from the root, and
     stays in proportion at six across, four across or three */
  container-type: inline-size;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border: 1px solid rgba(232, 180, 87, 0.24);
  transition: transform 0.5s var(--ease), border-color 0.5s var(--ease);
}

.deck-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.3s var(--ease);
}

.deck-card:hover {
  transform: translateY(-6px);
  border-color: rgba(232, 180, 87, 0.75);
}

.deck-card:hover img {
  transform: scale(1.07);
}

.deck-card__foot {
  position: absolute;
  inset: auto 0 0 0;
  padding: 5cqw 4.5cqw 4.5cqw;
  background: linear-gradient(to top, rgba(10, 6, 4, 0.96), transparent);
}

.deck-card__n {
  font-size: 20cqw;
  color: var(--candle-hi);
  line-height: 1;
}

.deck-card__t {
  font-size: 10cqw;
  line-height: 1.25;
  color: var(--ash);
}

/* ── The cards she could not lay ───────────────────────────────────────────
   These never turn over. They are dealt face down and they stay that way, so
   they are styled as an absence: darker than the deck, tilted, waiting. */
.shut-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(10px, 1.8vw, 22px);
  margin-top: 2.4rem;
  padding: 0.6rem 0;
}

.shut {
  position: relative;
  width: clamp(74px, 9vw, 116px);
  aspect-ratio: 2 / 3;
  padding: 0;
  border: none;
  background: none;
  transform: rotate(var(--tilt, 0deg));
  transition: transform 0.7s var(--ease), filter 0.7s ease;
  filter: brightness(0.52) saturate(0.6);
}

.shut__face {
  position: absolute;
  inset: 0;
  background-image: url("../assets/scene/card-back.webp");
  background-size: cover;
  background-position: center;
  border: 1px solid rgba(232, 180, 87, 0.22);
  box-shadow: 0 20px 44px -22px rgba(0, 0, 0, 0.95);
}

/* A sliver of the card that is never shown, catching a little light. */
.shut__edge {
  position: absolute;
  inset: auto 0 0 0;
  height: 3px;
  background: linear-gradient(90deg, transparent, rgba(232, 180, 87, 0.5), transparent);
  opacity: 0;
  transition: opacity 0.6s ease;
}

.shut:hover {
  filter: brightness(0.78) saturate(0.85);
  transform: rotate(var(--tilt, 0deg)) translateY(-7px);
}

.shut:hover .shut__edge,
.shut.is-picked .shut__edge {
  opacity: 1;
}

.shut.is-picked {
  filter: brightness(0.95) saturate(1);
  transform: rotate(var(--tilt, 0deg)) translateY(-11px);
}

.shut.is-picked .shut__face {
  border-color: rgba(255, 212, 137, 0.8);
  box-shadow:
    0 0 0 1px rgba(255, 212, 137, 0.5),
    0 26px 60px -20px rgba(232, 180, 87, 0.6);
}

/* Locked: the cards are visible but out of reach. */
.ask-plate {
  margin-top: 2.6rem;
  border-color: rgba(232, 180, 87, 0.38);
}

.entry + .entry {
  margin-top: 1px;
}

.entry {
  border: 1px solid rgba(232, 180, 87, 0.16);
  padding: 1.2rem 1.4rem;
  background: rgba(13, 7, 5, 0.82);
}

.entry summary {
  cursor: pointer;
  list-style: none;
  font-size: 1.24rem;
  color: var(--candle-hi);
  display: flex;
  gap: 0.7rem;
  align-items: baseline;
}

.entry summary::-webkit-details-marker {
  display: none;
}

.entry summary::before {
  content: "+";
  color: var(--candle);
  transition: transform 0.4s var(--ease);
  display: inline-block;
}

.entry[open] summary::before {
  transform: rotate(45deg);
}

.entry__body {
  margin-top: 0.9rem;
  color: var(--ash);
  line-height: 1.78;
}

.entry__body p + p {
  margin-top: 0.8rem;
}

/* ── The lamp (persistent corner controls) ─────────────────────────────── */

.lamp {
  position: fixed;
  z-index: 55;
  right: clamp(14px, 2.4vw, 26px);
  bottom: clamp(14px, 2.4vw, 26px);
  display: flex;
  gap: 10px;
  align-items: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.8s ease, visibility 0.8s;
}

.lamp.is-on {
  opacity: 1;
  visibility: visible;
}

/* ── Odds and ends ─────────────────────────────────────────────────────── */

.row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
}

.center {
  text-align: center;
  justify-content: center;
}

.stack > * + * {
  margin-top: 1.2rem;
}

.mt-lg {
  margin-top: clamp(16px, 2.6vh, 30px);
}

[hidden] {
  display: none !important;
}

@media (max-width: 640px) {
  body {
    font-size: 17px;
  }
  .spread {
    gap: 6px;
  }
  .slot__caption {
    font-size: 0.54rem;
    letter-spacing: 0.1em;
  }
}

/* Someone who asked for stillness still gets the tent, just without the
   drifting. The scenes still fade so the story can be followed. */
@media (prefers-reduced-motion: reduce) {
  .grain,
  .candlelight,
  .sign__name {
    animation: none;
  }
}

/* ── The other chair ────────────────────────────────────────────────────────
   Two cards set facing each other with a lit gap between them. The gap is the
   reading: she never scores a pairing, she reads the space. */
.chairs {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: clamp(12px, 3vw, 40px);
  margin-top: 2.4rem;
}

.chair-side {
  text-align: center;
}

.chair-card {
  position: relative;
  aspect-ratio: 2 / 3;
  width: min(190px, 100%);
  margin: 0 auto;
  overflow: hidden;
  border: 1px solid rgba(232, 180, 87, 0.3);
  box-shadow: 0 26px 56px -28px rgba(0, 0, 0, 0.95);
}

.chair-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The chair nobody is sitting in yet. */
.chair-card--empty {
  border-style: dashed;
  border-color: rgba(232, 180, 87, 0.24);
  background:
    repeating-linear-gradient(
      -45deg,
      rgba(232, 180, 87, 0.05) 0 8px,
      transparent 8px 16px
    );
  box-shadow: none;
}

.chair-who {
  margin: 0.85rem 0 0;
  font-size: 1.06rem;
  color: var(--cream);
}

.chair-num {
  margin: 0.15rem 0 0;
  font-size: 0.76rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ash-dim);
}

.chair-num .hand {
  font-size: 1.5rem;
  margin-right: 0.3rem;
  letter-spacing: 0;
}

.between {
  display: grid;
  place-items: center;
  height: 100%;
  min-height: 120px;
}

.between__line {
  display: block;
  width: 1px;
  height: min(230px, 26vh);
  background: linear-gradient(
    to bottom,
    transparent,
    rgba(232, 180, 87, 0.75),
    rgba(255, 212, 137, 0.95),
    rgba(232, 180, 87, 0.75),
    transparent
  );
  animation: between-breathe 4.2s ease-in-out infinite;
}

@keyframes between-breathe {
  0%, 100% { opacity: 0.5; transform: scaleY(0.92); }
  50% { opacity: 1; transform: scaleY(1); }
}

.two-ways {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  margin-top: 2.4rem;
}

.two-ways > div {
  border: 1px solid rgba(232, 180, 87, 0.16);
  background: rgba(13, 7, 5, 0.82);
  padding: 1.4rem;
}

.two-ways .entry__body {
  margin-top: 0.7rem;
}

@media (max-width: 560px) {
  .chairs {
    grid-template-columns: 1fr;
  }
  .between {
    min-height: 0;
  }
  .between__line {
    width: min(200px, 60%);
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(255, 212, 137, 0.9), transparent);
  }
}

/* ── The gap, when it is a change rather than a pairing ──────────────────── */
.between__arrow {
  display: block;
  width: 20px;
  height: 20px;
  border-top: 2px solid rgba(255, 212, 137, 0.95);
  border-right: 2px solid rgba(255, 212, 137, 0.95);
  filter: drop-shadow(0 0 10px rgba(232, 180, 87, 0.5));
  transform: rotate(45deg);
  animation: between-breathe 4.2s ease-in-out infinite;
}

/* ── The sealed card ─────────────────────────────────────────────────────── */
.wax-row {
  display: grid;
  place-items: center;
  margin: 2.4rem 0 0;
}

.wax {
  position: relative;
  display: grid;
  place-items: center;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 26%, #8e2130 0%, #6b1826 40%, #400d16 76%, #23070c 100%);
  box-shadow:
    inset 0 3px 7px rgba(255, 176, 170, 0.2),
    inset 0 -9px 20px rgba(0, 0, 0, 0.62),
    0 12px 34px rgba(0, 0, 0, 0.62),
    0 0 46px rgba(124, 30, 44, 0.26);
  /* Wax never sets in a perfect circle. */
  clip-path: polygon(
    50% 0%, 68% 4%, 82% 14%, 95% 30%, 100% 50%, 96% 70%,
    83% 86%, 66% 96%, 50% 100%, 33% 96%, 16% 85%, 5% 69%,
    0% 50%, 5% 30%, 17% 14%, 32% 4%
  );
  animation: wax-settle 7s ease-in-out infinite;
}

.wax::before {
  content: "";
  position: absolute;
  inset: 17px;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.4);
  box-shadow:
    inset 0 1px 2px rgba(0, 0, 0, 0.5),
    0 1px 1px rgba(255, 170, 165, 0.14);
}

.wax__n {
  font-family: var(--hand);
  position: relative;
  font-size: 3.2rem;
  line-height: 1;
  color: #3a0c14;
  text-shadow:
    0 1px 0 rgba(255, 170, 165, 0.3),
    0 2px 3px rgba(255, 170, 165, 0.16),
    0 -1px 1px rgba(0, 0, 0, 0.5);
}

.wax--open {
  background:
    radial-gradient(circle at 34% 26%, #6d2130 0%, #4a1520 44%, #2a0a11 100%);
  filter: saturate(0.78);
  animation: none;
}

/* Broken once, across the middle, by a thumb that was not careful. */
.wax--open::after {
  content: "";
  position: absolute;
  left: -4%;
  right: -4%;
  top: 50%;
  height: 3px;
  background: #17050a;
  box-shadow: 0 2px 2px rgba(255, 170, 165, 0.16);
  transform: rotate(-7deg);
  clip-path: polygon(
    0 40%, 8% 0, 17% 70%, 26% 10%, 35% 80%, 44% 20%,
    53% 90%, 62% 15%, 71% 75%, 80% 5%, 89% 65%, 100% 25%,
    100% 100%, 0 100%
  );
}

.wax--open .wax__n {
  opacity: 0.72;
}

@keyframes wax-settle {
  0%, 100% { transform: rotate(-1.2deg) scale(1); }
  50% { transform: rotate(1.2deg) scale(1.015); }
}

.seal-count {
  margin-top: 1.2rem;
  color: var(--ash-dim);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 0.78rem;
}

.seal-count .hand {
  display: block;
  font-size: 3.2rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--candle);
  margin-bottom: 0.2rem;
}

.sealed-note {
  margin-top: 1.8rem;
  padding: 2rem 2.2rem;
  max-width: 56ch;
  border: 1px solid rgba(232, 180, 87, 0.16);
  background: linear-gradient(160deg, rgba(42, 22, 14, 0.6), rgba(13, 20, 38, 0.4));
}

.sealed-text {
  margin-top: 1rem;
  font-size: 1.45rem;
  line-height: 1.62;
  color: var(--cream);
}

/* ── The almanac ─────────────────────────────────────────────────────────── */
.months,
.days {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1.8rem;
}

.mon,
.day {
  flex: 1 1 auto;
  min-width: 62px;
  display: grid;
  gap: 0.15rem;
  padding: 0.7rem 0.4rem 0.6rem;
  border: 1px solid rgba(232, 180, 87, 0.14);
  background: rgba(13, 20, 38, 0.5);
  color: var(--ash);
  font-family: var(--voice);
  cursor: pointer;
  transition:
    border-color 0.35s var(--ease),
    background 0.35s var(--ease),
    transform 0.35s var(--ease);
}

.mon:hover,
.day:hover {
  border-color: rgba(232, 180, 87, 0.38);
  transform: translateY(-2px);
}

.mon.is-open,
.day.is-open {
  border-color: rgba(232, 180, 87, 0.6);
  background: rgba(42, 22, 14, 0.72);
  box-shadow: 0 0 26px rgba(232, 180, 87, 0.13) inset;
}

.mon__label,
.day__d {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-dim);
}

.mon__n,
.day__n {
  font-family: var(--hand);
  font-size: 1.7rem;
  line-height: 1;
  color: var(--candle);
}

.mon.is-open .mon__n,
.day.is-open .day__n {
  color: var(--candle-hi);
}

.mon--shut {
  min-height: 62px;
  background:
    repeating-linear-gradient(
      -38deg,
      rgba(232, 180, 87, 0.06) 0 5px,
      transparent 5px 11px
    ),
    rgba(13, 20, 38, 0.5);
  cursor: default;
}

/* ── The letter ──────────────────────────────────────────────────────────── */
.letter {
  margin-top: 1.8rem;
  padding: 3rem clamp(1.6rem, 4vw, 3.4rem);
  max-width: 64ch;
  border: 1px solid rgba(232, 180, 87, 0.14);
  background:
    radial-gradient(120% 80% at 20% 0%, rgba(42, 22, 14, 0.55), transparent 65%),
    linear-gradient(178deg, rgba(20, 16, 14, 0.85), rgba(13, 20, 38, 0.6));
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.5);
}

.letter__sal {
  font-size: 1.05rem;
  letter-spacing: 0.06em;
  color: var(--ash-dim);
  font-style: italic;
}

.letter__open {
  margin-top: 1.4rem;
  font-size: 1.35rem;
  line-height: 1.66;
  color: var(--cream);
}

.letter__part {
  margin-top: 2.4rem;
}

.letter__head {
  font-size: 0.74rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--candle);
}

.letter__body {
  margin-top: 0.7rem;
  font-size: 1.16rem;
  line-height: 1.72;
  color: var(--ash);
}

.letter__close {
  margin-top: 2.8rem;
  font-size: 1.25rem;
  line-height: 1.66;
  color: var(--cream);
  font-style: italic;
}

.letter__sign {
  margin-top: 1.6rem;
  font-family: var(--hand);
  font-size: 1.9rem;
  color: var(--candle);
}

.letter__ps {
  margin-top: 2rem;
  padding-top: 1.4rem;
  border-top: 1px solid rgba(232, 180, 87, 0.12);
  font-size: 1.02rem;
  color: var(--ash-dim);
  font-style: italic;
}

.letter--shut {
  display: grid;
  gap: 0.9rem;
}

.rule {
  display: block;
  height: 1px;
  background: linear-gradient(to right, rgba(232, 180, 87, 0.22), rgba(232, 180, 87, 0.04));
}

.rule--short {
  width: 62%;
  margin-bottom: 0.8rem;
}

/* She does not keep a copy. This is the one that leaves the tent. */
/* She does not keep a copy. This is the one that leaves the tent. */
@media print {
  body { background: #fff; color: #16110c; }
  body > *:not(#scene-room) { display: none !important; }
  #scene-room .scene__bg,
  #scene-room .vignette,
  .room__back,
  .room .label,
  .room .row,
  .room .dim,
  #embers,
  .grain,
  .sound-btn { display: none !important; }
  #scene-room,
  #scene-room .scene__inner,
  .room {
    position: static !important; inset: auto !important; opacity: 1 !important;
    visibility: visible !important; overflow: visible !important;
    background: #fff !important; padding: 0 !important; transform: none !important;
    width: auto !important; max-width: none !important;
  }
  .letter {
    border: none; box-shadow: none; background: #fff; max-width: none;
    padding: 0; margin: 0;
  }
  .letter__open, .letter__body, .letter__close, .letter__sal, .letter__ps { color: #16110c; }
  .letter__head { color: #7a5a1c; }
  .letter__sign { color: #16110c; }
  .letter__part { break-inside: avoid; }
}

/* The things she adds after the main paragraph, one beat quieter. */
.telling__body--more {
  margin-top: 1rem;
  color: var(--ash);
  font-size: 0.96em;
}

/* The one thing about this chart least likely to be true of anyone else. */
.letter__mark {
  margin-top: 1.6rem;
  padding: 0.8rem 1.1rem;
  border-left: 2px solid rgba(232, 180, 87, 0.5);
  font-family: var(--hand);
  font-size: 1.5rem;
  color: var(--candle-hi);
}


/* Cards fade up a little quicker than the ambient scene washes. */
.slot__face--front video,
.card-detail video {
  transition: opacity 0.6s ease;
}

/* ── Your name, sorted into the four planes ─────────────────────────────────
   A picture rather than a score. The letters are the bar. The plane you have
   none of is the one worth looking at, so it is drawn as an absence. */
.planes {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.9rem;
  align-items: end;
  margin: 1.8rem 0 0;
  padding: 1.6rem 1.2rem 1.2rem;
  border: 1px solid rgba(232, 180, 87, 0.12);
  background: linear-gradient(178deg, rgba(20, 16, 14, 0.6), rgba(13, 20, 38, 0.45));
}

.plane {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

.plane__stack {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 3px;
  width: 100%;
  min-height: 160px;
  justify-content: flex-start;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid rgba(232, 180, 87, 0.28);
}

.plane__letter {
  display: grid;
  place-items: center;
  width: 100%;
  max-width: 46px;
  aspect-ratio: 1;
  border: 1px solid rgba(232, 180, 87, 0.3);
  background: linear-gradient(160deg, rgba(52, 28, 17, 0.92), rgba(28, 16, 11, 0.8));
  color: var(--candle-hi);
  font-size: 1.25rem;
  letter-spacing: 0.04em;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  animation: plane-settle 0.5s var(--ease) backwards;
}

/* They stack up like counters being laid down one at a time. */
.plane__letter:nth-child(1) { animation-delay: 0.05s; }
.plane__letter:nth-child(2) { animation-delay: 0.1s; }
.plane__letter:nth-child(3) { animation-delay: 0.15s; }
.plane__letter:nth-child(4) { animation-delay: 0.2s; }
.plane__letter:nth-child(5) { animation-delay: 0.25s; }
.plane__letter:nth-child(6) { animation-delay: 0.3s; }
.plane__letter:nth-child(7) { animation-delay: 0.35s; }
.plane__letter:nth-child(8) { animation-delay: 0.4s; }
.plane__letter:nth-child(n + 9) { animation-delay: 0.45s; }

@keyframes plane-settle {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

.plane--empty .plane__stack {
  justify-content: center;
  align-items: center;
  border-bottom-color: rgba(232, 180, 87, 0.12);
  background:
    repeating-linear-gradient(
      -38deg,
      rgba(232, 180, 87, 0.05) 0 6px,
      transparent 6px 13px
    );
}

.plane__none {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-dim);
  text-align: center;
  padding: 0 0.3rem;
}

.plane__label {
  margin-top: 0.7rem;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--candle);
  text-align: center;
}

.plane__count {
  color: var(--ash-dim);
}

.plane__note {
  margin-top: 0.25rem;
  font-size: 0.82rem;
  color: var(--ash-dim);
  text-align: center;
  line-height: 1.35;
}

.plane--empty .plane__label {
  color: var(--ash-dim);
}

@media (max-width: 640px) {
  .planes {
    grid-template-columns: repeat(2, 1fr);
    gap: 1.4rem 0.9rem;
  }
  .plane__stack {
    min-height: 120px;
  }
}

/* ── The object at the head of a drawer ─────────────────────────────────────
   Painted still lifes, one per offer, all shot on the same night. They sit
   behind the label rather than above it, so the drawer opens on the thing
   itself and the words arrive second. */
.offer-art {
  position: relative;
  margin: -1rem -1rem 1.6rem;
  aspect-ratio: 16 / 6;
  overflow: hidden;
  border-bottom: 1px solid rgba(232, 180, 87, 0.14);
}

.offer-art img,
.offer-art video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 62%;
}

.offer-art video {
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}

.offer-art video.is-on {
  opacity: 1;
}

/* The painting has to give the type somewhere to stand. */
.offer-art::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(7, 11, 24, 0.1) 0%, rgba(7, 11, 24, 0.72) 78%, var(--night) 100%),
    linear-gradient(to right, rgba(7, 11, 24, 0.5), transparent 45%);
  pointer-events: none;
}

@media (max-width: 640px) {
  .offer-art {
    aspect-ratio: 16 / 9;
  }
}

/* ── The sound control ──────────────────────────────────────────────────────
   A mark rather than a button. It should be findable but never be the thing
   you notice about the room, so it sits low in opacity until you go near it. */
.sound-btn {
  display: inline-grid;
  place-items: center;
  /* It lives in a flex row of real buttons and must not be squeezed by them. */
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  background: none;
  color: var(--ash-dim);
  opacity: 0.42;
  cursor: pointer;
  transition:
    opacity 0.4s var(--ease),
    color 0.4s var(--ease);
}

.sound-btn:hover,
.sound-btn:focus-visible {
  opacity: 1;
  color: var(--candle);
}

.sound-btn:focus-visible {
  outline: 1px solid rgba(232, 180, 87, 0.5);
  outline-offset: 3px;
}

/* Sound on: waves showing, no slash. Sound off: the reverse. */
.sound-btn[aria-pressed="true"] .sound-btn__slash,
.sound-btn[aria-pressed="false"] .sound-btn__waves {
  opacity: 0;
}

.sound-btn__slash,
.sound-btn__waves {
  transition: opacity 0.3s ease;
}

/* On the table it sits with the other footer controls, quieter than all of them. */
#lamp-sound {
  margin-right: 0.2rem;
}

/* ── After the reading ──────────────────────────────────────────────────────
   She does not sweep the cards away when she is done talking. They stay laid
   out, smaller, at the top of the table, and everything she says afterwards is
   said underneath them. Losing them was what made the second half feel like a
   different website from the first. */
#phase-read.is-put-away .telling,
#phase-read.is-put-away #read-actions {
  display: none;
}

#phase-read.is-put-away {
  margin-bottom: clamp(1.5rem, 4vh, 3rem);
}

/* min-width matters here and width alone does nothing. A flex item defaults to
   min-width:auto, which floors it at its content's minimum size, and the card's
   content is an image, so the floor is the image's intrinsic width. Without
   min-width:0 the card simply refuses to get smaller and the rule looks broken. */
#phase-read.is-put-away .card {
  min-width: 0;
  width: clamp(40px, 4.6vw, 62px);
}

/* The caption named the card while she was reading it. Once the cards are just
   lying there it is 40 characters wrapping inside a 62px column, which cost
   more height than the cards themselves. The number stays on the card. */
#phase-read.is-put-away .slot__caption {
  display: none;
}

#phase-read.is-put-away .workings {
  opacity: 0.5;
  transform: scale(0.88);
  transition:
    opacity 1.1s var(--ease),
    transform 1.1s var(--ease);
}

/* No single card is "current" any more; the whole spread is just lying there. */
#phase-read.is-put-away .card.is-current .slot {
  box-shadow: 0 24px 50px -26px rgba(0, 0, 0, 0.95);
}

#phase-read.is-put-away .slot__num {
  opacity: 0.75;
}

/* ── The reading must fit the screen ────────────────────────────────────────
   Scrolling mid-sentence breaks the spell: she is talking to you, and you
   should not have to go looking for the rest of it. The reading phase is held
   to the height of the window, and the spread gives way first because the
   words are the part that must not be cut. */
#phase-read:not(.is-put-away) {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(0.5rem, 1.6vh, 1.4rem);
  max-height: 100%;
}

#phase-read:not(.is-put-away) .card {
  min-width: 0;
  width: min(clamp(88px, 13vw, 176px), 22vh);
}

#phase-read:not(.is-put-away) .telling {
  overflow: hidden;
}

/* ── Legibility ─────────────────────────────────────────────────────────────
   Everything here is read off a lit painting, so the body copy carries a
   little more weight and a little more light than it would on flat paper. */
.telling__body,
.entry__body,
.letter__body {
  color: #e4d6bd;
  font-size: 1.2rem;
  line-height: 1.75;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
}

.telling__body--more {
  color: #cdbb9c;
  font-size: 1.08rem;
}

.telling__line {
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.65);
}

.dim,
.stat__note,
.kin__note,
.plane__note {
  color: #a8977c;
}

/* The plates the words sit on are darker, so the painting behind never
   competes with a sentence. */
.plate,
.telling {
  background: linear-gradient(178deg, rgba(9, 7, 12, 0.86), rgba(7, 11, 24, 0.8));
  backdrop-filter: blur(2px);
}

/* The reading has one screen and must live inside it. These are the three
   places the height goes, trimmed until the whole thing fits at a laptop size
   without ever asking the visitor to scroll mid-sentence. */
#scene-table .scene__inner {
  padding: clamp(16px, 2.6vw, 40px) clamp(24px, 5vw, 72px);
}

#phase-read:not(.is-put-away) .card {
  width: min(clamp(88px, 13vw, 176px), 17vh);
}

#phase-read:not(.is-put-away) .telling {
  padding-block: clamp(1rem, 2.2vh, 1.9rem);
}

#phase-read:not(.is-put-away) .telling__line {
  font-size: clamp(1.4rem, 2.5vh, 2rem);
}

#phase-read:not(.is-put-away) .telling__body {
  font-size: clamp(1rem, 1.75vh, 1.2rem);
  line-height: 1.68;
}

/* The two figures read side by side rather than stacked with their notes. */
#phase-after .stat-row {
  align-items: baseline;
}

#phase-after .stat-row > div {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  flex-wrap: wrap;
}

#phase-after .stat__note {
  flex: 1 1 8rem;
}

/* The arithmetic has been read out already; the cards alone are the memento. */
#phase-read.is-put-away .workings {
  display: none;
}

/* Each object says its name and one line, not three. */


/* She sweeps the cards together. The spread dips out of sight, resizes while
   nobody is looking, and comes back small — a flex item cannot be smoothly
   transitioned between two content-derived widths, so the change is hidden
   inside the dip rather than fought. */
#phase-read {
  transition:
    opacity 0.5s var(--ease),
    transform 0.5s var(--ease);
}

#phase-read.is-gathering {
  opacity: 0.12;
  transform: translateY(-8px) scale(0.985);
}

#phase-read.is-put-away {
  transition:
    opacity 0.75s var(--ease),
    transform 0.75s var(--ease);
}

/* Rooms arrive the way the tent does: out of the dark, not sliding in. */
.scene--room .scene__inner > * {
  animation: room-arrive 0.9s var(--ease) backwards;
}

.scene--room.is-on .room {
  animation-delay: 0.12s;
}

@keyframes room-arrive {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* ── Reading in a lit room ──────────────────────────────────────────────────
   The first attempt at this darkened the whole wall to near-black, which made
   the words easy to read and threw the room away with them. You were meant to
   still be somewhere.

   So the wall stays lit, and the darkness is LOCAL: a soft band of shadow only
   behind the column the words occupy, feathered out to nothing well before the
   edges of the frame. The painting keeps its corners, its candles and its
   colour, and the reading sits in a pool of shade in the middle of it, which is
   how a lit room actually works. */
.scene--room .scene__bg {
  filter: brightness(0.62) saturate(0.95) blur(1px);
  --bleed: 1.1;
}

/* The pool of shade. Strong under the text, gone by the edges. */
.scene--room .vignette {
  background:
    linear-gradient(
      to right,
      rgba(6, 4, 10, 0) 0%,
      rgba(6, 4, 10, 0.5) 12%,
      rgba(6, 4, 10, 0.86) 26%,
      rgba(6, 4, 10, 0.88) 74%,
      rgba(6, 4, 10, 0.5) 88%,
      rgba(6, 4, 10, 0) 100%
    ),
    linear-gradient(
      to bottom,
      rgba(6, 4, 10, 0.35) 0%,
      rgba(6, 4, 10, 0) 22%,
      rgba(6, 4, 10, 0) 78%,
      rgba(6, 4, 10, 0.35) 100%
    );
}

/* A narrower column, so there is painting left to see on either side of it. */
.scene--room .scene__inner {
  width: min(780px, 100%);
}

/* Her voice is quieter in a room than it is at the table.
 *
 * The full cinematic size is right when what she says IS the screen — the
 * threshold, the reading. In a room it is the greeting before the thing you came
 * for, and at 2.3rem a single sentence of it ran to 209px, a third of the window,
 * before the almanac had shown you anything at all. Inline font-size overrides
 * still win, so the lines deliberately set larger stay larger. */
.scene--room .voice {
  font-size: clamp(1.05rem, 0.55rem + 0.55vw + 1.4vh, 1.7rem);
  line-height: 1.38;
  /* And a wider measure. 22ch is a poetic column, which is right when a line is
     the whole screen; in a 684px room it left half the width empty and turned
     every closing sentence into five short lines and 151px of height. */
  max-width: 42ch;
}

/* The figure of the name, tightened for a room. Its height follows the data —
   it is a stack of letters — so the levers are the letter size and the floor
   under an empty column, not a fixed height. */
.scene--room .planes {
  margin-top: clamp(0.6rem, 1.6vh, 1.2rem);
  padding: clamp(0.6rem, 1.5vh, 1.1rem) 0.9rem clamp(0.5rem, 1.2vh, 0.9rem);
  gap: 0.7rem;
}

/* Everything below scales with the height of the window as well as its width.
   A room laid out purely in rem and vw fits a tall laptop and overflows a short
   one by exactly the amount of air in it, which is the wrong thing to be rigid
   about: on a short screen you want the same reading, closer together. */
.scene--room .plane__stack {
  min-height: clamp(74px, 15vh, 118px);
}

.scene--room .months,
.scene--room .days {
  margin-top: clamp(0.6rem, 1.7vh, 1.5rem);
  gap: clamp(4px, 0.7vh, 9px);
}

.scene--room .mon,
.scene--room .day {
  padding: clamp(0.3rem, 0.85vh, 0.7rem) 0.4rem clamp(0.26rem, 0.75vh, 0.6rem);
}

/* On a phone the fortnight wrapped to three rows of days and pushed the page
   88px past the screen. Seven to a row is two rows, and a day tile only ever
   holds a number and sometimes three letters of a month. */
@media (max-width: 560px) {
  .scene--room .mon,
  .scene--room .day {
    min-width: 38px;
    padding-inline: 0.2rem;
  }
}

/* The figure's height is the tallest column of letters, so this is the lever
   that actually controls it — a fixed cap fits a tall window and overflows a
   short one by however many letters the name happens to have. */
.scene--room .plane__letter {
  max-width: clamp(20px, 4.4vh, 36px);
}

/* What she says in a room is a paragraph, not a proclamation. The reading's own
   .telling keeps the full size — this is scoped to rooms only. */
.scene--room .telling {
  padding: clamp(14px, 2vw, 22px) clamp(16px, 2.4vw, 26px);
}

.scene--room .telling__line {
  font-size: clamp(1.25rem, 1rem + 0.9vw, 1.8rem);
}

/* Once one of the shut cards is turned over, the row steps back.
 *
 * At full size the three cards, the instruction and what she says about your
 * pick came to 887px in a 742px window — you had to scroll away from the cards
 * to read the answer they gave you. The cards are how you chose; the answer is
 * what you came for, so the cards get smaller and the instruction goes, because
 * you have plainly already followed it. */
.shut-row.has-picked {
  margin-top: 0.8rem;
  gap: clamp(8px, 1.2vw, 14px);
}

.shut-row.has-picked .shut {
  width: clamp(50px, 5.4vw, 72px);
}

.shut-row.has-picked + .dim {
  display: none;
}

/* The smallest labels measured 4.7:1 against the brightest pixel behind them:
   passing, but thin, and a moving wall costs you perceived contrast that a
   static measurement will not show. These are the runs worth the margin. */
.scene--room .plane__none,
.scene--room .plane__label,
.scene--room .plane__note,
.scene--room .dim,
.scene--room .stat__note,
.scene--room .kin__note {
  color: #bcaa8e;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
}

.scene--room .letter__sal,
.scene--room .label {
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.65);
}

/* ── The way on ─────────────────────────────────────────────────────────────
   Nine rooms, nine doors, and each door is the painting of the room it opens.
   These oils already existed and were only ever seen AFTER you had committed to
   a room, which is exactly why the way on felt unclear: the most interesting
   thing on the screen was drawn as nine identical grey rectangles, four of them
   parked off the right edge behind a scrollbar. A door you can see through is a
   door you want to open. */
.doors {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(8px, 1.3vh, 14px);
  width: min(900px, 100%);
  margin: clamp(0.4rem, 1.2vh, 1rem) auto 0;
}

.door {
  position: relative;
  display: block;
  height: clamp(80px, 12.6vh, 132px);
  padding: 0;
  border: none;
  background: none;
  overflow: hidden;
  cursor: pointer;
  isolation: isolate;
  transition: transform 0.55s var(--ease);
}

/* The painting itself, cropped to the part that says what the room is. */
.door__view {
  position: absolute;
  inset: 0;
  background-size: cover;
  filter: brightness(0.58) saturate(0.9);
  transform: scale(1.02);
  transition:
    filter 0.55s var(--ease),
    transform 0.7s var(--ease);
}

/* Enough ground under the name, and nothing above it. The top of every door
   stays painting, so the wall reads before the label does. */
.door__scrim {
  position: absolute;
  inset: auto 0 0 0;
  height: 68%;
  background: linear-gradient(
    to top,
    rgba(5, 3, 8, 0.95) 0%,
    rgba(5, 3, 8, 0.78) 42%,
    transparent 100%
  );
  pointer-events: none;
}

.door__name {
  position: absolute;
  left: clamp(10px, 1.2vw, 16px);
  right: clamp(10px, 1.2vw, 16px);
  bottom: clamp(8px, 1vh, 13px);
  font-family: var(--voice);
  font-size: clamp(0.92rem, 1.85vh, 1.1rem);
  line-height: 1.25;
  color: var(--cream);
  text-align: left;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.85);
  transition: color 0.45s var(--ease);
}

/* Pointing at a door is her moving the candle over it. */
.door:hover,
.door:focus-visible {
  transform: translateY(-3px);
  outline: none;
}

.door:hover .door__view,
.door:focus-visible .door__view {
  filter: brightness(0.92) saturate(1.02);
  transform: scale(1.06);
}

.door:hover .door__name,
.door:focus-visible .door__name {
  color: var(--candle-hi);
}

.door:focus-visible {
  box-shadow: 0 0 0 1px rgba(255, 212, 137, 0.75);
}

/* ── What is left of the closing ────────────────────────────────────────────
   One verdict, one quiet line of company, nine doors, and her voice at the
   bottom answering whatever your eye is resting on. Everything else on this
   screen was arithmetic dressed as a dashboard. */
.after {
  display: flex;
  flex-direction: column;
  gap: clamp(0.3rem, 0.8vh, 0.6rem);
  /* Was clamp(60px, 10vh, 120px), which is 148px of nothing above and below the
     nine doors — enough to push the last line of the screen 19px off the bottom
     of a 742px window. The doors are the point of this screen; the air around
     them was costing more than they were. */
  padding: clamp(10px, 2.2vh, 36px) 0;
}

.after > * {
  margin: 0 !important;
}

.after__verdict {
  font-style: italic;
  font-size: clamp(1.3rem, 2.7vh, 2rem);
  max-width: 40ch;
}

.after__kin {
  font-size: clamp(0.82rem, 1.5vh, 0.95rem);
  color: var(--ash-dim);
  letter-spacing: 0.04em;
}

.after__kin span {
  color: var(--ash);
}

.after__kin span:not(:last-child)::after {
  content: ", ";
  color: var(--ash-dim);
}

/* One sentence at a time, about the door under the cursor. Nine at once was
   nine things to read and nothing to look at. */
.after__say {
  min-height: 1.5em;
  margin-top: clamp(0.4rem, 1.1vh, 0.9rem) !important;
  text-align: center;
  font-size: clamp(0.95rem, 1.8vh, 1.15rem);
  font-style: italic;
  color: var(--ash-dim);
  transition: color 0.35s var(--ease);
}

.after__say.is-said {
  color: var(--cream);
}

@media (max-width: 760px) {
  .doors {
    grid-template-columns: repeat(2, 1fr);
  }
  .door {
    height: clamp(76px, 11vh, 112px);
  }
}

/* Only ever on screen for a rare road, so it can afford to be gold. */
.after__rare {
  font-family: var(--hand);
  font-size: clamp(1.1rem, 2vh, 1.35rem);
  color: var(--candle);
}


/* Her opening lines wait in the dark until she says them.
   `.said` animates a line in from nothing, but anything not yet carrying it sat
   at full opacity, so the four lines were already on the table before the
   curtain opened and the staggered arrival played to an empty room. */
#phase-ask [data-said]:not(.said) {
  opacity: 0;
}

/* ── A room read a page at a time ───────────────────────────────────────────
   The reading works because she gives you one thing and then waits. Rooms used
   to hand over their whole contents in one scroll; the letter arrived as some
   three hundred and seventy words at once, which is a document, not a reading.
   Every page is built into the page together and revealed one at a time, so
   printing still gets the whole letter and the room only wires itself once. */
[data-page][hidden] {
  display: none;
}

[data-page].is-open {
  animation: leaf-turn 0.6s var(--ease);
}

@keyframes leaf-turn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

.pager {
  display: flex;
  align-items: center;
  gap: clamp(0.8rem, 2vw, 1.6rem);
  margin-top: clamp(1.4rem, 3vh, 2.4rem);
}

.pager__back {
  border: none;
  background: none;
  padding: 0.4rem 0;
  color: var(--ash-dim);
  font-family: var(--voice);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.4s var(--ease);
}

.pager__back:hover {
  color: var(--candle);
}

/* How much is left, without a number on it. */
.pager__dots {
  display: flex;
  gap: 7px;
  margin-right: auto;
}

.pager__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(232, 180, 87, 0.22);
  transition:
    background 0.45s var(--ease),
    transform 0.45s var(--ease);
}

.pager__dot.is-at {
  background: var(--candle);
  transform: scale(1.5);
}

.pager__next.is-done {
  border-color: rgba(232, 180, 87, 0.55);
}

/* The whole letter prints, not the leaf you happen to be standing on. */
@media print {
  [data-page][hidden] {
    display: block !important;
  }
  .pager {
    display: none !important;
  }
}

/* A leaf of the letter has to fit a short laptop screen, not just a tall one.
   Measured at 707px tall: two headings came to 565px of text inside 96px of
   padding, which overflowed. The column widens a little, the air between
   headings comes down, and the page sits inside the window. */
.scene--room .letter {
  max-width: 100%;
  padding: clamp(1.1rem, 3vh, 2rem) clamp(1.1rem, 3vw, 2.2rem);
}

.scene--room .letter__part {
  margin-top: clamp(0.9rem, 2.2vh, 1.7rem);
}

.scene--room .letter__part:first-child {
  margin-top: 0;
}

.scene--room .letter__body {
  font-size: clamp(0.98rem, 1.9vh, 1.14rem);
  line-height: 1.62;
}

.scene--room .letter__open {
  font-size: clamp(1.05rem, 2.1vh, 1.3rem);
  line-height: 1.6;
}

.scene--room .letter__close {
  margin-top: clamp(1rem, 2.4vh, 1.9rem);
}

.scene--room .letter__ps {
  margin-top: clamp(0.9rem, 2vh, 1.5rem);
  padding-top: clamp(0.7rem, 1.6vh, 1.2rem);
}

.scene--room .letter__sign {
  margin-top: clamp(0.7rem, 1.6vh, 1.2rem);
}

/* The last leaf carries the closing, the signature and the postscript together,
   which is one stack more than any other. It gets the tightest spacing. */
.scene--room [data-page]:last-of-type .letter__close {
  margin-top: clamp(0.7rem, 1.8vh, 1.3rem);
}

.scene--room [data-page]:last-of-type .letter__ps {
  margin-top: clamp(0.6rem, 1.4vh, 1rem);
  padding-top: clamp(0.5rem, 1.2vh, 0.9rem);
}

/* ── The painting's own coordinates ─────────────────────────────────────────
   Anything pinned to a painted feature has to be measured in the painting, not
   in the window. .artwork is the same box .scene__bg occupies; .artwork__fit is
   the box the cover-fitted image occupies inside it, worked out the way the
   browser works it out — scale to whichever axis needs it, then place the
   painting's --plate-x/y point on the container's --plate-x/y point, which is
   precisely what background-position means. Percentages inside .artwork__fit are
   therefore percentages OF THE PAINTING.

   Named .artwork and not .plate, which is what this was called for about an
   hour. `.plate` was already taken — it is the dark panel the words sit on, used
   on the ask screen and in four of the rooms — so `position: fixed; inset: -4%;
   pointer-events: none` landed on all of them at once. Every one became a
   full-window sheet of translucent dark you could not click through to, which is
   what "the almanac is locked and I cannot do anything" looked like. Check a new
   class name against the stylesheet before using it. */
.artwork {
  position: fixed;
  inset: -4%; /* mirrors .scene__bg exactly */
  z-index: 6;
  pointer-events: none;
}

.artwork__fit {
  position: absolute;
  left: var(--plate-x, 50%);
  top: var(--plate-y, 50%);
  width: max(108vw, 108vh * 1920 / 1098);
  height: max(108vh, 108vw * 1098 / 1920);
  transform: translate(calc(var(--plate-x, 50%) * -1), calc(var(--plate-y, 50%) * -1));
}

.artwork__fit > * {
  pointer-events: auto;
}

/* ── The way in ─────────────────────────────────────────────────────────────
   You go into the tent by going into the tent. This covers the painted doorway
   between the tied-back curtains, measured off the plate: nothing to see until
   you come near it, then the opening warms and she says the word on the lit
   sand at the threshold. It is still a real button, so a keyboard reaches it,
   but there is no button to look at. */
.way-in {
  position: absolute;
  left: 60%;
  top: 35%;
  width: 18.5%;
  height: 40%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.way-in__glow {
  position: absolute;
  inset: -6%;
  border-radius: 50% 50% 42% 42% / 58% 58% 42% 42%;
  background: radial-gradient(
    ellipse at 50% 62%,
    rgba(255, 212, 137, 0.34),
    rgba(232, 180, 87, 0.12) 46%,
    transparent 72%
  );
  opacity: 0;
  transition: opacity 0.85s var(--ease);
  pointer-events: none;
}

.way-in__word {
  position: absolute;
  left: 50%;
  bottom: 4%;
  transform: translate(-50%, 8px);
  white-space: nowrap;
  font-family: var(--hand);
  font-size: clamp(1.25rem, 2.6vh, 1.7rem);
  color: var(--candle-hi);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.85);
  opacity: 0;
  transition:
    opacity 0.55s var(--ease),
    transform 0.55s var(--ease);
  pointer-events: none;
}

.way-in:hover .way-in__glow,
.way-in:focus-visible .way-in__glow {
  opacity: 1;
}

.way-in:hover .way-in__word,
.way-in:focus-visible .way-in__word {
  opacity: 1;
  transform: translate(-50%, 0);
}

.way-in:focus-visible {
  outline: none;
}

/* Someone who never moves a mouse still has to find the way in.
 *
 * Narrow screens get it too, not only devices that report no hover. A phone-sized
 * window that does have a pointer would otherwise show a tent with no way in and
 * nothing to hover over that is big enough to find by accident. */
@media (hover: none), (max-width: 860px) {
  .way-in__word {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

/* On a phone the crop blows the painting up to about four times the width of
   the screen, so the doorway alone is most of the frame and the hotspot is
   enormous. Stop it short of the sand: below it is where her side of the
   conversation and the sound toggle sit, and a tap meant for those must not be
   swallowed by the tent. */
@media (max-width: 860px) {
  /* Stop above her line. The crop blows the painting up to about four times the
     width of a phone, so a hotspot sized for a desktop reaches right down over
     "I heard you stop walking" — a hit test at the top of that sentence returned
     the tent, not the text. */
  .way-in {
    height: 26%;
  }
}

/* ── The card that is next ──────────────────────────────────────────────────
   There is no control for turning the next card. The card is the control: the
   one whose turn it is lifts a little and keeps a faint edge of candlelight, and
   touching it turns it over. */
.card.is-next .slot {
  box-shadow:
    0 0 0 1px rgba(232, 180, 87, 0.5),
    0 22px 54px -24px rgba(232, 180, 87, 0.55);
  animation: waiting 3.4s ease-in-out infinite;
}

.card.is-next {
  cursor: pointer;
}

@keyframes waiting {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

/* ── Something she will say if you ask ──────────────────────────────────────
   The extra observation used to sit on screen under the reading, which makes it
   something to skim. Asked for, the same sentence is a thing you went and got. */
.asked {
  margin-top: clamp(0.8rem, 1.8vh, 1.3rem);
}

.asked__ask {
  padding: 0.3em 0;
  border: none;
  background: none;
  font-family: var(--hand);
  font-size: clamp(1.02rem, 2vh, 1.25rem);
  color: var(--ash-dim);
  cursor: pointer;
  border-bottom: 1px dashed rgba(192, 171, 141, 0.4);
  transition:
    color 0.4s var(--ease),
    border-color 0.4s var(--ease);
}

.asked__ask::after {
  content: " ?";
  opacity: 0.6;
}

.asked__ask:hover,
.asked__ask:focus-visible {
  color: var(--candle-hi);
  border-color: rgba(232, 180, 87, 0.8);
  outline: none;
}

.asked__said {
  color: #cdbb9c;
  font-size: 1.06rem;
  line-height: 1.7;
  animation: said 1.1s var(--ease);
}
