/* ==========================================================================
   4ewdsr5y — THE ROOMS, IN 3D                  (index.html, stockroom.html)
   Layers for js/corridor3d.js. Colours are the theme's tokens only.
   ========================================================================== */

/* The WebGL canvas: the whole window, under everything readable. As tall
   as the screen with the browser's bars retracted (100lvh), not as the
   visible part: on a phone that never changes while scrolling, so the room
   is fitted once and stays as it loaded through the whole walk. Fitted to
   the visible height, Safari's bars shrinking as the walk began re-shaped
   it in steps. While the bars are out they cover its bottom edge. Where
   lvh is unknown, the window (100%). */
.room3d {
  position: fixed;
  inset: 0 0 auto 0;
  width: 100%;
  height: 100%;
  height: 100lvh;
  display: block;
  z-index: var(--z-base, 0);
  pointer-events: none;
}

/* Sideways drags look around; up-and-down drags stay the page's, so they
   scroll, and scrolling walks. Pinch-zoom is kept: never take that away. */
html[data-room3d="ready"] { touch-action: pan-y pinch-zoom; }

/* How far there is to walk, as page length. Under reduced motion there is
   no walk at all — the room holds still and the signs are links as ever. */
.room3d-walk { height: calc(100svh + 1800px); }
@media (prefers-reduced-motion: reduce) {
  .room3d-walk { height: 100svh; }
}

/* CSS3DRenderer's layer lives inside nav[data-doors], which is already
   fixed over the window and passes the pointer through. */
.doors3d { pointer-events: none; }

/* A sign's name, as a link laid exactly over its word in 3D. The word is
   drawn by the room (js/corridor3d.js makeWord()), so the link is
   invisible: still the name to a screen reader, still a link to the
   keyboard and to a new tab, with its focus ring. Pointed at or focused,
   the room grows the word a little. */
.door3d {
  box-sizing: border-box;
  display: block;
  font-family: var(--font-mono);
  line-height: 1;
  color: transparent;
  text-decoration: none;
  outline: none;
}
.door3d:focus-visible { outline: 0.08em solid var(--signal); outline-offset: 0.06em; }

/* The stockroom's racks as links (stockroom.html): each laid over where
   its section is on screen and clipped to its outline by the page, under
   the signs. Invisible — pointing at one lights the section up in the room
   and its sign as above. */
.hits3d {
  position: absolute;
  inset: 0;
  /* The invisible links over the stockroom's racks and the corridor's
     doorways (js/corridor3d.js makeHit()). Its own stacking level, so their
     z-indexes (nearer on top) stay inside it and under the signs' layer,
     which comes after it. Without it the stockroom's BAGS and ALL signs
     were under the racks' links: 0 of 25 probes reached them. */
  z-index: 0;
  pointer-events: none;
}
.hit3d {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: auto;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* Loading, and the one-line hint on how to move. */
.room3d-status {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: var(--z-doors, 20);
  font-size: var(--fs-xs);
  letter-spacing: var(--tr-wide);
  color: var(--fg-dim);
  pointer-events: none;
}
.room3d-status:empty { display: none; }

.room3d-hint {
  position: fixed;
  left: 50%;
  bottom: calc(35px + var(--sp-5));
  transform: translateX(-50%);
  z-index: var(--z-doors, 20);
  max-width: calc(100vw - 32px);
  font-size: var(--fs-sm);
  letter-spacing: var(--tr-wide);
  text-align: center;
  color: var(--fg-dim);
  pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease);
}
.room3d-hint[data-gone] { opacity: 0; }
/* The dark world's floor mist lies right under the hint and is lighter than
   the dark floor it was measured on: 3.3:1 there instead of 5.4:1. A soft
   backing of the page's own --bg, feathered so it reads as shadow rather
   than a box, gives the letters back a dark ground (about 5:1 over the
   brightest mist). A text-shadow halo was tried first: on 13px strokes a
   blurred shadow is too faint to change the ground. */
:root:not([data-theme="light"]) .room3d-hint {
  padding: 2px 10px;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  box-shadow: 0 0 14px 8px color-mix(in srgb, var(--bg) 72%, transparent);
}
/* In the fog world the hint stood on the pale room at about 2:1. On the
   ground, feathered, as the pages' title bands are: 6.7:1 (the dark theme's
   72% backing reached only 3.7:1 over the fog). Shipped 2026-10-09. */
:root[data-theme="light"] .room3d-hint {
  padding: 2px 10px;
  background: var(--bg);
  box-shadow: 0 0 12px 6px var(--bg);
}
html:not([data-room3d="ready"]) .room3d-hint { opacity: 0; }

/* ?fps: the frame-rate readout (js/corridor3d.js), for measuring a phone. */
.fps3d {
  position: fixed;
  left: 8px;
  bottom: calc(40px + env(safe-area-inset-bottom, 0px));
  z-index: calc(var(--z-header, 50) + 1);
  padding: 2px 6px;
  background: var(--bg);
  color: var(--fg);
  font: var(--fs-xs) / 1.4 var(--font-mono);
  pointer-events: none;
}

/* The pull ends in the dark. */
.room3d-fade {
  position: fixed;
  inset: 0;
  z-index: var(--z-warp, 95);
  background: var(--bg);
  opacity: 0;
  pointer-events: none;
}

/* If WebGL is missing or the room fails to load, the plain links show. */
.room3d-fallback {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: var(--z-doors, 20);
}

/* Waking up (the landing only; js/wake.js decides whether it plays). The
   window starts behind closed lids — the dark ink with a trace of the
   damage red in it, as light through skin — and a dimmed blur. When the room is
   drawn, html[data-wake="open"] runs the opening: a crack of the corridor,
   a blink, then the lids pull apart while the blur clears. The opening is
   an ellipse in a mask, its radii registered custom properties so they
   animate smoothly (where @property is not supported they step, and the
   lids still end open). It takes no pointer, and js/wake.js removes it. */
@property --eye-w { syntax: "<percentage>"; inherits: false; initial-value: 60%; }
@property --eye-h { syntax: "<percentage>"; inherits: false; initial-value: 0%; }

.wake { display: none; }
html[data-wake] .wake {
  display: block;
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-warp, 95) + 1);
  pointer-events: none;
}
.wake-blur,
.wake-lids {
  position: absolute;
  inset: 0;
}
.wake-blur {
  -webkit-backdrop-filter: blur(14px) brightness(0.6);
  backdrop-filter: blur(14px) brightness(0.6);
}
.wake-lids {
  --wake-ink: color-mix(in srgb, var(--fg) 94%, var(--damage));
  background: var(--wake-ink);
  /* black in a mask is opacity, not a colour: the lids show where it is */
  -webkit-mask-image: radial-gradient(ellipse var(--eye-w) var(--eye-h) at 50% 50%, transparent 50%, black 100%);
  mask-image: radial-gradient(ellipse var(--eye-w) var(--eye-h) at 50% 50%, transparent 50%, black 100%);
}
/* In the dark theme the ink is light, so the lids take the ground instead. */
html[data-theme="dark"] .wake-lids { --wake-ink: color-mix(in srgb, var(--bg) 86%, var(--damage)); }

html[data-wake="open"] .wake-lids { animation: wake-lids 2600ms linear both; }
html[data-wake="open"] .wake-blur { animation: wake-blur 2600ms linear both; }

@keyframes wake-lids {
  0%   { --eye-w: 60%;  --eye-h: 0%;   animation-timing-function: cubic-bezier(0.3, 0, 0.3, 1); }
  16%  { --eye-w: 82%;  --eye-h: 9%;   animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.6); }
  27%  { --eye-w: 78%;  --eye-h: 0%; }                       /* the blink */
  36%  { --eye-w: 78%;  --eye-h: 0%;   animation-timing-function: cubic-bezier(0.35, 0, 0.2, 1); }
  72%  { --eye-w: 118%; --eye-h: 60%;  animation-timing-function: cubic-bezier(0.3, 0, 0.3, 1); }
  100% { --eye-w: 150%; --eye-h: 170%; }
}
@keyframes wake-blur {
  0%, 36% { -webkit-backdrop-filter: blur(14px) brightness(0.6); backdrop-filter: blur(14px) brightness(0.6);
            animation-timing-function: cubic-bezier(0.3, 0, 0.3, 1); }
  100%    { -webkit-backdrop-filter: blur(0px) brightness(1); backdrop-filter: blur(0px) brightness(1); }
}

@media (prefers-reduced-motion: reduce) {
  .wake { display: none !important; }
}
