/* The clickable house.
 *
 * Layout rule that explains the file: .stage is sized to the exact CONTAIN
 * rectangle of the photograph — min(viewport, aspect-corrected viewport) on
 * each axis — so the whole house is always visible, nothing is ever cropped,
 * and a hotspot at 48% of the photo really is at 48% of .stage. The letterbox
 * left over is filled by .backdrop, a blurred blow-up of the same picture.
 *
 * There are no buttons and no instructions here by design. A hotspot is found
 * by the picture glowing under the cursor; clicking is the only verb.
 */

:root {
  --ink: #10180f;
  --paper: #f7f4ec;
  --brass: #d8a24a;
  --glow: 255, 240, 205;
  --ease: cubic-bezier(.45, .05, .2, 1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: #0b100c;
  color: var(--paper);
  font: 16px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  overscroll-behavior: none;
}

.scene-body { overflow: hidden; }

.skip {
  position: fixed; left: -999px; top: 0; z-index: 60;
  background: var(--paper); color: var(--ink); padding: .6rem 1rem;
}
.skip:focus { left: .5rem; top: .5rem; }

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

.viewport {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: #0b100c;
}

.backdrop {
  position: absolute;
  inset: -6%;                     /* over-sized so the blur has no hard edge */
  background-size: cover;
  background-position: center;
  filter: blur(38px) saturate(.75) brightness(.42);
  transform: scale(1.06);
}

.stage {
  position: absolute;
  top: 50%;
  left: 50%;
  /* CONTAIN, not cover: the entire photograph, always. */
  width: min(100vw, calc(100svh * var(--iw, 4) / var(--ih, 3)));
  height: min(100svh, calc(100vw * var(--ih, 3) / var(--iw, 4)));
  transform: translate(-50%, -50%);
  box-shadow: 0 30px 90px rgba(0, 0, 0, .55);
  /* Above the edge strips, so a hotspot that reaches the side of the picture
     — the kitchen does — is never swallowed by "step to the next room". The
     stage itself is click-through, so only the hotspot shapes (and the volume
     control) are targets and a click on plain floor or sky still falls to the
     edge underneath. */
  z-index: 2;
  pointer-events: none;
}

.plate {
  display: block;
  width: 100%;
  height: 100%;
  /* see .stage — the photograph itself must not catch clicks */
  pointer-events: none;
  /* .stage already carries the photo's aspect ratio, so "fill" cannot distort. */
  object-fit: fill;
  user-select: none;
  -webkit-user-drag: none;
}

.vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom,
      rgba(6, 12, 8, .55) 0, transparent 18%,
      transparent 82%, rgba(6, 12, 8, .45) 100%);
}

/* ------------------------------------------------------------- hotspots -- */

.hot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;          /* only the shapes below are targets */
}

/* A transparent fill still takes pointer events (fill:none would not), which
   is what makes the door itself clickable rather than a label beside it. */
.hs {
  pointer-events: auto;
  cursor: pointer;
  /* THE MYSTERY FRAME, SOLVED: clicking an SVG <a> focuses it, and Chromium
     paints its default focus ring — a heavy white outer / dark inner double
     ring around the shape's box. That was the frame around the piano, the
     front door and the porch. Suppressing it costs nothing here: these anchors
     are tabindex="-1" and aria-hidden, so no keyboard user ever lands on one.
     The .exits nav is the real keyboard path and keeps its own focus style. */
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.hs:focus, .hs:focus-visible { outline: none; }

/* THE RULE FOR EVERY HOTSPOT: nothing is announced. At rest a hotspot is a
   breath of warmth on the photograph and nothing more; under the cursor it
   draws one thin line around itself. No labels, no glow, no frame — the point
   is that a visitor finds these by exploring, and the cursor turning into a
   pointer is most of the reward.

   NO css `filter` and NO `backdrop-filter` anywhere inside .stage: both make
   Chromium re-composite this subtree and paint a heavy white ring and black
   band around whichever shape is lit. That was the frame around the piano. */
.hs > * {
  fill: rgba(var(--glow), .03);
  stroke: rgba(var(--glow), 0);
  /* Set from JS so one stroke unit is one device pixel whatever the window
     size — the viewBox is 100 wide, so the value depends on the stage width. */
  stroke-width: var(--sw, .12);
  stroke-linejoin: round;
  transition: fill .35s ease, stroke .35s ease;
}

/* Just enough movement that a wandering eye may catch it. */
.hs > *:not(.tap) { animation: breathe 5.5s ease-in-out infinite; }
.tour-touched .hs > * { animation: none; }

@keyframes breathe {
  0%, 100% { fill: rgba(var(--glow), .015); }
  50%      { fill: rgba(var(--glow), .06); }
}

.hs.on > * {
  fill: rgba(var(--glow), .07) !important;
  stroke: rgba(var(--glow), .45) !important;
  animation: none;
}

/* The oversized touch target: invisible, and only a target on touch screens,
   where the front door would otherwise be a 25px-wide thing to hit. */
.tap { fill: transparent; stroke: none; pointer-events: none; }
@media (pointer: coarse) {
  .hs .tap { pointer-events: auto; }
}

/* ------------------------------------------------------- keeping a fire -- */

/* The photograph's fire is a still. These layers sit ON it in screen blend and
   breathe at four periods that never come back into step (2.3 / 1.63 / 1.09 /
   4.7s), which is what stops it reading as a loop. Amplitudes were raised once
   after measuring: the first pass came out at 40-44 dB PSNR between frames and
   read as "possibly moving"; these land nearer 37-39 dB — plainly alive, still
   short of a screensaver.

   Sizes are % of the FIREBOX, positioned from scenes.py — no fixed pixels, so
   it stays registered on the fire at every window size. */

.fire {
  position: absolute;
  pointer-events: none;
  z-index: 1;
}
.fire > span {
  position: absolute;
  display: block;
  will-change: opacity, transform;
}

/* The light the fire throws back into the room and onto the stone. */
.fire-glow {
  inset: -160% -190% -110%;
  background: radial-gradient(50% 50% at 50% 62%,
      rgba(255, 176, 74, .30) 0%,
      rgba(255, 142, 46, .13) 38%,
      transparent 72%);
  mix-blend-mode: screen;
  animation: fire-glow 4.7s ease-in-out infinite;
}

/* The bed of the fire: broad, low, and the steadiest of the layers. */
.fire-body {
  left: 4%; right: 4%; top: 26%; bottom: 2%;
  background: radial-gradient(52% 62% at 50% 76%,
      rgba(255, 226, 150, .52) 0%,
      rgba(255, 160, 48, .30) 44%,
      rgba(216, 92, 16, .10) 70%,
      transparent 84%);
  filter: blur(1px);
  mix-blend-mode: screen;
  transform-origin: 50% 100%;
  animation: fire-body 2.3s ease-in-out infinite;
}

/* A tongue lifting off the logs, and the pale tip above it. */
.fire-tongue {
  left: 18%; right: 22%; top: 12%; bottom: 22%;
  background: radial-gradient(46% 56% at 50% 70%,
      rgba(255, 240, 196, .50) 0%,
      rgba(255, 186, 74, .26) 48%,
      transparent 78%);
  filter: blur(1.4px);
  mix-blend-mode: screen;
  transform-origin: 50% 100%;
  animation: fire-tongue 1.63s ease-in-out infinite;
}
.fire-tip {
  left: 32%; right: 34%; top: 4%; bottom: 44%;
  background: radial-gradient(50% 50% at 50% 76%,
      rgba(255, 248, 224, .40) 0%,
      rgba(255, 208, 120, .16) 52%,
      transparent 80%);
  filter: blur(2px);
  mix-blend-mode: screen;
  transform-origin: 50% 100%;
  animation: fire-tip 1.09s ease-in-out infinite;
}

@keyframes fire-glow {
  0%, 100% { opacity: .66; }
  38%      { opacity: 1; }
  61%      { opacity: .54; }
}
@keyframes fire-body {
  0%, 100% { opacity: .76; transform: scale(1, 1); }
  30%      { opacity: 1;   transform: scale(1.035, 1.10); }
  55%      { opacity: .68; transform: scale(.98, .93); }
  78%      { opacity: .94; transform: scale(1.02, 1.06); }
}
@keyframes fire-tongue {
  0%, 100% { opacity: .46; transform: translateX(0) scaleY(1); }
  27%      { opacity: 1;   transform: translateX(-4.5%) scaleY(1.24); }
  52%      { opacity: .34; transform: translateX(2.5%) scaleY(.84); }
  74%      { opacity: .86; transform: translateX(4.5%) scaleY(1.12); }
}
@keyframes fire-tip {
  0%, 100% { opacity: .2;  transform: translateX(0) scaleY(.88); }
  22%      { opacity: .88; transform: translateX(5%) scaleY(1.34); }
  46%      { opacity: .12; transform: translateX(-3.5%) scaleY(.78); }
  70%      { opacity: .68; transform: translateX(-5.5%) scaleY(1.18); }
}

/* --------------------------------------------------------- candlelight -- */

/* A halo per flame, deliberately wider than the flame itself — a candle shows
   in a photograph mostly by the light it throws, not by the wick moving. Each
   carries its own period and start offset from scenes.py so they never beat
   together. Sizes are % of the PICTURE, so they hold at any window size. */
.candles { position: absolute; inset: 0; pointer-events: none; }

.candle {
  position: absolute;
  display: block;
  width: calc(2.4% * var(--cs, 1));
  height: calc(3.4% * var(--cs, 1));
  transform: translate(-50%, -50%);
  background: radial-gradient(50% 50% at 50% 50%,
      rgba(255, 226, 158, .58) 0%,
      rgba(255, 190, 96, .26) 42%,
      transparent 74%);
  filter: blur(1.2px);
  mix-blend-mode: screen;
  will-change: opacity, transform;
  animation: candle var(--cp, 2s) ease-in-out var(--cd, 0ms) infinite;
}

/* Small moves only: a candle in a still room breathes, it does not dance. */
@keyframes candle {
  0%, 100% { opacity: .55; transform: translate(-50%, -50%) scale(1, 1); }
  23%      { opacity: .95; transform: translate(-50%, -51%) scale(1.06, 1.12); }
  47%      { opacity: .48; transform: translate(-50%, -49.5%) scale(.96, .94); }
  71%      { opacity: .82; transform: translate(-50%, -50.5%) scale(1.03, 1.07); }
}

/* ------------------------------------------------------- a piano to play -- */

/* While the recording runs the piano keeps the same thin line it shows under
   the cursor, breathing very slightly. It is a hint that this is the thing
   making the sound — not a highlight, and never a box. */
.hs.playing > *:not(.tap) {
  animation: playing 3.4s ease-in-out infinite;
}
@keyframes playing {
  0%, 100% { fill: rgba(var(--glow), .03); stroke: rgba(var(--glow), .20); }
  50%      { fill: rgba(var(--glow), .08); stroke: rgba(var(--glow), .42); }
}

/* ------------------------------------------------------ a volume control -- */

/* Appears only while something in the room is playing, sits under whatever is
   making the sound, and is the one piece of chrome allowed on the picture —
   sound is the one thing a visitor cannot judge from the image. */
.volume {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .34rem .7rem .34rem .6rem;
  border-radius: 999px;
  background: rgba(11, 16, 12, .66);
  border: 1px solid rgba(var(--glow), .16);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .4);
  transform: translate(-50%, -50%);
  pointer-events: auto;
  opacity: 0;
  transition: opacity .25s ease;
}
.volume.show { opacity: 1; }
.volume[hidden] { display: none; }

.vol-mark { width: .95rem; height: .95rem; flex: none; color: rgba(var(--glow), .7); }

/* Styled by hand rather than left to `accent-color`, which still leaves a
   blue track on some builds — and blue is the one colour nothing in this
   house is. */
.vol-range {
  -webkit-appearance: none;
  appearance: none;
  width: 5.6rem;
  height: 1rem;
  margin: 0;
  background: transparent;
  cursor: pointer;
}
.vol-range::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 2px;
  background: rgba(var(--glow), .28);
}
.vol-range::-moz-range-track {
  height: 3px;
  border-radius: 2px;
  background: rgba(var(--glow), .28);
}
.vol-range::-moz-range-progress {
  height: 3px;
  border-radius: 2px;
  background: var(--brass);
}
.vol-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 11px;
  height: 11px;
  margin-top: -4px;
  border: none;
  border-radius: 50%;
  background: var(--brass);
}
.vol-range::-moz-range-thumb {
  width: 11px;
  height: 11px;
  border: none;
  border-radius: 50%;
  background: var(--brass);
}
.vol-range:focus { outline: none; }
.vol-range:focus-visible { outline: 2px solid var(--brass); outline-offset: 4px; }

@media (max-width: 640px) {
  .vol-range { width: 4.4rem; }
  .volume { padding: .3rem .6rem .3rem .5rem; }
}

/* ---------------------------------------------------- stepping sideways -- */

/* Deliberately BELOW the hotspots in stacking order: a click that lands on
   something in the picture always means that thing, and the edges only pick
   up clicks on sky, lawn and letterbox. */
.edge {
  position: absolute;
  top: 0;
  bottom: 0;
  width: min(9vw, 104px);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  opacity: .34;
  transition: opacity .28s ease, background .28s ease;
}
.edge-prev { left: 0; }
.edge-next { right: 0; }
.edge:hover, .edge:focus-visible {
  opacity: 1;
  outline: none;
  background: linear-gradient(to right, rgba(6, 12, 8, .42), transparent);
}
.edge-next:hover, .edge-next:focus-visible {
  background: linear-gradient(to left, rgba(6, 12, 8, .42), transparent);
}

.chev {
  width: 20px;
  height: 20px;
  border-right: 2px solid var(--paper);
  border-bottom: 2px solid var(--paper);
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .9));
}
.edge-prev .chev { transform: rotate(135deg); margin-left: 6px; }
.edge-next .chev { transform: rotate(-45deg); margin-right: 6px; }

/* --------------------------------------------------- the door swings in -- */

/* A cut-out of the door's own pixels, hinged on its left edge, with the hall
   behind it. Sized and positioned by JS from the hotspot rectangle. */
.doorway {
  position: absolute;
  perspective: 900px;
  perspective-origin: 50% 50%;
  pointer-events: none;
}
.doorway-reveal,
.doorway-dark,
.doorway-leaf {
  position: absolute;
  inset: 0;
  display: block;
}

/* The room the door actually leads to, seen through the opening. Dimmed and
   cooled, because you are looking into a room from bright daylight. */
.doorway-reveal {
  background-size: cover;
  background-position: 50% 42%;
  filter: brightness(.62) saturate(.9) contrast(1.04);
}
/* Shade over the revealed room: warm, and darkest at the edges where the
   door frame would be. Semi-transparent so the room still reads through it —
   it also covers the case of a hotspot with no reveal image at all. */
.doorway-dark {
  background:
    radial-gradient(85% 45% at 50% 98%, rgba(255, 214, 152, .22) 0%,
                    rgba(255, 200, 130, .06) 55%, transparent 82%),
    linear-gradient(180deg, rgba(27, 20, 9, .6) 0%, rgba(46, 33, 18, .38) 48%,
                    rgba(29, 20, 9, .52) 100%);
  box-shadow: inset 0 0 34px 10px rgba(0, 0, 0, .72);
}
.doorway-leaf {
  background-repeat: no-repeat;
  transform-origin: left center;
  transform: rotateY(0deg);
  backface-visibility: hidden;
}
.opening .doorway-leaf {
  transform: rotateY(-76deg);
  box-shadow: 24px 0 46px rgba(0, 0, 0, .6);
  transition: transform 1150ms cubic-bezier(.35, 0, .25, 1),
              box-shadow 1150ms ease;
}

/* ---------------------------------------------- arriving and departing --- */

.backdrop { animation: arrive 780ms var(--ease) both; }
@keyframes arrive {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.stage { animation: arrive-stage 780ms var(--ease) both; }
@keyframes arrive-stage {
  from { opacity: 0; transform: translate(-50%, -50%) scale(1.035); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* The stage's own departure (transform + opacity) is driven from JS — see
   depart() for why a class cannot do it. This only fades what surrounds it. */
.leaving .backdrop, .leaving .hud, .leaving .edge {
  animation: none;
  opacity: 0;
  transition: opacity 340ms ease;
}

/* The hotspot outlines are an invitation to click, and the click has already
   happened. They also scale with the stage, so a 2px outline becomes a 10px
   white frame around the opening door if it is left on screen. */
.opening .hot, .leaving .hot {
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease;
}

/* ------------------------------------------------------ the standing menu -- */

/* Same lockup as the "Swannanoa, NC" brand: same family, same weight, same
   letter-spacing and the same soft shadow, so it reads as one piece of
   furniture with the header rather than a widget bolted on.

   It sits ABOVE the stage (which is z-index 2) and the left edge strip is
   pushed clear of it, so the menu never fights "step back a room". */
.sidemenu {
  position: fixed;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 25;
  padding: 1rem clamp(1rem, 2.4vw, 1.6rem);
  padding-left: max(clamp(1rem, 2.4vw, 1.6rem), env(safe-area-inset-left));
  pointer-events: none;
}
.sidemenu ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: .34rem;
}
.sidemenu a {
  pointer-events: auto;
  display: inline-block;
  padding: .17rem 0;
  color: var(--paper);
  text-decoration: none;
  font-weight: 650;
  font-size: clamp(.78rem, 1.05vw, .88rem);
  letter-spacing: .02em;
  text-shadow: 0 2px 14px rgba(0, 0, 0, .95), 0 0 26px rgba(0, 0, 0, .7);
  opacity: .82;
  border-left: 2px solid transparent;
  padding-left: .55rem;
  transition: opacity .2s ease, border-color .2s ease, transform .2s ease;
}
.sidemenu a:hover,
.sidemenu a:focus-visible {
  opacity: 1;
  border-left-color: var(--brass);
  transform: translateX(2px);
  outline: none;
}
.sidemenu a[aria-current="page"] {
  opacity: 1;
  border-left-color: var(--brass);
  color: var(--brass);
}

/* Open/closed is a checkbox, so the menu works with no JavaScript at all. */
.menu-toggle { position: absolute; left: -9999px; }
.menu-button, .menu-scrim { display: none; }

/* Keep the "step back" strip out from under the menu. */
.edge-prev { left: clamp(7rem, 13vw, 10rem); }

@media (max-width: 900px) {
  /* No room for a standing menu beside the picture: it becomes a button in
     the corner and slides over. */
  .sidemenu {
    top: 0;
    bottom: 0;
    transform: none;
    display: flex;
    align-items: center;
    width: min(72vw, 17rem);
    padding-left: max(1.6rem, env(safe-area-inset-left));
    background: rgba(8, 12, 9, .93);
    border-right: 1px solid rgba(var(--glow), .14);
    translate: -102% 0;
    transition: translate .28s var(--ease);
    pointer-events: auto;
  }
  .sidemenu a { font-size: 1rem; padding: .3rem 0 .3rem .6rem; }
  .menu-toggle:checked ~ .sidemenu { translate: 0 0; }

  .menu-button {
    position: fixed;
    left: max(.9rem, env(safe-area-inset-left));
    bottom: max(.9rem, env(safe-area-inset-bottom));
    z-index: 26;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: 999px;
    background: rgba(11, 16, 12, .72);
    border: 1px solid rgba(var(--glow), .28);
    box-shadow: 0 6px 20px rgba(0, 0, 0, .5);
    cursor: pointer;
  }
  .menu-bars, .menu-bars::before, .menu-bars::after {
    content: "";
    display: block;
    width: 1.05rem;
    height: 2px;
    border-radius: 2px;
    background: var(--paper);
  }
  .menu-bars { position: relative; }
  .menu-bars::before { position: absolute; top: -6px; }
  .menu-bars::after { position: absolute; top: 6px; }

  .menu-toggle:checked ~ .menu-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 24;
    background: rgba(4, 7, 5, .5);
  }

  /* the strip goes back to the edge once the menu is a button */
  .edge-prev { left: 0; }
}

/* --------------------------------------------------------------- the HUD -- */

.hud {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 20;
  padding: 1rem clamp(1rem, 4vw, 2.25rem);
  padding-left: max(clamp(1rem, 4vw, 2.25rem), env(safe-area-inset-left));
  padding-right: max(clamp(1rem, 4vw, 2.25rem), env(safe-area-inset-right));
  pointer-events: none;
}
.hud a { pointer-events: auto; }

.hud-top {
  top: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: max(1rem, env(safe-area-inset-top));
}

/* The bar sits over open sky; text-shadow alone is not enough on a bright
   morning, so it carries its own scrim. */
.hud-top::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(6, 12, 8, .68), rgba(6, 12, 8, 0));
}
.hud-top > * { position: relative; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  color: var(--paper);
  text-decoration: none;
  font-weight: 650;
  letter-spacing: .02em;
  text-shadow: 0 2px 14px rgba(0, 0, 0, .8);
}
.brand-mark { width: 1.3rem; height: 1.3rem; flex: none; }

/* The house is on Christian Creek, so the name has a creek running under it.
   The paths are drawn wider than the box and slide by exactly one wavelength
   (32 user units), which makes the loop seamless — you cannot see it restart.
   Two waves at different speeds so it reads as water rather than a wiggle. */
.brand-stack { display: grid; gap: .1rem; }
.brand-creek {
  width: 100%;
  height: .42rem;
  display: block;
  opacity: .95;
}
.creek {
  fill: none;
  stroke-linecap: round;
  animation: creek-flow 3.4s linear infinite;
}
.creek-a { stroke: #34b3f5; stroke-width: 1.9; }
.creek-b { stroke: #8bd9ff; stroke-width: 1.1; opacity: .75; animation-duration: 5.1s; }

@keyframes creek-flow {
  from { transform: translateX(0); }
  to   { transform: translateX(32px); }
}

@media (prefers-reduced-motion: reduce) { .creek { animation: none; } }

.hud-nav { display: flex; align-items: center; gap: clamp(.6rem, 2.4vw, 1.4rem); }
.hud-nav a {
  color: var(--paper);
  text-decoration: none;
  font-size: .92rem;
  text-shadow: 0 2px 14px rgba(0, 0, 0, .8);
  opacity: .92;
}
.hud-nav a:hover { opacity: 1; text-decoration: underline; }

.hud-cta {
  background: var(--brass);
  color: #22190a !important;
  padding: .5rem .95rem;
  border-radius: 999px;
  font-weight: 650;
  text-shadow: none !important;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .4);
}
.hud-cta:hover { text-decoration: none !important; filter: brightness(1.07); }

.hud-bottom {
  bottom: 0;
  padding-bottom: max(1.1rem, env(safe-area-inset-bottom));
  text-align: center;
}

@media (max-width: 640px) {
  /* The header must never overflow: on a 390px phone "Book a showing" plus the
     town name plus two links does not fit, and the pill gets cut off. */
  .hud-top { gap: .5rem; }
  .brand { font-size: .85rem; }
  .brand-text { white-space: nowrap; }
  .hud-nav { gap: .7rem; }
  .hud-nav a { font-size: .82rem; }
  .hud-cta { padding: .42rem .8rem; }
  .edge { width: 13vw; }
}

@media (max-width: 480px) { .cta-tail { display: none; } }

/* Last resort before anything gets clipped: the town name is the first to go,
   since the photo is already saying where you are. */
@media (max-width: 360px) { .brand-text { display: none; } }

/* ------------------------------------------------- the drawn interior --- */

/* You are standing in the dim entry looking back at the open front door, with
   the afternoon coming in behind you. Drawn, not photographed — see scenes.py
   for why there is no interior picture here. */

.room {
  width: 100vw;
  height: 100svh;
  box-shadow: none;
  background:
    radial-gradient(62% 48% at 50% 38%, #2a3524 0%, #17201705 40%, transparent 70%),
    radial-gradient(120% 100% at 50% 108%, #1c2419 0%, #0c110d 60%, #080b09 100%);
}

.room-scene { position: absolute; inset: 0; }

.room-door {
  position: absolute;
  left: 50%;
  top: 12%;
  width: min(24vw, 250px);
  height: min(46vh, 430px);
  transform: translateX(-50%);
  border-radius: 6px 6px 2px 2px;
  background: linear-gradient(180deg,
      rgba(255, 243, 214, .82) 0%,
      rgba(255, 226, 172, .55) 45%,
      rgba(255, 206, 140, .28) 100%);
  box-shadow:
    0 0 120px 44px rgba(255, 216, 150, .22),
    0 0 260px 90px rgba(255, 200, 120, .10);
  animation: lamp 9s ease-in-out infinite;
}

/* The light the doorway throws across the floorboards — without it the
   doorway floats and the room reads as an empty box. */
.room-pool {
  position: absolute;
  left: 50%;
  top: calc(12% + min(46vh, 430px));
  width: min(72vw, 720px);
  height: min(30vh, 260px);
  transform: translateX(-50%);
  background: radial-gradient(50% 100% at 50% 0%,
      rgba(255, 226, 170, .30) 0%,
      rgba(255, 210, 145, .12) 42%,
      transparent 78%);
  filter: blur(6px);
}

@keyframes lamp {
  0%, 100% { opacity: .88; }
  50%      { opacity: 1; }
}

.inside-hud { max-width: 46rem; margin: 0 auto; }
.inside-h1 {
  margin: 0 0 .4rem;
  font-size: clamp(1.5rem, 5vw, 2.3rem);
  letter-spacing: -.01em;
  text-shadow: 0 2px 20px rgba(0, 0, 0, .8);
}
.inside-p {
  margin: 0 auto;
  max-width: 34rem;
  font-size: .96rem;
  opacity: .84;
  text-shadow: 0 2px 16px rgba(0, 0, 0, .8);
}

/* ------------------------------------------------------------ plain pages -- */

/* The menu's own pages. Same palette and lockup as the tour, but a normal
   scrolling document — these are for reading, not exploring. */
.page-body {
  overflow: auto;
  /* html and body are height:100% for the tour, which pins the body BOX to one
     viewport — a long page then overflows it and its background stops dead at
     the fold, leaving the dark canvas showing underneath. Reading pages need
     the box to grow with the content. */
  height: auto;
  background:
    radial-gradient(120% 80% at 50% -10%, #16211a 0%, #0b100c 60%, #080b09 100%);
  min-height: 100%;
}

.sheet {
  max-width: 44rem;
  margin: 0 auto;
  padding: clamp(6rem, 14vh, 9rem) 1.5rem 2rem;
  padding-left: max(1.5rem, clamp(7rem, 13vw, 10rem));
  padding-right: max(1.5rem, env(safe-area-inset-right));
}
.sheet h1 {
  margin: 0 0 .6rem;
  font-size: clamp(1.6rem, 4.5vw, 2.4rem);
  letter-spacing: -.01em;
}
.sheet-note { opacity: .7; margin: 0 0 1.4rem; }
.sheet-link { color: var(--brass); }

.sheet h2 {
  margin: 2.2rem 0 .7rem;
  font-size: 1.05rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  opacity: .62;
  font-weight: 650;
}
.sheet p { margin: 0 0 1rem; }
.sheet-lede { font-size: 1.02rem; opacity: .8; margin-bottom: .4rem; }
.sheet-price {
  font-size: clamp(1.5rem, 4vw, 2rem);
  font-weight: 650;
  color: var(--brass);
  margin: .2rem 0 1rem;
}
.sheet-psf { font-size: .8rem; font-weight: 400; opacity: .6; color: var(--paper); }

.factstrip {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  margin: 0 0 1.8rem;
  padding: .9rem 0;
  list-style: none;
  border-top: 1px solid rgba(var(--glow), .14);
  border-bottom: 1px solid rgba(var(--glow), .14);
  font-size: .95rem;
  opacity: .9;
}
.factstrip b { font-weight: 650; }

.prose p { max-width: 38rem; line-height: 1.65; opacity: .88; }

.sheet table.spec {
  width: 100%;
  border-collapse: collapse;
  font-size: .93rem;
}
.sheet table.spec tr { border-bottom: 1px solid rgba(var(--glow), .1); }
.sheet table.spec th {
  text-align: left;
  font-weight: 650;
  padding: .55rem 1rem .55rem 0;
  white-space: nowrap;
  vertical-align: top;
  opacity: .62;
  width: 11rem;
}
.sheet table.spec td { padding: .55rem 0; opacity: .92; }

.bullets {
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
  display: grid;
  gap: .35rem;
  max-width: 38rem;
}
.bullets li {
  padding-left: 1.1rem;
  position: relative;
  opacity: .9;
  font-size: .95rem;
}
.bullets li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--brass);
}

.sheet-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .8rem;
  margin: 2.4rem 0 1rem;
}
.sheet-button {
  display: inline-block;
  background: var(--brass);
  color: #22190a;
  padding: .62rem 1.15rem;
  border-radius: 999px;
  font-weight: 650;
  text-decoration: none;
}
.sheet-button:hover { filter: brightness(1.07); }
.sheet-source { font-size: .8rem; opacity: .5; max-width: 38rem; }

.sheet-foot {
  max-width: 44rem;
  margin: 0 auto;
  padding: 2rem 1.5rem 3rem;
  padding-left: max(1.5rem, clamp(7rem, 13vw, 10rem));
  font-size: .8rem;
  opacity: .55;
}

@media (max-width: 900px) {
  .sheet, .sheet-foot { padding-left: 1.5rem; }
}

/* ------------------------------------------------------- the warm pages -- */

/* The tour is evening: dark, cinematic, you are outside looking in. A page you
   actually READ wants the opposite — daylight on paper, the colours of the
   house itself (linen, oak, stone, brass). Long-form text on near-black is
   tiring, and this page is long by design.
   Scoped to .page-body.warm, and it has to re-dress the header and the
   standing menu, which are white-on-photo everywhere else. */

.page-body.warm {
  --ink: #2b2018;                 /* deep warm brown, not black */
  --soft: #6a584a;                /* secondary text */
  --faint: #8d7c6c;
  --line: #e6dac6;
  --paper-1: #f7f0e4;             /* the page */
  --paper-2: #fffdf8;             /* the cards */
  --gold: #96661a;                /* brass dark enough to read as text */
  color: var(--ink);
  background:
    radial-gradient(90% 55% at 50% 0%, #fdf8ef 0%, rgba(253,248,239,0) 60%) no-repeat,
    #f4ecdd;
}

/* The canvas itself, so an overscroll bounce does not flash the dark tour
   background at the top or bottom of a warm page. */
html:has(.page-body.warm) { background: #f4ecdd; }

/* --- the chrome, re-dressed for daylight --- */
.page-body.warm .hud-top::before {
  background: linear-gradient(to bottom, rgba(247, 240, 228, .96), rgba(247, 240, 228, 0));
}
.page-body.warm .brand,
.page-body.warm .hud-nav a {
  color: var(--ink);
  text-shadow: none;
}
.page-body.warm .hud-nav a { opacity: .72; }
.page-body.warm .hud-nav a:hover { opacity: 1; }
.page-body.warm .hud-cta {
  background: var(--ink);
  color: #f7f0e4 !important;
  box-shadow: 0 4px 14px rgba(43, 32, 24, .22);
}
.page-body.warm .sidemenu a {
  color: var(--ink);
  text-shadow: none;
  opacity: .62;
}
.page-body.warm .sidemenu a:hover,
.page-body.warm .sidemenu a:focus-visible { opacity: 1; border-left-color: var(--gold); }
.page-body.warm .sidemenu a[aria-current="page"] { color: var(--gold); opacity: 1; }
@media (max-width: 900px) {
  .page-body.warm .sidemenu {
    background: rgba(250, 245, 235, .98);
    border-right-color: var(--line);
  }
  .page-body.warm .menu-button {
    background: var(--paper-2);
    border-color: var(--line);
    box-shadow: 0 4px 14px rgba(43, 32, 24, .16);
  }
  .page-body.warm .menu-bars,
  .page-body.warm .menu-bars::before,
  .page-body.warm .menu-bars::after { background: var(--ink); }
}
.page-body.warm .sheet-foot { color: var(--soft); opacity: .8; }

/* --- the sheet itself --- */
.warmsheet {
  display: grid;
  gap: clamp(1.1rem, 2.4vw, 1.7rem);
  max-width: 52rem;
}
.warm .sheet { max-width: 58rem; }

.wcard {
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: clamp(1.2rem, 3vw, 1.9rem);
  box-shadow: 0 1px 2px rgba(90, 66, 42, .05), 0 10px 30px rgba(90, 66, 42, .055);
}

.warmsheet h2 {
  margin: 0 0 .9rem;
  font-size: .78rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 650;
}
.wspecs > h2, .wrooms > h2 { padding-left: .2rem; }
.warmsheet h3 {
  margin: 0 0 .7rem;
  font-size: 1rem;
  font-weight: 650;
  color: var(--ink);
}
.warmsheet p { margin: 0 0 .85rem; line-height: 1.68; color: var(--soft); }
.warmsheet p:last-child { margin-bottom: 0; }

/* --- the hero --- */
.whero { padding: 0; overflow: hidden; }
.whero-img {
  display: block;
  width: 100%;
  height: clamp(180px, 30vw, 320px);
  object-fit: cover;
  object-position: 50% 62%;
}
.whero-body { padding: clamp(1.2rem, 3vw, 1.9rem); }
.weyebrow {
  margin: 0 0 .5rem !important;
  font-size: .72rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 650;
}
.whero h1 {
  margin: 0 0 .5rem;
  font-size: clamp(1.5rem, 4vw, 2.1rem);
  line-height: 1.2;
  letter-spacing: -.015em;
  color: var(--ink);
}
.waddress { margin: 0 0 1rem !important; color: var(--soft); }
.wprice {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  flex-wrap: wrap;
  margin: 0 0 1.1rem !important;
  font-size: clamp(1.6rem, 4.4vw, 2.1rem);
  font-weight: 650;
  color: var(--ink) !important;
}
.wprice span { font-size: .82rem; font-weight: 400; color: var(--faint); }

.wfacts {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin: 0 0 1.3rem;
  padding: 0;
  list-style: none;
}
.wfacts li {
  display: flex;
  align-items: baseline;
  gap: .35rem;
  padding: .42rem .8rem;
  border-radius: 999px;
  background: #f4ecdd;
  border: 1px solid var(--line);
  font-size: .85rem;
  color: var(--soft);
}
.wfacts b { font-size: .98rem; color: var(--ink); font-weight: 650; }

/* A caveat that has to stay next to the number it qualifies. */
.wcaveat {
  margin: -.7rem 0 1.2rem !important;
  font-size: .86rem;
  color: #8a6a22;
}
.wcaveat b { font-weight: 650; }

.wcta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin: 0 !important;
}
.wbutton {
  display: inline-block;
  background: var(--gold);
  color: #fffdf8;
  padding: .68rem 1.3rem;
  border-radius: 999px;
  font-weight: 650;
  text-decoration: none;
  box-shadow: 0 6px 18px rgba(150, 102, 26, .28);
}
.wbutton:hover { filter: brightness(1.08); }
.wlink { color: var(--gold); text-decoration: none; font-weight: 650; font-size: .9rem; }
.wlink:hover { text-decoration: underline; }

.wstory .wlede { font-size: 1.06rem; color: var(--ink); }

/* --- room by room --- */
/* Four rooms: a 2x2 reads as a set, 3-plus-1 reads as a mistake. */
.wroomgrid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(.8rem, 2vw, 1.1rem);
}
@media (min-width: 34rem) {
  .wroomgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.wroom {
  display: block;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
  text-decoration: none;
  box-shadow: 0 1px 2px rgba(90, 66, 42, .05), 0 8px 22px rgba(90, 66, 42, .05);
  transition: transform .18s ease, box-shadow .18s ease;
}
.wroom:hover, .wroom:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 2px 4px rgba(90, 66, 42, .07), 0 16px 34px rgba(90, 66, 42, .11);
  outline: none;
}
.wroom img { display: block; width: 100%; height: 8.6rem; object-fit: cover; }
.wroom-name {
  display: block;
  padding: .7rem .9rem .1rem;
  font-weight: 650;
  color: var(--ink);
  font-size: .95rem;
}
.wroom-note {
  display: block;
  padding: 0 .9rem .8rem;
  font-size: .8rem;
  color: var(--faint);
}

/* --- the specification, in groups --- */
.wspecgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: clamp(.8rem, 2vw, 1.1rem);
}
.wspec dl { margin: 0; display: grid; gap: .1rem; }
.wspec dt {
  font-size: .74rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: .7rem;
}
.wspec dt:first-of-type { margin-top: 0; }
.wspec dd { margin: 0; color: var(--ink); font-size: .95rem; line-height: 1.5; }

.warm .bullets li { color: var(--soft); }
.warm .bullets li::before { background: var(--gold); opacity: .8; }

/* --- the honest gaps --- */
.wnote {
  background: #fbf3e2;
  border-color: #ecd9b4;
}

/* The house's own account of the storm — the strongest thing on the page, so
   it is given its own colour rather than the amber "be careful" note. */
.wstood {
  background: #eef6ec;
  border-color: #cfe4c9;
}
.wstood h2 { color: #3d6b39; }
.wstood .wlede { color: #23491f; font-size: 1.1rem; }
.wstood .bullets li::before { background: #4b8544; }
.wnote h2 { color: #8a6a22; }
.wnote p { color: #6b5433; font-weight: 650; }
.wnote-sub { font-weight: 400 !important; opacity: .85; font-size: .92rem; }

/* The uploaded gallery, on the warm pages. */
.warm .masonry { columns: 3; column-gap: 1rem; }
@media (max-width: 900px) { .warm .masonry { columns: 2; } }
@media (max-width: 560px) { .warm .masonry { columns: 1; } }
.warm .shot {
  break-inside: avoid;
  margin: 0 0 1rem;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
}
.warm .shot img { cursor: zoom-in; width: 100%; display: block; }
.warm .shot figcaption {
  padding: .55rem .8rem;
  font-size: .85rem;
  color: var(--faint);
}

.wchips { display: flex; flex-wrap: wrap; gap: .45rem; margin: 0 0 1rem; }
.wchip {
  padding: .3rem .8rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--paper-2);
  color: var(--soft);
  text-decoration: none;
  font-size: .85rem;
}
.wchip.on { background: var(--gold); border-color: var(--gold); color: #fffdf8; }

/* Sources, listed rather than implied. */
.wsources-card { background: #f6efe1; }
.wsources-card .bullets li { font-size: .88rem; }
.warmsheet a.wlink[target="_blank"]::after { content: " ↗"; opacity: .55; }
.warmsheet em { font-style: italic; }
.warmsheet strong { color: var(--ink); font-weight: 650; }

.wclose {
  text-align: center;
  padding: 1.4rem .5rem .4rem;
}
.wclose .wcta { justify-content: center; }
.wsource {
  font-size: .78rem;
  color: var(--faint);
  text-align: center;
  max-width: 34rem;
  margin: .6rem auto 0 !important;
}

/* -------------------------------------------- keyboard and crawler paths -- */

/* The same destinations as the picture, off-screen until tabbed to. */
.exits {
  position: absolute;
  left: -10000px;
  top: 0;
}
.exits a:focus {
  position: fixed;
  left: 50%;
  top: 1rem;
  transform: translateX(-50%);
  z-index: 60;
  background: var(--paper);
  color: var(--ink);
  padding: .55rem 1rem;
  border-radius: 999px;
  text-decoration: none;
}

/* Present in the document for search engines and for anyone who takes the
   skip link; parked off-screen rather than display:none so it stays real. */
.facts {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Taking the skip link turns the tour off and leaves a plain, scrollable
   page — the stage is fixed and the body cannot scroll otherwise. */
.scene-body:has(#facts:target) { overflow: auto; }
.scene-body:has(#facts:target) .viewport,
.scene-body:has(#facts:target) .hud-bottom { display: none; }

.facts:focus-within,
.facts:target {
  position: static;
  width: auto;
  height: auto;
  max-width: 42rem;
  margin: 0 auto;
  padding: 6rem 1.5rem 3rem;
  overflow: visible;
}

@media (prefers-reduced-motion: reduce) {
  .stage, .backdrop { animation: none; opacity: 1; }
  .opening .doorway-leaf { transition: transform 300ms ease; }
  .hs > *, .room-door { animation: none !important; }
  .hs > * { fill: rgba(var(--glow), .09); stroke: rgba(var(--glow), .5); }
  /* The fire and the candles hold still, but stay lit rather than vanishing. */
  .fire > span, .candle { animation: none !important; }
}
