/* ============================================================
   The room.

   One fixed viewport that never scrolls. Four aligned renders of the
   same room (day / night, headlight off / on) are stacked and crossfaded.
   Everything clickable is positioned in percent of the render, so it
   stays locked to its object at any window size.
   ============================================================ */

:root {
  --scene-w: 2048;
  --scene-h: 1152;

  /* The doorway: the hole the two doors fill, measured off the render. */
  --door-x: 5.859%;
  --door-y: 7.899%;
  --door-w: 89.111%;
  --door-h: 86.285%;
  --door-seam: 49.644%;

  --hud: #efe9dc;
  --glow: #fff1d6;
  --surround: #0d0c0a;
}

html.room,
html.room body {
  height: 100%;
  margin: 0;
  overflow: hidden;
  overscroll-behavior: none;
}
html.room body {
  background: var(--surround);
  color: var(--hud);
  font-family: var(--mono);
  -webkit-font-smoothing: antialiased;
}

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

/* The whole render, letterboxed; the dark surround reads as a frame. */
.scene {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(100vw, calc(100svh * var(--scene-w) / var(--scene-h)));
  height: min(100svh, calc(100vw * var(--scene-h) / var(--scene-w)));
}

/* Its own node so the engine shake does not fight the centring. Isolated so
   the light overlays blend with the room, not with the dark page behind it. */
.shake {
  position: absolute;
  inset: 0;
  isolation: isolate;
  transform-origin: 50% 70%;
}

.photo { position: absolute; inset: 0; }
.layer { position: absolute; inset: 0; opacity: 0; transition: opacity 1100ms ease; }
.layer img {
  width: 100%;
  height: 100%;
  object-fit: fill;
  user-select: none;
  -webkit-user-drag: none;
}
.layer[data-layer='day'] { opacity: 1; }
.layer.is-on { opacity: 1; }
/* A headlight is quicker than a sunset. */
.layer[data-layer$='moto'] { transition-duration: 240ms; }


/* The robot's glance: a patch the size of the robot, 740,480 to 1010,840 on the
   2048x1152 render, faded in over whichever render is showing. */
.glance {
  position: absolute;
  left: 36.133%;
  top: 41.667%;
  width: 13.184%;
  height: 31.25%;
  opacity: 0;
  pointer-events: none;
  user-select: none;
  transition: opacity 260ms ease;
}
.is-glancing:not(.is-night) .glance--day,
.is-glancing.is-night .glance--night { opacity: 1; transition: opacity 180ms ease 60ms; }
.is-night .glance--day,
html:not(.is-night) .glance--night { transition-duration: 0s; }


/* --- hotspots, outlines, labels ------------------------------ */

.hotspot {
  position: absolute;
  z-index: 4;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.hotspot:focus { outline: none; }

.outlines,
.arrows {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
.outlines { z-index: 3; }
.arrows { z-index: 5; }

.outline {
  fill: rgba(255, 244, 220, 0);
  stroke: var(--glow);
  stroke-width: 1.8;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  opacity: 0;
  filter: drop-shadow(0 0 3px rgba(255, 220, 160, 0.55)) drop-shadow(0 0 1px rgba(0, 0, 0, 0.8));
  transition: opacity 220ms ease, fill 220ms ease;
}
.outline.is-on { opacity: 1; fill: rgba(255, 244, 220, 0.06); }

.labels { position: absolute; inset: 0; z-index: 6; pointer-events: none; }

/* Labels: the Chinese large, written with a brush-like hand (LXGW WenKai),
   the English small beneath it in the same serif as the reading pages (Newsreader). */
.olabel {
  position: absolute;
  transform: translate(-50%, calc(-100% - 6px));
  display: grid;
  justify-items: center;
  white-space: nowrap;
  color: #fffaf1;
  text-shadow:
    0 1px 2px rgba(20, 12, 4, 0.7),
    0 2px 14px rgba(20, 12, 4, 0.55),
    0 0 42px rgba(20, 12, 4, 0.45);
  opacity: 0;
  transition: opacity 220ms ease, transform 300ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
.olabel.is-on { opacity: 1; transform: translate(-50%, calc(-100% - 11px)); }
.olabel b {
  font-family: 'LXGW WenKai', 'Kaiti SC', 'STKaiti', serif;
  font-size: clamp(24px, 2.9vw, 46px);
  font-weight: 500;
  letter-spacing: 0.14em;
  margin-right: -0.14em;    /* the tracking after the last character, so it centres */
  line-height: 1.15;
}
.olabel small {
  margin-top: 0.1em;
  font-family: var(--serif);
  font-size: clamp(14px, 1.3vw, 20px);
  letter-spacing: 0.04em;
}

.arrow {
  fill: none;
  stroke: #fff;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.85));
  opacity: 0;
  transition: opacity 320ms ease;
}
.arrow.is-on { opacity: 1; }
.arrow.is-out { opacity: 0; }

@media (max-width: 40rem) {
  .olabel b { font-size: clamp(17px, 5vw, 24px); }
  .olabel small { font-size: 12px; }
  .arrow { stroke-width: 1.8; }
}


/* --- ambient life -------------------------------------------- */

/* The sun drifts behind the pines: a warm pool that swells and ebbs. */
.sunlight {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(70% 70% at 50% 38%, rgba(255, 206, 140, 0.55), transparent 65%);
  mix-blend-mode: soft-light;
  opacity: 0.12;
  animation: sun 21s ease-in-out infinite;
}
@keyframes sun { 0%, 100% { opacity: 0.08; } 50% { opacity: 0.34; } }

/* After dark the paper lantern breathes, and the desk lamp holds steady. */
.lampglow,
.deskglow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 1100ms ease;
}
.lampglow { background: radial-gradient(22% 34% at 49.9% 21%, rgba(255, 196, 120, 0.42), transparent 70%); }
.deskglow { background: radial-gradient(9% 13% at 14.2% 60%, rgba(255, 190, 110, 0.35), transparent 70%); }

.is-night .sunlight { animation: none; opacity: 0; transition: opacity 900ms ease; }
.is-night .lampglow { opacity: 0.6; animation: lantern 7s ease-in-out infinite; }
.is-night .deskglow { opacity: 0.55; }
@keyframes lantern { 0%, 100% { opacity: 0.38; } 50% { opacity: 0.85; } }

/* Coffee steam: four soft wisps on staggered clocks, curling as they rise.
   Screen-blended, so on the sunlit wall they read as light moving rather than
   as grey smudges; the render has a faint painted wisp that they drift through. */
.steam {
  position: absolute;
  left: 15.66%;
  top: 55.2%;
  width: 3.4%;
  height: 12%;
  pointer-events: none;
  mix-blend-mode: screen;
  transition: opacity 900ms ease;
}
.steam i {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 34%;
  height: 22%;
  margin-left: -17%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 251, 242, 0.95), rgba(255, 251, 242, 0));
  filter: blur(2.5px);
  opacity: 0;
  animation: steam 4.6s ease-in infinite;
}
.steam i:nth-child(2) { animation-delay: 1.15s; }
.steam i:nth-child(3) { animation-delay: 2.3s; }
.steam i:nth-child(4) { animation-delay: 3.45s; }
@keyframes steam {
  0%   { transform: translate(0, 0) scale(0.5, 0.6); opacity: 0; }
  15%  { opacity: 0.85; }
  50%  { transform: translate(-32%, -170%) scale(1, 1.6); opacity: 0.55; }
  80%  { transform: translate(24%, -300%) scale(1.4, 2); opacity: 0.2; }
  100% { transform: translate(40%, -360%) scale(1.6, 2.2); opacity: 0; }
}
/* In a dark room the wisps read as a glowing ball unless they come down. */
.is-night .steam { opacity: 0.45; }

/* --- the motorcycle ------------------------------------------ */

.shake.is-cranking { animation: crank 1.4s ease-out; }
@keyframes crank {
  0%   { transform: translate3d(0, 0, 0) rotate(0); }
  7%   { transform: translate3d(-2.5px, 1.5px, 0) rotate(0.08deg); }
  15%  { transform: translate3d(2px, -2px, 0) rotate(-0.07deg); }
  25%  { transform: translate3d(-2px, -1px, 0) rotate(0.06deg); }
  40%  { transform: translate3d(1.5px, 1.5px, 0) rotate(-0.04deg); }
  58%  { transform: translate3d(-1px, -0.8px, 0) rotate(0.03deg); }
  76%  { transform: translate3d(0.6px, 0.5px, 0) rotate(-0.01deg); }
  100% { transform: translate3d(0, 0, 0) rotate(0); }
}
/* A small endless tremble while it idles. */
.shake.is-idling { animation: idle 240ms linear infinite; }
@keyframes idle {
  0%, 100% { transform: translate3d(0, 0, 0); }
  25%      { transform: translate3d(0.4px, -0.3px, 0); }
  50%      { transform: translate3d(-0.3px, 0.4px, 0); }
  75%      { transform: translate3d(0.25px, 0.3px, 0); }
}


/* --- the doors ---------------------------------------------
   Two glass doors in cedar frames, rendered with the room so the wood and the
   light match it. Each door is two images: the whole door as rendered (frame,
   glass, reflections, the room seen through it) and the frame alone with the
   glass cut out. Closed, the whole door shows. The moment they start to slide
   the whole door fades to the frame alone over a faint clear pane, so what you
   see through the glass is the real room, standing still, as it should.
   Sizes inside are in container units, so everything scales with the scene.
   ------------------------------------------------------------ */

.scene { container-type: inline-size; }

.doorway {
  position: absolute;
  z-index: 8;
  left: var(--door-x);
  top: var(--door-y);
  width: var(--door-w);
  height: var(--door-h);
  overflow: hidden;           /* the walls the doors slide into */
  cursor: pointer;
}
.is-open .doorway { pointer-events: none; cursor: default; }

.slider {
  position: absolute;
  top: 0;
  bottom: 0;
  /* Closing: glide back and settle, a beat after the latch in the sound. */
  transition: transform 1600ms cubic-bezier(0.45, 0, 0.2, 1) 250ms, visibility 0s;
  will-change: transform;
}
.slider--left  { left: 0; width: var(--door-seam); --g: 2.213% 2.870% 4.527% 3.422%; }
.slider--right { right: 0; width: calc(100% - var(--door-seam)); --g: 2.213% 3.264% 4.527% 2.829%; }

/* Opening waits 250ms for the latch, then moves with the rollers in the sound. */
.is-open .slider { visibility: hidden; transition: transform 1700ms cubic-bezier(0.6, 0.02, 0.2, 1) 250ms, visibility 0s 1950ms; }
.is-open .slider--left  { transform: translateX(-100.5%); }
.is-open .slider--right { transform: translateX(100.5%); }

/* A small give under the pointer, as if a hand were already on the pull. */
html:not(.is-open) .doorway:hover .slider { transition: transform 450ms ease; }
html:not(.is-open) .doorway:hover .slider--left  { transform: translateX(-0.8%); }
html:not(.is-open) .doorway:hover .slider--right { transform: translateX(0.8%); }

.leaf {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  user-select: none;
  -webkit-user-drag: none;
}
/* Closing: the whole door comes back only once it has slid home. */
.leaf--full { transition: opacity 320ms ease 1650ms; }
.is-open .leaf--full { opacity: 0; transition: opacity 320ms ease 250ms; }

/* The clear pane the room is seen through while the doors move. */
.pane {
  position: absolute;
  inset: var(--g);
  background:
    linear-gradient(112deg, rgba(255, 236, 205, 0.16) 0%, rgba(255, 236, 205, 0.04) 18%, transparent 30%,
                    transparent 58%, rgba(255, 236, 205, 0.07) 64%, transparent 74%),
    rgba(40, 32, 22, 0.06);
  -webkit-backdrop-filter: brightness(0.92);
  backdrop-filter: brightness(0.92);
}

/* A note taped to the right-hand glass, rendered with the doors. It slides
   away with its door. The paper is an image; the words are text written on
   it, in ink, so the paper's grain and the pine shadows show through them.
   Positions are the rendered patch (480x672) within the 919x994 door leaf. */
.note {
  position: absolute;
  left: 14.45%;
  top: 20.08%;
  width: 45.96%;
  height: 59.49%;
  /* Taped along its top edge: the breeze lifts the bottom off the glass. */
  transform-origin: 50% 12%;
  animation: breeze 12s ease-in-out infinite;
  animation-delay: 1.2s;
}
/* Gusts, not a metronome: a big puff that lifts the bottom off the glass and
   lets it flap down, a smaller one later, and stillness in between. The paper
   brightens a touch as it lifts into the light. */
@keyframes breeze {
  0%, 100% { transform: perspective(700px) rotateX(0deg) rotateZ(0deg) skewX(0deg); filter: brightness(1); }
  4%   { transform: perspective(700px) rotateX(24deg) rotateZ(-1.2deg) skewX(1.5deg); filter: brightness(1.06); }
  8%   { transform: perspective(700px) rotateX(6deg) rotateZ(0.8deg) skewX(-1deg); filter: brightness(1.01); }
  12%  { transform: perspective(700px) rotateX(17deg) rotateZ(-0.7deg) skewX(0.9deg); filter: brightness(1.04); }
  16%  { transform: perspective(700px) rotateX(3deg) rotateZ(0.4deg) skewX(-0.4deg); filter: brightness(1); }
  20%  { transform: perspective(700px) rotateX(8deg) rotateZ(-0.3deg) skewX(0.3deg); filter: brightness(1.02); }
  27%  { transform: perspective(700px) rotateX(0deg) rotateZ(0deg) skewX(0deg); filter: brightness(1); }
  60%  { transform: perspective(700px) rotateX(0deg) rotateZ(0deg) skewX(0deg); filter: brightness(1); }
  64%  { transform: perspective(700px) rotateX(13deg) rotateZ(0.8deg) skewX(-0.8deg); filter: brightness(1.03); }
  69%  { transform: perspective(700px) rotateX(2deg) rotateZ(-0.3deg) skewX(0.2deg); filter: brightness(1); }
  73%  { transform: perspective(700px) rotateX(6deg) rotateZ(0.2deg) skewX(-0.2deg); filter: brightness(1.01); }
  80%  { transform: perspective(700px) rotateX(0deg) rotateZ(0deg) skewX(0deg); filter: brightness(1); }
}
}
.note__paper { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; user-select: none; }
/* The paper's corners in the patch: 16%/13% to 86%/87%, turned about a degree. */
.note__words {
  position: absolute;
  left: 23%;
  right: 20%;
  top: 22%;
  transform: rotate(-1.1deg);
  transform-origin: 0 0;
  color: #2b2621;
  mix-blend-mode: multiply;
  font-family: var(--serif);
  font-size: clamp(10px, 0.98cqw, 19px);
  line-height: 1.45;
  text-wrap: pretty;
}
.note__words p { margin: 0 0 0.6em; }
.note__hi { font-size: 1.45em; line-height: 1.2; margin-bottom: 0.55em !important; }
.note__zh {
  font-family: 'LXGW WenKai', 'Kaiti SC', serif;
  font-size: 1.12em;
  letter-spacing: 0.06em;
  margin-top: 0.9em !important;
}
.enter {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4em;
  margin: 0.2em 0 0;
  padding: 0 0 0.12em;
  border: 0;
  border-bottom: 1px solid rgba(43, 38, 33, 0.55);
  background: none;
  color: inherit;
  font: inherit;
  font-size: 1.12em;
  cursor: pointer;
  transition: border-color 200ms ease;
}
.enter:hover { border-bottom-color: #2b2621; }
.enter__arrow { transition: transform 250ms cubic-bezier(0.3, 0.7, 0.2, 1); }
.enter:hover .enter__arrow { transform: translateX(0.3em); }
.enter:focus-visible { outline: 1.5px solid #2b2621; outline-offset: 3px; }

/* Close them again: deliberately quiet, it sits on the lintel. */
.door-close {
  position: absolute;
  z-index: 9;
  left: 50%;
  top: 2.4%;
  transform: translateX(-50%);
  width: 2.4%;
  min-width: 22px;
  height: 2.4%;
  min-height: 12px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: opacity 400ms ease 600ms, visibility 400ms 600ms;
}
.is-open .door-close { opacity: 0.2; visibility: visible; }
.door-close:hover { opacity: 0.7; }
.door-close svg { width: 100%; height: 100%; fill: none; stroke: #fff; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }


/* --- leaving through an object -------------------------------
   A soft diagonal edge sweeps across the room: behind it the room is in
   pencil, ahead of it still a photograph. The two masks are exact
   complements, so at every point the drawing and the photo add up to one
   and nothing in between ever shows through. Then the drawing is lifted
   off, and the next page is already on the paper underneath.
   ------------------------------------------------------------ */

.linework {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  object-fit: fill;
  pointer-events: none;
  filter: var(--drawing-filter);
  visibility: hidden;
}

/* The gradient is three scenes wide; sliding its position moves the edge.
   At 100% the scene sits wholly in the far end of the gradient, at 0% in the near end. */
.photo,
.linework {
  -webkit-mask-size: 300% 100%;
  mask-size: 300% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: 100% 0;
  mask-position: 100% 0;
}
.photo    { -webkit-mask-image: linear-gradient(100deg, transparent 0 42%, #000 58%); mask-image: linear-gradient(100deg, transparent 0 42%, #000 58%); }
.linework { -webkit-mask-image: linear-gradient(100deg, #000 0 42%, transparent 58%); mask-image: linear-gradient(100deg, #000 0 42%, transparent 58%); }

html.is-departing .linework { visibility: visible; }
html.is-departing .photo,
html.is-departing .linework {
  -webkit-mask-position: 0% 0;
  mask-position: 0% 0;
  transition: -webkit-mask-position 1150ms cubic-bezier(0.45, 0.05, 0.3, 1), mask-position 1150ms cubic-bezier(0.45, 0.05, 0.3, 1);
}

/* Everything that is not the room gets out of the way at once. */
html.is-departing :is(.sunlight, .lampglow, .deskglow, .steam, .vinyl, .outlines, .arrows, .labels,
                      .dock, .corner-controls, .door-close, .doorway, .leaveask, .portrait-head) {
  opacity: 0;
  transition: opacity 220ms ease;
}

.nextsheet {
  position: fixed;
  inset: 0;
  background: var(--paper);
  opacity: 0;
  pointer-events: none;
  transition: opacity 700ms cubic-bezier(0.2, 0.75, 0.3, 1);
}
.nextsheet::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: var(--grain);
  opacity: var(--grain-opacity);
  mix-blend-mode: multiply;
}
html.is-departing .nextsheet { opacity: 1; }

/* The finished drawing is set aside: it lifts a little and fades. */
html.is-lifting .scene {
  transform: translate(-50%, -50%) translateY(-2.5%);
  opacity: 0;
  transition: transform 600ms cubic-bezier(0.32, 0, 0.22, 1), opacity 500ms ease 40ms;
}


/* --- the dock ----------------------------------------------
   The way in to everything, always there: one piece of frosted glass,
   centred. Point at a section and the glass grows upward into a card with
   the newest things in it; the rows arrive one after another.
   ------------------------------------------------------------ */

.dock {
  position: fixed;
  z-index: 10;
  left: 50%;
  bottom: max(1.25rem, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  border-radius: 22px;
  border: 0.5px solid rgba(255, 255, 255, 0.24);
  background: rgba(24, 21, 17, 0.28);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.22), 0 6px 20px rgba(0, 0, 0, 0.16);
  transition: background 280ms ease, box-shadow 280ms ease;
  animation: dock-in 700ms cubic-bezier(0.2, 0.8, 0.2, 1) 300ms both;
}
.dock.is-open {
  background: rgba(24, 21, 17, 0.42);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.24), 0 18px 50px rgba(0, 0, 0, 0.35);
}
@keyframes dock-in { from { opacity: 0; transform: translate(-50%, 10px); } to { opacity: 1; transform: translate(-50%, 0); } }

.dock__bar { position: relative; display: flex; padding: 3px; }

.dock__bar a {
  position: relative;
  z-index: 1;
  padding: 6px 15px 7px;
  border-radius: 999px;
  font-family: var(--serif);
  font-size: 15px;
  letter-spacing: 0.015em;
  line-height: 1.2;
  color: rgba(255, 255, 255, 0.8);
  text-decoration: none;
  white-space: nowrap;
  transition: color 200ms ease;
}
.dock__bar a.is-lit,
.dock__bar a[aria-expanded='true'] { color: #fff; }
.dock__bar a:focus-visible { outline: 1.5px solid rgba(255, 255, 255, 0.8); outline-offset: 1px; }

.dock__lens {
  position: absolute;
  z-index: 0;
  top: 3px;
  bottom: 3px;
  left: 0;
  width: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.28);
  opacity: 0;
  transition: transform 340ms cubic-bezier(0.3, 0.8, 0.2, 1), width 340ms cubic-bezier(0.3, 0.8, 0.2, 1), opacity 200ms ease;
}
.dock__lens.is-on { opacity: 1; }

/* The card. Its height is set from JS to the height of what is inside,
   growing fast and settling slowly; the width is the bar's. */
.dock__panel {
  height: 0;
  overflow: hidden;
  transition: height 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
.dock__panel.is-closing { transition: height 220ms cubic-bezier(0.7, 0, 0.84, 0); }
.dock__panel-inner {
  width: 0;
  min-width: 100%;
  padding: 16px 18px 12px;
  color: #fff;
}
.dock__panel-inner::after {
  content: '';
  display: block;
  height: 0.5px;
  margin: 12px -18px -12px;
  background: rgba(255, 255, 255, 0.16);
}

/* Rows arrive one by one once the glass has grown. */
.dock__panel .row { opacity: 0; transform: translateY(5px); transition: opacity 260ms ease, transform 360ms cubic-bezier(0.2, 0.8, 0.2, 1); }
.dock__panel .row.is-in { opacity: 1; transform: none; }

.card-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 10px;
}
.card-head b {
  font-family: 'LXGW WenKai', 'Kaiti SC', serif;
  font-size: 19px;
  font-weight: 500;
  letter-spacing: 0.1em;
}
.card-head i {
  font-family: var(--serif);
  font-style: normal;
  font-size: 15px;
  color: rgba(255, 255, 255, 0.72);
}
.card-more {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  font-family: var(--serif);
  font-style: normal;
  font-size: 14px;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.62);
  text-decoration: none;
  white-space: nowrap;
  transition: color 220ms ease;
}
.card-more__line {
  display: block;
  width: 18px;
  height: 0.5px;
  margin-right: -9px;
  background: currentColor;
  transition: width 320ms cubic-bezier(0.3, 0.8, 0.2, 1);
}
.card-more__head {
  width: 6px;
  height: 10px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.card-more:hover { color: #fff; }
.card-more:hover .card-more__line { width: 30px; }
.card-more:focus-visible { outline: 1px solid rgba(255, 255, 255, 0.7); outline-offset: 4px; border-radius: 2px; }

/* A list of entries: title on the left, date on the right. */
.entry-row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 0 -10px;
  padding: 7px 10px;
  border-radius: 10px;
  text-decoration: none;
  color: #fff;
  transition: background 160ms ease;
}
.entry-row:hover { background: rgba(255, 255, 255, 0.1); }
.entry-row span {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--serif);
  font-size: 15px;
}
.entry-row time {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

/* A trip: its cover beside the words. */
.trip-row {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr);
  align-items: center;
  gap: 14px;
  margin: 0 -10px;
  padding: 8px 10px;
  border-radius: 12px;
  text-decoration: none;
  color: #fff;
  transition: background 160ms ease;
}
.trip-row:hover { background: rgba(255, 255, 255, 0.1); }
.trip-row img { width: 104px; aspect-ratio: 3 / 2; object-fit: cover; border-radius: 7px; }
.trip-row small {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.trip-row span { display: block; margin-top: 3px; font-family: var(--serif); font-size: 16px; }

.card-note {
  margin: 2px 0 4px;
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
}
.card-note em { font-family: var(--serif); font-size: 15px; color: rgba(255, 255, 255, 0.68); }

.card-links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.card-links a {
  flex: 1;
  padding: 7px 10px;
  border-radius: 10px;
  border: 0.5px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.06);
  text-align: center;
  font-family: var(--serif);
  font-size: 14px;
  color: #fff;
  text-decoration: none;
  transition: background 160ms ease;
}
.card-links a:hover { background: rgba(255, 255, 255, 0.14); }

.corner-controls {
  position: fixed;
  z-index: 10;
  right: 1.25rem;
  bottom: max(1.1rem, env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 10px;
  height: 40px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 600ms ease 500ms;
}
.is-open .corner-controls { opacity: 1; pointer-events: auto; }

.hint {
  margin: 0;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(239, 233, 220, 0.55);
}
.hint--hover, .hint--tap { display: none; }
@media (hover: hover) and (pointer: fine) { .hint--hover { display: inline; } }
@media not all and (hover: hover) and (pointer: fine) { .hint--tap { display: inline; } }

.mute {
  display: inline-grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  color: rgba(255, 255, 255, 0.85);
  border: 0.5px solid rgba(255, 255, 255, 0.24);
  background: rgba(22, 20, 17, 0.26);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.22);
  cursor: pointer;
  transition: background 200ms ease, color 200ms ease;
}
.mute:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }
.mute svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.mute-body { fill: currentColor; stroke-width: 1; }
/* Muting draws a pen stroke through the speaker, and the waves go quiet. */
.mute-slash { stroke-dasharray: 20; stroke-dashoffset: 20; transition: stroke-dashoffset 180ms ease; }
.mute-wave { transition: opacity 140ms ease; }
.mute[aria-pressed='true'] .mute-slash { stroke-dashoffset: 0; }
.mute[aria-pressed='true'] .mute-wave { opacity: 0; }


/* --- the band under the room --------------------------------
   On landscape screens the dock lives in the dark below the room, never on
   it: the room is shrunk just enough to leave a band of --dock-room, and the
   room and the band are centred together. On a tall window nothing shrinks
   and the dock simply sits under the picture; on a wide one the picture
   gives up a little height. Portrait phones keep their own layout below.
   ------------------------------------------------------------ */

@media (min-width: 40.0625rem) and (orientation: landscape) {
  :root {
    --dock-room: 84px;
    --scene-margin: 10px;
    --dock-bar-h: 38px;
    --scene-px-h: min(calc(100svh - var(--dock-room) - 2 * var(--scene-margin)),
                      calc(100vw * var(--scene-h) / var(--scene-w)));
    --scene-px-w: calc(var(--scene-px-h) * var(--scene-w) / var(--scene-h));
    /* From the bottom of the window to the bottom of the band. */
    --band-bottom: calc(50svh - var(--dock-room) / 2 - var(--scene-px-h) / 2);
  }
  .scene {
    top: calc(50% - var(--dock-room) / 2);
    width: var(--scene-px-w);
    height: var(--scene-px-h);
  }
  /* Anchored by its bottom, so the card still grows upward over the room. */
  .dock { bottom: calc(var(--band-bottom) + (var(--dock-room) - var(--dock-bar-h)) / 2); }
  .corner-controls {
    bottom: calc(var(--band-bottom) + (var(--dock-room) - 40px) / 2);
    right: max(1.25rem, calc((100vw - var(--scene-px-w)) / 2));
  }
}
/* A finger cannot hover, so dock B shows a caption under each circle: a deeper band. */
@media (min-width: 40.0625rem) and (orientation: landscape) and (not ((hover: hover) and (pointer: fine))) {
  :root { --dock-room: 104px; }
}


/* --- the record player ---------------------------------------
   Bottom left, in the band under the room, opposite the speaker. The record
   is a render and turns (vinyl.js); the sheen stays still over it, the way
   light sits on a real record while it spins.
   ------------------------------------------------------------ */

.vinyl {
  --disc: 70px;
  position: fixed;
  z-index: 10;
  left: 1.25rem;
  bottom: max(1rem, env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 600ms ease 500ms;
}
.is-open .vinyl { opacity: 1; pointer-events: auto; }

.vinyl__disc {
  position: relative;
  flex: none;
  width: var(--disc);
  height: var(--disc);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #0b0a09;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.55), 0 0 0 0.5px rgba(255, 255, 255, 0.14);
  transition: transform 260ms cubic-bezier(0.3, 0.8, 0.2, 1), box-shadow 260ms ease;
}
.vinyl__disc:hover { transform: translateY(-1px) scale(1.05); box-shadow: 0 9px 22px rgba(0, 0, 0, 0.6), 0 0 0 0.5px rgba(255, 255, 255, 0.2); }
.vinyl__disc:focus-visible { outline: 1.5px solid rgba(255, 255, 255, 0.8); outline-offset: 3px; }
.vinyl__record { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; will-change: transform; user-select: none; -webkit-user-drag: none; }
/* Two soft highlights on the grooves, fixed in place; none on the paper label. */
.vinyl__sheen {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  background: conic-gradient(from 210deg, transparent 0deg, rgba(255, 255, 255, 0.2) 26deg, transparent 58deg,
                             transparent 180deg, rgba(255, 255, 255, 0.12) 206deg, transparent 236deg);
  -webkit-mask: radial-gradient(closest-side, transparent 36%, #000 39%, #000 95%, transparent 100%);
  mask: radial-gradient(closest-side, transparent 36%, #000 39%, #000 95%, transparent 100%);
  mix-blend-mode: screen;
}
/* Play or pause, printed small on the label, only while pointing at it. */
.vinyl__glyph {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 15px;
  height: 15px;
  margin: -7.5px 0 0 -7.5px;
  fill: #2b2621;
  opacity: 0;
  transition: opacity 180ms ease;
}
.vinyl__disc:hover .vinyl__glyph,
.vinyl__disc:focus-visible .vinyl__glyph { opacity: 0.85; }
.vinyl .g-pause, .vinyl.is-playing .g-play { display: none; }
.vinyl.is-playing .g-pause { display: inline; }

/* What is on: shown while it plays, or while you point at the player. */
.vinyl__info {
  min-width: 0;
  max-width: 15rem;
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity 300ms ease, transform 300ms ease;
}
.vinyl.is-playing .vinyl__info,
.vinyl:hover .vinyl__info,
.vinyl.is-empty .vinyl__info,
.vinyl:focus-within .vinyl__info { opacity: 1; transform: none; }
.vinyl__title,
.vinyl__artist { margin: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.vinyl__title { font-family: var(--serif); font-size: 15.5px; line-height: 1.3; color: #efe9dc; }
.vinyl__artist { margin-top: 2px; font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(239, 233, 220, 0.5); }

.vinyl__artist a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; transition: color 160ms ease, border-color 160ms ease; }
.vinyl__artist a:hover { color: rgba(239, 233, 220, 0.85); border-bottom-color: rgba(239, 233, 220, 0.4); }

.vinyl__skip { display: flex; gap: 4px; opacity: 0; transition: opacity 200ms ease; }
.vinyl:hover .vinyl__skip,
.vinyl:focus-within .vinyl__skip { opacity: 1; }
.vinyl.is-empty-list .vinyl__skip { display: none; }
.vinyl__skip button {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0.5px solid rgba(255, 255, 255, 0.22);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  color: rgba(239, 233, 220, 0.85);
  cursor: pointer;
  transition: background 160ms ease;
}
.vinyl__skip button:hover { background: rgba(255, 255, 255, 0.16); }
.vinyl__skip svg { width: 11px; height: 11px; fill: currentColor; stroke: currentColor; stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round; }

/* In the band under the room on landscape screens, level with the dock. */
@media (min-width: 40.0625rem) and (orientation: landscape) {
  .vinyl {
    left: max(1.25rem, calc((100vw - var(--scene-px-w)) / 2));
    bottom: calc(var(--band-bottom) + (var(--dock-room) - var(--disc)) / 2);
  }
}
/* Phones: the record bottom left, the speaker bottom right. */
@media (max-width: 40rem), (orientation: portrait) {
  .vinyl { --disc: 58px; gap: 12px; bottom: max(1.1rem, env(safe-area-inset-bottom)); }
  .vinyl__info { max-width: calc(100vw - 11rem); }
  .vinyl__skip { display: none; }
  .corner-controls { right: 1.25rem !important; transform: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .vinyl__disc, .vinyl__info { transition: none; }
}


/* --- touch: ask before leaving ------------------------------- */

.leaveask {
  position: fixed;
  z-index: 12;
  left: 50%;
  bottom: 4.5rem;
  transform: translateX(-50%);
  width: min(19rem, calc(100vw - 2.5rem));
  padding: 0.9rem 1rem 0.8rem;
  text-align: center;
  background: rgba(13, 12, 10, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 3px;
  backdrop-filter: blur(6px);
}
.leaveask[hidden] { display: none; }
.leaveask__text {
  margin: 0 0 0.75rem;
  font-size: 11px;
  line-height: 1.55;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.leaveask__row { display: flex; justify-content: center; gap: 6px; }
.leaveask button { padding: 7px 14px; }
.leaveask button[data-leave-go] { border-color: rgba(255, 241, 214, 0.7); color: #fff; }


/* --- tall screens -------------------------------------------- */

.portrait-head { display: none; }

@media (max-width: 40rem), (orientation: portrait) {
  .dock {
    bottom: auto;
    top: calc(50% + (100vw * var(--scene-h) / var(--scene-w)) / 2 + 1.2rem);
    max-width: calc(100vw - 1.5rem);
  }
  .dock__bar a { padding: 6px 9px 7px; font-size: 13.5px; }
  .dock__panel-inner { padding: 14px 14px 10px; }
  .dock__panel-inner::after { margin: 10px -14px -10px; }
  .corner-controls { bottom: max(1.2rem, env(safe-area-inset-bottom)); right: 50%; transform: translateX(50%); }
  .hint { display: none; }

  .portrait-head {
    display: block;
    position: fixed;
    z-index: 10;
    left: 0;
    right: 0;
    bottom: calc(50% + (100vw * var(--scene-h) / var(--scene-w)) / 2 + 1.1rem);
    padding: 0 1.25rem;
    text-align: center;
    opacity: 0;
    transition: opacity 600ms ease 500ms;
  }
  /* Before the doors open the note speaks here (too small to read on the glass);
     afterwards, just the name. */
  .portrait-head { opacity: 1; transition: none; }
  .portrait-head__closed, .portrait-head__open { transition: opacity 500ms ease; }
  .portrait-head__open { position: absolute; left: 0; right: 0; bottom: 0; opacity: 0; pointer-events: none; }
  .is-open .portrait-head__closed { opacity: 0; pointer-events: none; }
  .is-open .portrait-head__open { opacity: 1; transition-delay: 500ms; }
  .portrait-head__name { margin: 0 0 0.5rem; font-family: var(--serif); font-size: 22px; letter-spacing: 0.01em; }
  .portrait-head__lede { margin: 0 auto 0.9rem; max-width: 19rem; text-wrap: pretty; font-family: var(--serif); font-size: 15px; line-height: 1.5; color: rgba(239, 233, 220, 0.8); }
  .portrait-head__zh { margin: -0.3rem 0 0.9rem; font-family: 'LXGW WenKai', 'Kaiti SC', serif; font-size: 16px; letter-spacing: 0.06em; color: rgba(239, 233, 220, 0.85); }
  .portrait-head__note { margin: 0; font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(239, 233, 220, 0.55); }
  .enter--phone { color: #efe9dc; font-family: var(--serif); font-size: 16px; border-bottom-color: rgba(239, 233, 220, 0.5); }
  .enter--phone:hover { border-bottom-color: #efe9dc; }
  .note { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .slider, .is-open .slider { transition-duration: 400ms; }
  .note { animation: none; }
  .leaf--full, .is-open .leaf--full { transition: none; }
  .shake.is-cranking, .shake.is-idling { animation: none; }
  .steam i { animation: none; opacity: 0.2; }
  .sunlight, .is-night .lampglow { animation: none; }
  html.is-departing .photo, html.is-departing .linework { transition: none; }
  html.is-lifting .scene { transition: opacity 200ms ease; transform: translate(-50%, -50%); }
}
