/* ============================================================================
   Teras AI, attempt 2: Lamplit rooms.
   Six colours and three faces on top of the engine. Everything bespoke below
   is page-local and driven off --sc-p, --lit, --lx, --ly. The engine files are
   untouched.
   ========================================================================== */

:root {
  /* Warm dark. The campus is shot after dark, so the page carries the same
     night: warm off-black ground, bone ink, lamplight amber. The about plate
     stays the one light room, so the hero's bloom still lands on the
     lightest ground on the page and the cut back to dark reads as a cut. */
  --sc-canvas:      #14100C;
  --sc-surface:     #1F1913;
  --sc-ink:         #F4ECDF;
  --sc-ink-soft:    #A79883;
  --sc-accent:      #F2A93B;
  --sc-accent-ink:  #14100C;
  --accent-text:    #F0B65E;   /* the accent's text stop on the dark ground */

  --sc-font-display: "Bricolage Grotesque", "Instrument Sans", system-ui, sans-serif;
  --sc-font-text:    "Instrument Sans", system-ui, sans-serif;
  --sc-font-mono:    "Geist Mono", ui-monospace, "SF Mono", monospace;

  --sc-shadow-color: 30 30% 12%;
  --sc-r-sm: 4px; --sc-r-md: 8px; --sc-r-lg: 14px;

  /* the light */
  --warm: #FFD9A8;
  --lamp: #FFB45C;
  --paper: #F0E7D8;
  --stone: 20, 16, 12;      /* canvas as an rgb triple for washes */
}

/* --lit is written by page.js. Registered so reduced motion can transition it
   instead of stepping. */
@property --lit { syntax: "<number>"; inherits: true; initial-value: 0; }
@property --m   { syntax: "<number>"; inherits: true; initial-value: 0; }
/* --door-hover is written by page.js on the door: one eased value that the
   lamp and the words both read, so they swell together. */
@property --door-hover { syntax: "<number>"; inherits: true; initial-value: 0; }

/* The gutter is reserved at all times so the vault's scroll lock (which sets
   overflow: hidden on this element) cannot shift the page sideways. The page
   always scrolls, so at rest this changes nothing. */
html { background: var(--sc-canvas); scrollbar-gutter: stable; }

/* ---- back and forward ---------------------------------------------------- */
/* A Back press cannot be delayed by the page: the browser reports the traversal
   as cancelable:false, canIntercept:false, so there is no way to fade out of a
   document on the way to one the reader has already seen. The one thing that
   can is the browser's own cross-document transition, which holds the outgoing
   page and blends it into the incoming one — a fade out and a fade in at once,
   and no black in between.

   It is turned on for traversals ONLY. The page script skips it for a link
   click, where our own fade to the shared ground is the better move and the one
   case we actually control. Measured: with the skip in place a link click is
   frame-for-frame what it was, and a Back press stops flashing black.

   Browsers without cross-document transitions ignore all of this and keep the
   instant cut they have today. */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 300ms; }

/* ======================================================= THE ARRIVAL ===== */
/* The page used to go blank -> whole in one step: everything at full opacity
   the moment it painted. Nothing here changes what the page looks like at
   rest — every rule below lives behind .is-entering / .is-leaving, and when
   neither class is on <html> not one of them applies. The pseudo-element
   below does not exist unless a class is present, so a reader without JS,
   or one who asked for less motion, gets exactly the page that shipped.

   The values are written by page.js, one eased 0->1 per frame, the same way
   every other effect on this page is driven. */
/* Land the anchor, do not travel to it. The stylesheet asks for smooth
   scrolling, which on a fresh document turns "open at #services" into an
   animated scroll from the top — and the reader watches it pass the About
   plate through the lifting veil, as a flash of cream. Instant while the
   arrival is on; smooth returns with the class. */
html.is-entering { scroll-behavior: auto; }
html.is-entering::before, html.is-leaving::before {
  content: ""; position: fixed; inset: 0; z-index: 2147483646; pointer-events: none;
  background: var(--sc-canvas);
  opacity: var(--enter-veil, 1);
}
/* The three things that arrive in order. Each is an element the engine does
   NOT write an inline opacity onto — the cues own .hall__copy's children, so
   the stagger rides the parent and leaves the cue system untouched. */
html.is-entering .hall__name { opacity: var(--enter-name, 0); }
html.is-entering .hall__copy { opacity: var(--enter-copy, 0); }
html.is-entering .panel     { opacity: var(--enter-panel, 0); }
body { font-weight: 400; }
.sc-display { font-weight: 560; font-optical-sizing: auto; }
.sc-display--xl { letter-spacing: -0.04em; }
.sc-body { font-size: var(--sc-t-base); }
.sc-label { font-weight: 500; }
strong { font-weight: 600; }

/* --------------------------------------------------------- the switch panel */
.panel {
  position: fixed; top: 1rem; right: 1rem; z-index: var(--sc-z-chrome);
  display: flex; align-items: stretch; gap: .6rem;
  padding: .45rem .55rem .45rem .95rem;
  background: var(--sc-surface); color: var(--sc-ink);
  border: 1px solid var(--sc-hairline);
  border-radius: var(--sc-r-md);
  box-shadow: var(--sc-e2), inset 0 1px 0 rgba(255,255,255,.55);
}
.panel__mark {
  display: flex; align-items: center; gap: .35rem;
  font-family: var(--sc-font-display); font-weight: 640; font-size: 1rem;
  letter-spacing: -0.015em; text-decoration: none;
  padding-right: .8rem; margin-right: .15rem;
  border-right: 1px solid var(--sc-hairline);
}
.panel__mark span { color: var(--sc-ink-soft); font-weight: 500; }
.panel__switches { list-style: none; margin: 0; padding: 0; display: flex; gap: .15rem; }
.switch {
  display: flex; flex-direction: column; align-items: center; gap: .32rem;
  min-width: 2.85rem; padding: .3rem .25rem .25rem;
  text-decoration: none; color: var(--sc-ink-soft);
  border-radius: var(--sc-r-sm);
  transition: color var(--sc-d-fast) var(--sc-ease-out), background var(--sc-d-fast) var(--sc-ease-out);
}
.switch:hover { background: color-mix(in oklab, var(--sc-ink) 6%, transparent); color: var(--sc-ink); }
.switch:active { transform: translateY(1px); }
.switch__rocker {
  position: relative; width: 12px; height: 22px; border-radius: 3px;
  background: #2C241C;
  box-shadow: inset 0 1px 1px rgba(0,0,0,.18), inset 0 0 0 1px rgba(0,0,0,.12);
}
.switch__rocker i {
  position: absolute; left: 2px; right: 2px; top: 11px; height: 9px; border-radius: 2px;
  background: #6E5F4C;
  transition: transform var(--sc-d-base) var(--sc-ease-out), background var(--sc-d-base) var(--sc-ease-out);
}
.switch.is-on .switch__rocker i { transform: translateY(-9px); background: #C9B08A; }
.switch__lamp {
  width: 5px; height: 5px; border-radius: 50%; background: #4A3F33;
  transition: background var(--sc-d-base) var(--sc-ease-out), box-shadow var(--sc-d-base) var(--sc-ease-out);
}
.switch.is-on .switch__lamp { background: var(--sc-accent); box-shadow: 0 1px 5px 1px color-mix(in oklab, var(--sc-accent) 45%, transparent); }
.switch.is-current .switch__lamp { background: #FFC85A; }
.switch__label {
  font-family: var(--sc-font-mono); font-size: .56rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; line-height: 1;
}
.switch.is-on .switch__label { color: var(--sc-ink); }
.switch.is-current .switch__label { color: var(--accent-text); }

/* ------------------------------------------------------------------ rooms */
.room { --lit: 0; --lx: 50; --ly: 50; position: relative; }
.room__media { position: absolute; inset: 0; overflow: clip; }
.room__media > img, .room__media > picture > img, .room__media > video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; max-width: none;
  -webkit-touch-callout: none; user-select: none;
  /* The resting state on a night photograph: the room before the light
     comes up. The subpages rest at .88; this page is walked through rather than
     read, so it sits deeper — but not so deep that the building reads as having
     the power cut. The lit layer beneath is the photograph. */
  filter: brightness(.78) saturate(.92);
}
.room__warm {
  position: absolute; inset: 0; pointer-events: none;
  filter: saturate(1.06) brightness(1.03);
  opacity: var(--lit);
  -webkit-mask-image: radial-gradient(circle at calc(var(--lx) * 1%) calc(var(--ly) * 1%),
    #000 0%, #000 calc(var(--lit) * var(--lit) * 92%), transparent calc(var(--lit) * var(--lit) * 92% + 44%));
  mask-image: radial-gradient(circle at calc(var(--lx) * 1%) calc(var(--ly) * 1%),
    #000 0%, #000 calc(var(--lit) * var(--lit) * 92%), transparent calc(var(--lit) * var(--lit) * 92% + 44%));
}
.room__warm img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; max-width: none; }
.room__tint {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 100% at calc(var(--lx) * 1%) calc(var(--ly) * 1%),
    rgba(255,172,92,.6) 0%, rgba(255,140,70,.2) 42%, transparent 74%);
  mix-blend-mode: soft-light;
  opacity: calc(var(--lit) * .7);
}
.lamp { position: absolute; left: calc(var(--lx) * 1%); top: calc(var(--ly) * 1%); width: 0; height: 0; pointer-events: none; z-index: 2; }
.lamp > i {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  display: block; border-radius: 50%; mix-blend-mode: screen; will-change: transform, opacity;
}
.lamp__bloom {
  width: 130vmin; height: 130vmin;
  background: radial-gradient(circle, rgba(255,178,96,.36) 0%, rgba(255,150,72,.15) 28%, transparent 60%);
  opacity: calc(var(--lit) * var(--lit));
  transform: scale(calc(.3 + var(--lit) * 1.15));
}
.lamp__core {
  width: 16vmin; height: 16vmin;
  background: radial-gradient(circle, rgba(255,242,214,.96) 0%, rgba(255,212,144,.62) 20%, rgba(255,172,84,.22) 45%, transparent 68%);
  opacity: calc(.1 + var(--lit) * .9);
  transform: scale(calc(.5 + var(--lit) * 1.35));
}
/* copy warms with the room */
/* --ink-warm is an rgb() written by page.js (the ink mixed toward the lamp by
   the room's light level), so the computed colour stays a plain rgb. */
.room .sc-copy, .room .roomcopy { color: var(--ink-warm, var(--sc-ink)); }
/* captions parked in the dark, paid out by the hold */
.room__note {
  position: absolute; margin: 0; z-index: 4; pointer-events: none;
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); letter-spacing: .08em; text-transform: uppercase;
  /* Warm bone: this caption arrives in the dark parts of a night room as its
     lamp comes up, so it is read against dark, never against the light. */
  color: #FCEFD8;
  opacity: clamp(0, calc((var(--lit) - .58) * 2.6), 1);
  max-width: 16rem; text-wrap: pretty; line-height: 1.5;
}
.room__note::before { content: ""; display: inline-block; width: 1.4rem; height: 1px; background: currentColor; vertical-align: middle; margin-right: .5rem; opacity: .7; }

/* The doorway on hover: the lamp drives past where the page alone can take it,
   so the swell still reads when the close is already lit. Same three layers the
   hold moves, pushed by --door-hover instead of --lit. */
.door .lamp__bloom {
  opacity: calc(var(--lit) * var(--lit) + var(--door-hover, 0) * .3);
  transform: scale(calc(.3 + var(--lit) * 1.15 + var(--door-hover, 0) * .26));
}
.door .lamp__core {
  opacity: calc(.1 + var(--lit) * .9);
  transform: scale(calc(.5 + var(--lit) * 1.35 + var(--door-hover, 0) * .34));
}
.door .room__tint { opacity: calc(var(--lit) * .7 + var(--door-hover, 0) * .18); }
.door .room__warm { filter: saturate(calc(1.06 + var(--door-hover, 0) * .1)) brightness(calc(1.03 + var(--door-hover, 0) * .09)); }

/* the lamp control: keyboard and screen-reader route, and the hint */
.lamp-btn {
  position: absolute; left: calc(var(--lx) * 1%); top: calc(var(--ly) * 1%); translate: -50% -50%;
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; cursor: pointer; z-index: 26;
}
.lamp-btn::before {
  content: ""; position: absolute; inset: 16px; border-radius: 50%;
  background: var(--sc-accent); box-shadow: 0 0 0 1px rgba(255,225,180,.4), 0 1px 2px rgba(0,0,0,.4);
  transition: transform var(--sc-d-fast) var(--sc-ease-out);
}
.lamp-btn::after {
  content: ""; position: absolute; inset: 9px; border-radius: 50%;
  border: 1px solid rgba(233,150,46,.7);
  animation: lamp-ring 2.6s var(--sc-ease-out) infinite;
}
@keyframes lamp-ring { 0% { transform: scale(.55); opacity: .95; } 100% { transform: scale(1.7); opacity: 0; } }
.lamp-btn:hover::before, .lamp-btn:focus-visible::before { transform: scale(1.25); }
.lamp-btn[aria-pressed="true"]::before { transform: scale(1.45); background: #FFE6BD; }
.lamp-btn[aria-pressed="true"]::after { animation: none; opacity: 0; }
.is-holding { cursor: grabbing; }

/* a scrim that is a SIBLING of the copy it protects, never a child */
.plate-scrim {
  position: absolute; pointer-events: none; z-index: 19;
  background: radial-gradient(closest-side, rgba(var(--stone),.94), rgba(var(--stone),.72) 50%, transparent 100%);
}

.cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 3rem; padding: 0 1.4rem;
  font-family: var(--sc-font-text); font-weight: 600; font-size: var(--sc-t-sm); letter-spacing: -0.005em;
  color: var(--sc-accent-ink); background: var(--sc-accent);
  border-radius: var(--sc-r-md); text-decoration: none; white-space: nowrap;
  box-shadow: var(--sc-e1), inset 0 1px 0 rgba(255,255,255,.28);
  transition: transform var(--sc-d-fast) var(--sc-ease-out), background var(--sc-d-fast) var(--sc-ease-out);
}
.cta:hover { background: #FFBE5A; }
.cta:active { transform: scale(.97); }

/* ================================================================== 1 HALL */
.hall__stage { background: var(--sc-canvas); isolation: isolate; }
.hall__plane { position: absolute; inset: 0; will-change: transform; z-index: 1; }
.hall__plane img, .hall__plane picture img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; max-width: none;
  /* Click-drag across the hero starts a text selection. Chromium then paints
     selected images with ::selection — the brand amber — so the doors flash
     a clipped highlight. The photographs are not copy. */
  -webkit-touch-callout: none; user-select: none;
}
.hall__plane--far { transform-origin: calc(var(--lx) * 1%) calc(var(--ly) * 1%); }
/* Hero resting shade, tuned against the real 41-floor/45-floor-p frames rather
   than the placeholder .62 this started at. At .78 the photograph reads at
   rest — desks, monitors, the courtyard through the glass — while the frame
   still sits in genuine night. Measured swell on hold is 2.44x mean luminance
   (0.0489 -> 0.1193), against 2.64x at .62: 8% less headroom for 26% more
   resting exposure. The four service rooms, the corridor and the door keep
   their own .62 on .room__media above; this rule is hero-only. */
.hall__plane--far > picture > img { filter: brightness(.78); }
.hall__plane--far .room__warm { z-index: 1; }
.hall__plane--far .lamp { z-index: 2; }
.hall__plane--atmos { z-index: 2; transform-origin: 62% 30%; mix-blend-mode: screen; opacity: calc(.22 + var(--lit) * .5); }
/* The colonnade is a wall with slits, so it is used as the near pair of
   columns you stand between: two halves of the same cutout, scaled up and
   parted by scroll so the hall opens between them. Near planes are softer. */
.hall__plane--mid { z-index: 3; transform-origin: 50.5% 50%; filter: blur(.5px); }
.hall__plane--mid-l { clip-path: inset(0 49.6% 0 0); }
.hall__plane--mid-r { clip-path: inset(0 0 0 49.6%); }
.hall__plane--mid img { pointer-events: none; }
/* This pool seats .hall__copy; it is not a grade on the room. At 68% x 56% from
   50% 90% at .97 it washed the whole lower third, costing the floor 39% of its
   luminance in the lower band and 55% in the bottom against the raw 41-floor
   frame — the desks, the monitors and the light down the boards going flat.
   Re-centred on the lede rather than the frame and tightened: the lower band
   now reads 33.4 against the raw file's 33.9, so the floor is effectively the
   photograph again.
   It cannot simply go, and the mean is a liar here — with no scrim the lede
   averages a comfortable 60 but a sun-flare patch behind it peaks at 169, which
   is 2.0:1 and unreadable. Sized against that WORST tile, not the average:
   8.1:1 at the darkest useful setting, well clear of the 4.5:1 floor for text
   this size. Anything wider or weaker buys single-digit luminance and costs
   real contrast — the sweep is in the session that made this change.
   The haze above is mix-blend-mode: screen: it can only ever add light, and was
   never what dimmed this frame. */
.hall__scrim { position: absolute; inset: 0; z-index: 18; pointer-events: none;
  background: radial-gradient(34% 22% at 50% 80%, rgba(var(--stone),.72), rgba(var(--stone),.6) 44%, rgba(var(--stone),.28) 70%, transparent 88%); }
/* The copy sits below the slot of light, never across it: the light is the
   hero and the headline is its caption. */
.hall__copy {
  position: absolute; z-index: 20; left: 50%; bottom: clamp(2.6rem, 6vh, 5rem); translate: -50% 0;
  width: min(54rem, 90vw); text-align: center; will-change: opacity, transform;
}
.hall__copy h1 { max-width: 14ch; margin-inline: auto; font-size: clamp(3rem, 2rem + 4.6vw, 6rem); }
.hall__copy .sc-lede { margin: var(--sc-4) auto 0; max-width: 44ch; font-size: var(--sc-t-base); color: #EFE3D2; }
.hall__copy .cta { margin-top: var(--sc-5); }
.hall__hint {
  position: absolute; z-index: 25; margin: 0;
  left: var(--sc-gutter); bottom: 2.2rem;
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); letter-spacing: .1em; text-transform: uppercase;
  color: #F0E2CE; white-space: nowrap;
  transition: visibility 0s linear 400ms;
}
.hall__hint::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--sc-accent); margin-right: .7rem; vertical-align: 1px; }
.hall__hint.is-seen { visibility: hidden; }
.hall__hint-plate { left: calc(var(--sc-gutter) - 7rem); bottom: -5rem; width: 30rem; height: 13rem; z-index: 24; opacity: 0; }
.hall__bloom {
  position: absolute; inset: 0; z-index: 30; pointer-events: none;
  background: var(--paper);
  clip-path: circle(0% at calc(var(--lx) * 1%) calc(var(--ly) * 1%));
  will-change: clip-path;
}
.hall__bloom::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle at calc(var(--lx) * 1%) calc(var(--ly) * 1%), #FFF6E6 0%, #FBEFDC 9%, var(--paper) 22%);
  opacity: var(--hot, 1);
}
.hall .lamp-btn {
  /* Control only. The swell still originates from --lx/--ly on the sun.
     Sit under the wordmark (top: 43%) in the open band above the headline. */
  left: 50%;
  top: 43%;
  translate: -50% 2.25rem;
  z-index: 26;
}

/* The logo is the hero's near plane. It is the site's own mark (paths lifted
   from components/ui/TerasLogoSvg.tsx on main), and it stands in frame at
   rest, then lifts and drifts up out of the top as the dolly starts — the
   layer that clears first, ahead of the panels parting. page.js writes its
   transform and opacity from the act's progress; nothing here is timed, so
   there is no entrance to skip, cancel or replay. The soft warm halo is what
   makes it read as lit rather than pasted onto the frame. */
.hall__name {
  position: absolute; z-index: 22; left: 50%; top: 43%; translate: -50% -50%; margin: 0;
  width: min(46rem, 68vw); pointer-events: none; will-change: transform, opacity;
}
/* The halo moved off .hall__name and onto the mark itself: the plate below is a
   child, and a filter on the parent would hang a warm glow around a dark cloud.
   Same two shadows, same only child, so the mark is unchanged. */
.hall__name-svg { display: block; width: 100%; height: auto; overflow: visible;
  filter: drop-shadow(0 0 26px rgba(255,214,150,.5)) drop-shadow(0 0 66px rgba(255,196,120,.34)); }
/* The same plate the service rooms put behind their copy, sized to the mark.
   The lit slot in the clip runs straight behind the divider — the flare tile
   there measures 154 against ink at 237, which is 2.4:1, and against the
   softer AI at 154 it is 1.0:1: identical luminance, so those two letters are
   photometrically invisible. This seats the mark on its own ground.
   It is a child rather than a sibling on purpose. .hall__name already carries
   the lift, the scale and the fade (page.js writes lfade: full to q 0.16, gone
   by 0.46), and a child inherits transform AND opacity, so the plate leaves
   with the mark for free — no JS, unlike .rooms__plate which has to copy an
   opacity it cannot inherit. */
.hall__name::before {
  content: ""; position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  z-index: -1; pointer-events: none; width: 170%; height: 340%;
  background: radial-gradient(closest-side, rgba(var(--stone),.9) 0%, rgba(var(--stone),.78) 42%, rgba(var(--stone),.38) 70%, transparent 100%);
}
.hall__name .nm { fill: var(--sc-ink); }
.hall__name .nm--soft { fill: var(--sc-ink-soft); }
.hall__name .nm--div { fill: none; stroke: var(--sc-ink-soft); }

/* ================================================================= 2 PLATE */
.plate {
  --sc-canvas: #F0E7D8; --sc-surface: #E8DFCF;
  --sc-ink: #1F1712; --sc-ink-soft: #5C4F42;
  --sc-accent: #E9962E; --sc-accent-ink: #1F1712;
  /* the one light room keeps the dark-on-light accent stop, so links and
     focus rings inside it stay legible against the paper */
  --accent-text: #8A4A15;
  --sc-hairline: color-mix(in oklab, var(--sc-ink) 14%, transparent);
  background: var(--sc-canvas); color: var(--sc-ink);
  position: relative; z-index: 2;
  /* One viewport of paper so a hash jump to #about cannot leave a sliver of
     the services pin (and "Four ways in") sitting under the plate. */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.plate__inner {
  box-sizing: border-box;
  width: 100%;
  padding: clamp(2.5rem, 7vh, 5.5rem) var(--sc-gutter);
  max-width: var(--sc-maxw);
  margin-inline: auto;
}
.plate h2 { max-width: 15ch; }
.plate__rule { margin-top: var(--sc-8); }
.plate__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: var(--sc-6) var(--sc-8); margin-top: var(--sc-6); }
.plate__cols .sc-body { max-width: 44ch; color: var(--sc-ink-soft); font-size: var(--sc-t-lg); line-height: 1.5; letter-spacing: var(--sc-track-snug); }
.plate__cols .sc-body strong { color: var(--sc-ink); font-weight: 600; }
.plate__mark { font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); letter-spacing: .12em; text-transform: uppercase; color: var(--sc-ink-soft); margin: 0 0 var(--sc-6); }

/* ================================================================= 3 ROOMS */
.rooms__stage { background: var(--sc-canvas); overflow: clip; isolation: isolate; }
.roomcol {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  overflow: clip; transform-origin: 0 0; will-change: transform;
}
.roomcol__inner { position: absolute; inset: 0; transform-origin: 0 0; will-change: transform; }
.roomcol__inner .room__media img { object-fit: cover; }
.roomcol::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 2px;
  background: var(--sc-canvas); z-index: 5;
}
.roomcol:first-child::after { display: none; }
.rooms__name {
  position: absolute; left: 0; bottom: clamp(1.6rem, 6vh, 4rem); z-index: 8; margin: 0;
  font-family: var(--sc-font-display); font-weight: 560; font-size: var(--sc-t-lg); letter-spacing: -0.02em;
  color: var(--sc-ink);
  /* A white glow was fine while this was decoration on a dark strip. It is now
     the control that opens the room, and a collapsed strip can show any part of
     its photograph: on a lit one, bone on a white halo disappeared (1.12:1).
     A dark shadow reads on both. */
  text-shadow: 0 1px 2px rgba(0,0,0,.72), 0 0 16px rgba(0,0,0,.78);
  will-change: transform, opacity; white-space: nowrap;
  /* The name was already the label on a shut room, so it becomes the control
     that opens it: the affordance was on screen, it just was not real. */
  background: none; border: 0; padding: .3rem .5rem; cursor: pointer;
  display: inline-block;
}
/* No plus: the whole room is the target, so the mark was labelling a control
   that is the size of the photograph. The name alone is the affordance. */
.rooms__name:hover { color: #FFFFFF; }
.rooms__name:active { transform: translateY(1px); }
.rooms__intro {
  position: absolute; z-index: 20; left: var(--sc-gutter); top: clamp(4.5rem, 12vh, 8rem); margin: 0;
  max-width: 14ch;
}
.roomcopy {
  position: absolute; z-index: 20; bottom: clamp(3.4rem, 13vh, 8rem);
  width: min(30rem, 42vw); translate: -50% 0;
  will-change: opacity, transform;
}
.roomcopy h3 { margin: 0; }
.roomcopy .sc-body { margin-top: var(--sc-4); color: #EFE3D2; max-width: 34ch; }
.roomcopy__fact {
  margin: var(--sc-5) 0 0; font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  letter-spacing: .06em; text-transform: uppercase; color: #E9A74A; line-height: 1.6;
}
/* The card opens in place. Scroll still chooses which room is open; this
   only says how much of that room you are shown, and it lets go the moment
   the accordion moves on, so there is no state to get stuck in. */
.roomcopy__more {
  --m: 0;
  overflow: hidden;
  height: calc(var(--m) * var(--mh, 0px));
  opacity: clamp(0, (var(--m) - .2) * 2, 1);
  transition: --m 340ms var(--sc-ease-out), opacity 240ms var(--sc-ease-out);
}
.roomcopy.is-open .roomcopy__more { --m: 1; }
.roomcopy__more > * { display: flow-root; padding-top: var(--sc-4); }
.roomcopy__more ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.roomcopy__more li {
  position: relative; padding-left: 1.5rem; color: #EFE3D2;
  font-size: var(--sc-t-sm); line-height: 1.4;
}
.roomcopy__more li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: .8rem; height: 1px; background: var(--accent-text);
}
.roomcopy__actions { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sc-3) var(--sc-5); margin-top: var(--sc-4); }
.roomcopy__toggle {
  display: inline-flex; align-items: center; gap: .5rem; cursor: pointer;
  padding: .4rem .8rem .4rem .6rem; border-radius: var(--sc-r-sm);
  background: none; color: var(--sc-ink-soft);
  border: 1px solid color-mix(in oklab, var(--sc-ink) 20%, transparent);
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  letter-spacing: .08em; text-transform: uppercase;
  transition: color 160ms var(--sc-ease-out), border-color 160ms var(--sc-ease-out);
}
.roomcopy__toggle::before {
  content: ""; width: 9px; height: 9px; flex: none;
  background:
    linear-gradient(currentColor, currentColor) center/9px 1px no-repeat,
    linear-gradient(currentColor, currentColor) center/1px 9px no-repeat;
  transition: transform 300ms var(--sc-ease-out);
}
.roomcopy__toggle:hover { color: var(--sc-ink); border-color: var(--sc-ink-soft); }
.roomcopy__toggle[aria-expanded="true"] { color: var(--accent-text); border-color: var(--accent-text); }
.roomcopy__toggle[aria-expanded="true"]::before {
  transform: rotate(90deg);
  background: linear-gradient(currentColor, currentColor) center/9px 1px no-repeat;
}
.roomcopy__link {
  /* No margin-top: the link used to be a block on its own line, and inside the
     actions row that margin just pushed it 12px below the toggle's text. */
  display: inline-block; font-weight: 600; font-size: var(--sc-t-sm);
  color: var(--sc-ink); text-decoration-color: var(--accent-text); text-decoration-thickness: 1.5px;
}
.roomcopy__link:hover { color: var(--accent-text); }
.rooms__plate {
  position: absolute; z-index: 19; bottom: -4rem; translate: -50% 0; pointer-events: none;
  width: min(62rem, 76vw); height: min(40rem, 78vh);
  background: radial-gradient(closest-side, rgba(var(--stone),.96) 0%, rgba(var(--stone),.92) 48%, rgba(var(--stone),.62) 74%, transparent 100%);
  opacity: 0;
}
.rooms__intro-plate { z-index: 19; left: calc(var(--sc-gutter) - 10rem); top: -6rem; width: 40rem; height: 30rem; translate: 0; bottom: auto; }

/* ============================================================== 4 CORRIDOR */
/* A section exactly one viewport tall has exactly ONE scroll position that
   fills the screen, so a click on WORK lands it and a mouse wheel never does.
   The extra 12svh is a landing window — 92 to 173px depending on the
   viewport, about a wheel notch — anywhere in which the section still holds
   the whole frame.
   6 and not 12: the ledger is now centred on the SECTION rather than on the
   first viewport of it (see below), so the window is the whole error budget —
   the list sits half a window low on a WORK click and half a window high at
   the far end of it. 12svh put that at 48-79px measured across 1440x900 to
   1728x1080; 6svh halves it to 24-47px and still leaves a landing window of
   ~50px, and the ledger's top does not clip at any size tested. */
.corridor { --corridor-h: 106svh;
  position: relative; min-height: var(--corridor-h); background: var(--sc-canvas); overflow: clip; }
.corridor__bed {
  position: absolute; inset: 0; margin: 0; z-index: 1;
  -webkit-mask-image: linear-gradient(to right, #000 34%, transparent 74%);
  mask-image: linear-gradient(to right, #000 34%, transparent 74%);
}
.corridor__bed .room__media { inset: -8% 0; }
.corridor__wrap {
  position: relative; z-index: 2; max-width: var(--sc-maxw); margin-inline: auto;
  padding: clamp(3rem, 10vh, 7rem) var(--sc-gutter) clamp(4rem, 10vh, 7rem);
  display: grid; grid-template-columns: minmax(0, .72fr) minmax(0, 1.28fr); gap: var(--sc-8);
}
.corridor__lead {
  align-self: end; position: relative;
  /* Same vertical travel as .ledger (100% = row height ~ ledger). Lead was
     left at the unshifted row foot (upper half of #work) while the list
     dropped to centre; this mirrors it into the lower half without touching
     .ledger rules. */
  top: calc((var(--corridor-h) - 100%) / 2 - clamp(3rem, 10vh, 7rem));
  /* Nudge left only — ledger unchanged. */
  left: calc(-1 * clamp(1.25rem, 3vw, 2.75rem));
}
/* The bridge's lit displays sit directly behind this block on the dark
   ground, so the lead carries its own wash to keep the sub-copy legible.
   Same --stone mechanism as every other scrim on the page. */
.corridor__lead::before {
  content: ""; position: absolute; z-index: -1; inset: -2.5rem -3.5rem;
  background: radial-gradient(closest-side, rgba(var(--stone),.94), rgba(var(--stone),.78) 56%, transparent 100%);
  pointer-events: none;
}
.corridor__lead .sc-body { margin-top: var(--sc-4); max-width: 26ch; }
/* .corridor__wrap is capped at --sc-maxw and centred, so the ledger's right
   edge stops well short of the viewport on a wide screen. Shift the list so
   it holds a 6rem (96px) inset from the viewport edge: the two margins are
   equal and opposite, so the block moves without resizing, and the half-gap
   is floored at 0 so nothing shifts below the cap. .corridor is overflow:
   clip, so the negative edge can never reach the scrollbar. */
.ledger {
  --ledger-shift: calc(var(--sc-gutter) - 6rem + max(0px, (100vw - var(--sc-maxw)) / 2));
  list-style: none; padding: 0; border-top: 1px solid var(--sc-hairline-strong);
  margin: 0 calc(-1 * var(--ledger-shift)) 0 var(--ledger-shift);
  /* Right column only. .corridor__lead stays align-self: end. The wrap's row
     is content-tall (the ledger is the tall item), so align-self: center has
     no travel; the shift below does the centring.

     This reference used to be 100svh, on the reasoning that the WORK anchor
     parks the section's TOP at the viewport's top, so the frame a reader
     arrives in is the first viewport of the section — centre on the frame,
     not the box. That is right about arrival and wrong about everything
     after it: reading down, the list sat 40-51px above the section's own
     middle, with visibly more air below it than above. The old note here
     warned that centring on the box costs 122px on the WORK click; measured,
     it costs 48-79px, and cutting the landing window above to 6svh brings
     that to 24-47px. The box wins, and the window pays for it.

     --corridor-h, not a literal: it is the section's min-height, so the two
     cannot drift apart. If the list ever grows past the section's height this
     stops being a centring and starts being an offset. */
  align-self: center;
  position: relative;
  top: calc((var(--corridor-h) - 100%) / 2 - clamp(3rem, 10vh, 7rem));
}
/* The list's own wash, the same device .corridor__lead::before uses and for
   the same reason: the bridge is still lit where the names start. Sampled at
   1440x900, the backdrop under the name column reads ~60 of 255 against a
   ground of ~15, and only reaches ground-dark at x=750 — so a third of every
   name sat on lit photograph.

   It has to hang off the LIST rather than off the picture. The ledger's left
   edge travels from 36%vw at 900px to 69%vw at 2560px, so no fixed stop on
   the bed's own mask can clear the names at both ends; anchored here it
   tracks them for free. Feathered on every side, because a hard edge laid
   across a photograph reads as a rectangle of smoked glass. */
.ledger::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -3rem 0 -6rem -14rem;
  background: linear-gradient(to right,
    transparent 0%,
    rgba(var(--stone), .40) 11%,
    rgba(var(--stone), .88) 25%,
    rgba(var(--stone), .88) 43%,
    rgba(var(--stone), .32) 63%,
    transparent 82%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 9%, #000 88%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 9%, #000 88%, transparent 100%);
}
.ledger__row {
  position: relative;
  display: grid; grid-template-columns: 9.5rem minmax(0, 1fr); gap: var(--sc-1) var(--sc-6);
  padding: var(--sc-3) 0; border-bottom: 1px solid var(--sc-hairline);
}
/* The hover was a background on the row box: it faded to the right and was
   cut dead straight on the other three sides, so it read as a rectangle of
   tinted glass rather than as light. A pseudo-element instead, bleeding past
   the row vertically and feathered at both ends, with the horizontal ramp
   starting just inside the left edge so there is no vertical seam either.
   Nothing reaches left of the row except the accent, which lands inside the
   wash above and so never touches the photograph. */
.ledger__row::before {
  content: ""; position: absolute; z-index: -1; inset: -0.35rem 0; pointer-events: none;
  border-radius: var(--sc-r-lg); opacity: 0;
  transition: opacity 340ms var(--sc-ease-out);
  background: linear-gradient(to right,
    transparent 0%,
    color-mix(in oklab, var(--warm) 10%, transparent) 7%,
    color-mix(in oklab, var(--warm) 6%, transparent) 36%,
    transparent 78%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 28%, #000 72%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 28%, #000 72%, transparent 100%);
}
.ledger__row::after {
  content: ""; position: absolute; z-index: -1; left: -0.85rem; top: 12%; bottom: 12%; width: 2px;
  opacity: 0; pointer-events: none; transition: opacity 340ms var(--sc-ease-out);
  background: color-mix(in oklab, var(--accent-text) 72%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 26%, #000 74%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 26%, #000 74%, transparent);
}
/* any-hover, not hover. `hover` asks about the PRIMARY input, so on a
   touchscreen laptop — a Surface, a 2-in-1, any touch-enabled notebook — it
   reports no hover and the highlight never appeared, mouse plugged in or not.
   `any-hover` asks whether anything attached can hover: a hybrid laptop gets
   the highlight, a phone still does not, so a tap never leaves a row lit. */
@media (any-hover: hover) {
  .ledger__row:hover::before, .ledger__row:hover::after { opacity: 1; }
  .ledger__row:hover .ledger__name { color: var(--accent-text); }
}
.ledger__name {
  margin: 0; font-family: var(--sc-font-display); font-weight: 600; font-size: var(--sc-t-lg);
  letter-spacing: -0.02em; line-height: 1.1; transition: color 340ms var(--sc-ease-out);
}
.ledger__copy {
  display: flex; flex-direction: column; gap: var(--sc-1); min-width: 0;
}
.ledger__what {
  margin: 0; color: var(--sc-ink-soft); max-width: 52ch; text-wrap: pretty;
}
.ledger__meta {
  margin: 0; display: flex; flex-wrap: wrap; gap: .4rem 1.1rem;
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); letter-spacing: .06em; text-transform: uppercase; color: var(--sc-ink-soft);
}
.ledger__meta b { font-weight: 500; color: #E9A74A; }
.ledger__meta > *::before {
  content: "•";
  margin-right: .45em;
}
.ledger__cta,
.ledger__status {
  color: var(--accent-text);
  letter-spacing: inherit;
  text-transform: inherit;
}
.ledger__cta {
  text-decoration: none;
}
.ledger__cta:hover,
.ledger__cta:focus-visible { color: var(--sc-ink); }
.ledger__cta:focus-visible {
  outline: 1px solid color-mix(in oklab, var(--accent-text) 60%, transparent);
  outline-offset: 2px;
}

/* ---- the control at the foot of the list -------------------------------- */
/* Out of flow on purpose. The four rows are what the centring above measures,
   so this hangs off the bottom of them without pulling the list off centre. */
.ledger__more { position: absolute; top: 100%; left: 0; right: 0; padding-top: var(--sc-4); }
.morebtn {
  display: inline-flex; align-items: center; gap: var(--sc-3);
  padding: .55rem .95rem .55rem .6rem; margin-left: -.6rem;
  background: none; border: 0; border-radius: var(--sc-r-md); cursor: pointer;
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  letter-spacing: .1em; text-transform: uppercase; color: var(--sc-ink-soft);
  transition: color var(--sc-d-base) var(--sc-ease-out), background var(--sc-d-base) var(--sc-ease-out);
}
/* the plus, drawn rather than set, so it can turn into nothing on hover */
.morebtn__plus { position: relative; width: 1.05rem; height: 1.05rem; flex: none; }
.morebtn__plus::before, .morebtn__plus::after {
  content: ""; position: absolute; inset: 50% 0 auto 0; height: 1px; margin-top: -.5px;
  background: currentColor; transition: transform var(--sc-d-base) var(--sc-ease-out);
}
.morebtn__plus::after { transform: rotate(90deg); }
.morebtn:hover, .morebtn:focus-visible { color: var(--accent-text); background: color-mix(in oklab, var(--warm) 7%, transparent); }
.morebtn:hover .morebtn__plus::after { transform: rotate(180deg); }
.morebtn:focus-visible { outline: 1px solid color-mix(in oklab, var(--accent-text) 60%, transparent); outline-offset: 2px; }
.morebtn:disabled,
.morebtn[aria-disabled="true"] {
  cursor: default;
  opacity: .55;
  pointer-events: none;
}
.morebtn:disabled:hover,
.morebtn[aria-disabled="true"]:hover { color: var(--sc-ink-soft); background: none; }
.morebtn:disabled:hover .morebtn__plus::after,
.morebtn[aria-disabled="true"]:hover .morebtn__plus::after { transform: rotate(90deg); }

/* ---- 4b the vault ------------------------------------------------------- */
/* A native <dialog>: Escape, the focus trap and the return of focus are the
   platform's, and the top layer ignores .corridor's overflow: clip. */
.vault {
  width: min(64rem, calc(100vw - 2 * var(--sc-gutter))); max-width: none;
  max-height: min(46rem, calc(100svh - 2 * var(--sc-6)));
  padding: 0; border: 1px solid var(--sc-hairline-strong); border-radius: var(--sc-r-lg);
  background: var(--sc-surface); color: var(--sc-ink); overflow: auto;
  box-shadow: 0 2.4rem 5rem -1rem rgba(0, 0, 0, .6);
}
.vault::backdrop { background: rgba(8, 6, 4, .72); backdrop-filter: blur(3px); }
/* The open is the same easing every other reveal on the page uses. Reduced
   motion gets the end state with no travel. */
.vault[open] { animation: vault-in 260ms var(--sc-ease-out); }
.vault[open]::backdrop { animation: vault-fade 260ms var(--sc-ease-out); }
@keyframes vault-in   { from { opacity: 0; transform: translateY(10px) scale(.99); } }
@keyframes vault-fade { from { opacity: 0; } }
.vault__inner { padding: clamp(1.5rem, 4vw, 2.75rem); }
.vault__head { display: flex; align-items: start; justify-content: space-between; gap: var(--sc-6); margin-bottom: var(--sc-7); }
.vault__head h2 { margin: 0; font-size: var(--sc-t-xl); letter-spacing: -0.02em; }
.vault__head .sc-body { margin: var(--sc-3) 0 0; color: var(--sc-ink-soft); max-width: 46ch; }
.vault__close {
  flex: none; cursor: pointer; background: none; color: var(--sc-ink-soft);
  border: 1px solid var(--sc-hairline-strong); border-radius: var(--sc-r-sm);
  padding: .45rem .85rem; font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  letter-spacing: .08em; text-transform: uppercase;
  transition: color var(--sc-d-base) var(--sc-ease-out), border-color var(--sc-d-base) var(--sc-ease-out);
}
.vault__close:hover, .vault__close:focus-visible { color: var(--sc-ink); border-color: var(--sc-ink-soft); }
.vault__grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sc-4); }
.slot {
  display: grid; gap: var(--sc-2); width: 100%; height: 100%; text-align: left; cursor: pointer;
  padding: var(--sc-5); border-radius: var(--sc-r-md);
  background: color-mix(in oklab, var(--sc-canvas) 60%, transparent);
  border: 1px solid var(--sc-hairline); color: inherit; font: inherit;
  transition: border-color var(--sc-d-base) var(--sc-ease-out), background var(--sc-d-base) var(--sc-ease-out);
}
.slot:hover, .slot:focus-visible { border-color: color-mix(in oklab, var(--accent-text) 45%, transparent); background: color-mix(in oklab, var(--warm) 5%, transparent); }
.slot:focus-visible { outline: 1px solid color-mix(in oklab, var(--accent-text) 60%, transparent); outline-offset: 2px; }
.slot__name { font-family: var(--sc-font-display); font-weight: 600; font-size: var(--sc-t-lg); letter-spacing: -0.02em; }
.slot__what { color: var(--sc-ink-soft); font-size: var(--sc-t-sm); line-height: 1.5; }
.slot__meta { font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); letter-spacing: .08em; text-transform: uppercase; color: var(--sc-ink-soft); opacity: .7; }
/* The dialog is modal, so the page behind must not scroll under it. The gutter
   is reserved on <html> at all times (see the top of this file), so hiding the
   overflow moves nothing. */
html.is-vault-open { overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
  .vault[open], .vault[open]::backdrop { animation: none; }
}

/* ================================================================ 5 INSIDE */
.inside__stage { background: var(--sc-canvas); }
/* The three ledes are placed independently in the frame rather than stacked in
   one corner: line 1 keeps the lead corner, line 2 sits above the person and
   left of centre, line 3 drops to their right and below the desk line. The
   clip is a push, so the lit monitor bank travels across the frame — each
   position was measured against the footage at the moment ITS OWN line is lit,
   not against a single frame. Line 3's spot reads 0% blown-out pixels across
   its whole window; the bank has moved off it by then.
   `.sc-copy` sets inset-inline, so the block is released to the whole stage
   here and the lines are placed against that. */
.inside__copy { inset: 0; width: auto; max-width: none; }
.inside__copy p {
  position: absolute; margin: 0; width: min(31rem, 42vw);
  left: var(--lede-x); top: var(--lede-y, auto); bottom: var(--lede-b, auto);
}
/* Each line carries its own wash and fades with it. The old single pool was on
   the container, so it sat on the footage for the whole act; three scattered
   pools left permanently on would read as smudges. */
.inside__copy p::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  left: -3.5rem; right: -3.5rem; top: -3.25rem; bottom: -3.25rem;
  background: radial-gradient(closest-side at 50% 50%,
    rgba(var(--stone),.92), rgba(var(--stone),.70) 56%, transparent 100%);
}
.inside__copy p:nth-of-type(1) { --lede-x: 12%; --lede-b: clamp(6rem, 25vh, 16rem); }
.inside__copy p:nth-of-type(2) { --lede-x: 30%; --lede-y: 20%; }
.inside__copy p:nth-of-type(3) { --lede-x: 62%; --lede-y: 65%; }
.inside__label { position: absolute; z-index: 20; left: var(--sc-gutter); top: clamp(4.5rem, 12vh, 8rem); margin: 0; color: #E6D8C4; }
.inside__label-plate { left: calc(var(--sc-gutter) - 7rem); top: calc(clamp(4.5rem, 12vh, 8rem) - 5rem); width: 22rem; height: 12rem; z-index: 19; opacity: 0; }
.inside__copy .sc-lede { font-size: clamp(1.9rem, 1.45rem + 1.7vw, 2.75rem); line-height: 1.22; letter-spacing: var(--sc-track-snug); max-width: 22ch; font-family: var(--sc-font-display); font-weight: 520; }

/* ================================================================== 6 DOOR */
.door { position: relative; min-height: 100svh; background: var(--sc-canvas); overflow: clip; display: grid; }
.door__bed { position: absolute; inset: 0; z-index: 1; }
.door__scrim { position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(to right, rgba(var(--stone),.94) 0%, rgba(var(--stone),.84) 30%, rgba(var(--stone),.46) 48%, transparent 62%),
              linear-gradient(to top, rgba(var(--stone),.88) 0%, rgba(var(--stone),.4) 18%, transparent 34%); }
.door__inner {
  /* Wider than the page's measure: the photograph is full bleed and the door
     sits right of centre, so the 82rem column would squeeze the copy into a
     ribbon to stay off the jamb. The footer travels with it and the two stay
     on one left edge. */
  position: relative; z-index: 3; max-width: min(98rem, 100%); width: 100%; margin-inline: auto;
  padding: clamp(5rem, 14vh, 9rem) var(--sc-gutter) clamp(1.4rem, 4vh, 2.5rem);
  display: grid; grid-template-rows: 1fr auto; min-height: 100svh;
}
.door__copy {
  align-self: end; max-width: min(30rem, var(--door-copy-max, 30rem));
  padding-bottom: clamp(2rem, 8vh, 5rem);
  /* The column is measured against the jamb, so it narrows on its own as the
     crop moves the door. The close has to be set from that column and not from
     the viewport, or the display line stacks up into a ribbon at the widths
     where the two are furthest apart. */
  container-type: inline-size;
}
.door__copy h2 { font-size: clamp(2.4rem, 15cqi, 4.6rem); }
.door__copy .sc-body { margin-top: var(--sc-4); color: #EFE3D2; }
.door__mail { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sc-3) var(--sc-4); margin-top: var(--sc-6); }
.door__mail a { font-family: var(--sc-font-mono); font-size: var(--sc-t-sm); letter-spacing: .02em; text-decoration-color: var(--accent-text); }
.copybtn {
  min-height: 2.25rem; padding: 0 .8rem; border-radius: var(--sc-r-sm);
  border: 1px solid var(--sc-hairline-strong); background: transparent; color: var(--sc-ink);
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); letter-spacing: .08em; text-transform: uppercase; cursor: pointer;
  transition: background var(--sc-d-fast) var(--sc-ease-out), border-color var(--sc-d-fast) var(--sc-ease-out);
}
.copybtn:hover { background: color-mix(in oklab, var(--sc-ink) 8%, transparent); border-color: var(--sc-ink-soft); }
.copybtn:active { transform: translateY(1px); }
.copybtn[data-done="true"] { color: var(--accent-text); border-color: var(--accent-text); }
.door__cta {
  position: absolute; z-index: 4; display: flex; align-items: flex-end; justify-content: center;
  text-decoration: none; color: var(--sc-ink); padding-bottom: 12%;
  outline-offset: 6px; border-radius: var(--sc-r-sm);
}
/* The words sit low, over the floor inside the doorway, and grow into the
   accent as the lamp comes up under them. Nothing is drawn around the frame:
   the swell is the affordance. */
.door__cta span {
  position: relative; text-align: center; text-wrap: balance; max-width: 13ch;
  font-family: var(--sc-font-display); font-weight: 600; letter-spacing: -0.015em; line-height: 1.14;
  font-size: clamp(1.1rem, 1.5vw, 1.6rem);
  color: color-mix(in oklab, var(--accent-text) calc(var(--door-hover, 0) * 100%), var(--sc-ink));
  transform: scale(calc(1 + var(--door-hover, 0) * 0.12));
  text-shadow: 0 1px 3px rgba(18,10,3,.6),
               0 0 calc(14px + var(--door-hover, 0) * 30px) rgba(255,214,150, calc(.3 + var(--door-hover, 0) * .55));
  will-change: transform;
}
/* A soft plate so the words hold on the lit floor, the same radial the rest of
   the page parks its copy on. Grows with the text. */
.door__cta span::before {
  content: ""; position: absolute; z-index: -1; left: 50%; top: 50%; translate: -50% -50%;
  width: 260%; height: 340%; pointer-events: none;
  background: radial-gradient(closest-side, rgba(22,13,5,.66), rgba(22,13,5,.36) 54%, transparent 100%);
  opacity: calc(.55 + var(--door-hover, 0) * .3);
}
.door__foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sc-3) var(--sc-6);
  padding-top: var(--sc-4); border-top: 1px solid var(--sc-hairline);
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); letter-spacing: .06em; text-transform: uppercase; color: var(--sc-ink-soft);
}
.door__foot a { text-decoration: none; }
.door__foot a:hover { color: var(--sc-ink); }
.door__foot .door__foot-mark { color: var(--sc-ink); margin-right: auto; }

.door__foot-store { position: relative; display: inline-flex; align-items: center; }
.door__storebtn {
  background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit;
}
.door__storebtn:hover { color: var(--sc-ink); }
.door__storebtn:focus-visible { outline: 1px solid color-mix(in oklab, var(--accent-text) 60%, transparent); outline-offset: 3px; }
.store-soon {
  position: absolute; left: 50%; bottom: calc(100% + .55rem); translate: -50% 0;
  z-index: 8; padding: .45rem .7rem; border-radius: var(--sc-r-sm);
  border: 1px solid var(--sc-hairline-strong);
  background: color-mix(in oklab, rgba(var(--stone), 1) 92%, transparent);
  color: var(--accent-text); white-space: nowrap;
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  letter-spacing: .08em; text-transform: uppercase;
  box-shadow: 0 10px 28px rgba(0,0,0,.35);
  pointer-events: none;
}
.store-soon[hidden] { display: none; }

/* ============================================================== keyboard */
:focus-visible { outline-color: var(--accent-text); }

/* ========================================================= reduced motion */
@media (prefers-reduced-motion: reduce) {
  .roomcopy__more { transition: --m 120ms linear, opacity 120ms linear; }
  .roomcopy__toggle::before { transition: none; }
  .room { transition: --lit 260ms var(--sc-ease-out); }
  .door { transition: --lit 260ms var(--sc-ease-out), --door-hover 260ms var(--sc-ease-out); }
  .door__cta span { transform: none; }
  .lamp-btn::after { animation: none; opacity: .5; }
  .hall__plane { will-change: auto; }
}

/* ================================================================ phones */
@media (max-width: 860px) {
  .panel {
    top: auto; right: 0; bottom: 0; left: 0; border-radius: 0; border-width: 1px 0 0 0;
    padding: .35rem .5rem calc(.35rem + env(safe-area-inset-bottom)) .9rem; gap: .35rem;
    justify-content: space-between;
  }
  .panel__mark { padding-right: .6rem; font-size: .9rem; }
  .panel__switches { flex: 1; justify-content: space-between; }
  .switch { min-width: 2.75rem; }
  .switch__label { font-size: .5rem; letter-spacing: .08em; }

  .hall__copy { bottom: clamp(7rem, 17vh, 9.5rem); width: calc(100vw - 2 * var(--sc-gutter)); }
  .hall__copy h1 { font-size: var(--sc-t-2xl); max-width: 12ch; }
  .hall__copy .sc-lede { font-size: var(--sc-t-base); max-width: 30ch; }
  /* Same rebalance on the portrait frame, measured the same way: the low band
     goes 21.7 -> 41.9 against a 60.4 ceiling, worst tile behind the lede 7.8:1. */
  .hall__scrim { background: radial-gradient(68% 30% at 50% 82%, rgba(var(--stone),.74), rgba(var(--stone),.62) 46%, rgba(var(--stone),.3) 70%, transparent 88%); }
  .hall__hint { left: 50%; top: auto; bottom: 4.6rem; translate: -50% 0; font-size: .62rem; }
  .hall__hint-plate { left: 50%; translate: -50% 0; bottom: -2rem; width: 26rem; height: 12rem; }
  .hall__plane--mid { transform-origin: 50% 50%; }

  .plate__inner {
    padding-top: clamp(2rem, 6vh, 3rem);
    padding-bottom: calc(5.5rem + env(safe-area-inset-bottom));
  }

  .rooms__name { bottom: auto; top: .9rem; left: var(--sc-gutter); font-size: var(--sc-t-base); }
  .roomcopy { bottom: auto; left: var(--sc-gutter); right: var(--sc-gutter); width: auto; translate: 0 -50%; }
  .roomcopy .sc-body { max-width: 40ch; }
  .rooms__plate { left: -15vw; right: auto; width: 130vw; height: 132vw; translate: 0 -50%; bottom: auto;
    background: radial-gradient(closest-side at 50% 50%, rgba(var(--stone),.97) 0%, rgba(var(--stone),.95) 58%, rgba(var(--stone),.72) 80%, transparent 100%); }
  .rooms__intro { top: clamp(2rem, 7vh, 4rem); }
  .rooms__intro-plate { left: -12rem; top: -6rem; width: 44rem; height: 26rem;
    background: radial-gradient(closest-side, rgba(var(--stone),.97) 0%, rgba(var(--stone),.95) 58%, rgba(var(--stone),.7) 80%, transparent 100%); }

  .corridor__bed { -webkit-mask-image: linear-gradient(to bottom, #000 30%, transparent 62%); mask-image: linear-gradient(to bottom, #000 30%, transparent 62%); }
  .corridor__wrap { grid-template-columns: 1fr; gap: var(--sc-6); padding-top: 42svh; padding-bottom: 7rem; }
  /* Desktop lead uses a large `top` to mirror into the lower half beside the
     ledger. On one column that offset drops the H2 onto the first product —
     zero it so the lead occupies the empty band above the list. */
  .corridor__lead {
    align-self: start;
    top: 0;
    left: 0;
    margin-bottom: var(--sc-2);
  }
  .ledger { --ledger-shift: 0px; align-self: start; top: 0; }
  /* The bed is masked down the page here, not across it, and the wrap's 42svh
     of top padding drops the list clear of it — so the list has no lit
     photograph to protect itself from and the wash is only in the way. */
  .ledger::before { display: none; }
  /* The list is not centred on this breakpoint, so the control has no reason
     to stay out of the flow. */
  .ledger__more { position: static; padding-top: var(--sc-5); }
  .ledger__row { grid-template-columns: 1fr; }
  .ledger__meta { grid-column: 1; }
  .vault__head { flex-direction: column; gap: var(--sc-4); }
  .vault__grid { grid-template-columns: 1fr; }

  .inside__copy p {
    left: var(--sc-gutter); right: var(--sc-gutter); top: auto; width: auto;
    bottom: clamp(5rem, 20vh, 12rem);
  }
  .inside__copy p::before { left: -2rem; right: -2rem; top: -2.5rem; bottom: -3rem; }
  /* On a phone the lead corner is the whole width, and the desk in the portrait
     clip is dark: wash the band, not the corner. */
  .inside .sc-scrim--lead { background: linear-gradient(to top, rgba(var(--stone),.97) 0%, rgba(var(--stone),.9) 26%, rgba(var(--stone),.5) 44%, transparent 62%); }
  .inside__copy .sc-lede { font-size: var(--sc-t-lg); max-width: 24ch; }

}

/* ====================================================== the door, stacked

   The door stacks earlier than the rest of the page. Its photograph is full
   bleed and the doorway sits right of centre, so below about 1180px there is
   no column left of the jamb wide enough to set the display line in: the copy
   goes above the door instead of beside it. Copy, then the door with room to
   itself, then the small print. The section grows past the viewport when the
   screen is short; the door rect is mapped from the section box, so the link
   stays on the door. */
@media (max-width: 1180px) {
  .door__inner { padding-top: clamp(4rem, 9vh, 6rem); padding-bottom: calc(5.5rem + env(safe-area-inset-bottom)); grid-template-rows: auto minmax(52svh, 1fr) auto; }
  .door__copy { max-width: none; padding-bottom: 0; align-self: start; }
  .door__cta span { font-size: clamp(1.15rem, 3.4vw, 1.6rem); max-width: 11ch; translate: -1.25rem 0; }
  .door__foot { grid-row: 3; align-self: end; row-gap: .7rem; column-gap: 1.4rem; }
  .door__foot .door__foot-mark { flex-basis: 100%; margin-right: 0; }
  .door__scrim { background: linear-gradient(to bottom, rgba(var(--stone),.95) 0%, rgba(var(--stone),.88) 34%, rgba(var(--stone),.58) 48%, rgba(var(--stone),.1) 60%, transparent 68%),
                             linear-gradient(to top, rgba(var(--stone),.97) 0%, rgba(var(--stone),.9) 16%, rgba(var(--stone),.6) 24%, transparent 34%); }
  .door__cta {
    align-items: center;
    justify-content: center;
    padding-bottom: 0;
    /* Bias the label into the upper-middle of the door, not the floor. */
    padding-top: 0;
    transform: translateY(-12%);
  }
}
@media (max-width: 400px) {
  .switch__label { display: none; }
  .switch { min-width: 2.75rem; padding-block: .45rem; }
}
