*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: #05070d;
  font-family: system-ui, -apple-system, sans-serif;
  overflow: hidden;
}

/* ---------------------------------------------------------------
   Stage
   The frame keeps the plate's 16:9 aspect and "covers" the viewport,
   so every % coordinate below stays locked to the same pixel of the
   artwork no matter how the window is resized.

   --px / --py are set by JS from the pointer. Each layer multiplies
   them by its own depth factor, so the street slides further than
   the sky and the mist slides further than the street.
----------------------------------------------------------------*/

.hero {
  position: relative;
  width: 100vw;
  height: 100svh;
  overflow: hidden;
  background: #05070d;
}

.frame {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: max(100vw, 177.78svh);
  height: max(56.25vw, 100svh);
  --px: 0;
  --py: 0;
}

.stars, .scene, .mist, .zones {
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform;
}

.stars  { transform: translate3d(calc(var(--px) * 0.22px), calc(var(--py) * 0.22px), 0) scale(1.008); }
/* Keep this as close to 1.0 as the parallax allows: every extra
   percent is another percent of upscale on the plate. */
.scene,
.zones  { transform: translate3d(calc(var(--px) * 1px), calc(var(--py) * 1px), 0) scale(1.015); }
.mist--far  { transform: translate3d(calc(var(--px) * 1.5px), calc(var(--py) * 1.5px), 0) scale(1.06); }
.mist--near { transform: translate3d(calc(var(--px) * 2.4px), calc(var(--py) * 2.4px), 0) scale(1.08); }

.scene { position: absolute; inset: 0; z-index: 2; }

.plate {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
  -webkit-user-drag: none;
}

/* ---------------------------------------------------------------
   Stars — masked to the open sky so the tree canopy stays solid
----------------------------------------------------------------*/

.stars {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 1.1s ease;
  -webkit-mask-image: radial-gradient(ellipse 54% 30% at 49% 13%, #000 42%, rgba(0,0,0,0.55) 68%, transparent 88%);
          mask-image: radial-gradient(ellipse 54% 30% at 49% 13%, #000 42%, rgba(0,0,0,0.55) 68%, transparent 88%);
}

/* ---------------------------------------------------------------
   Lit overlays
----------------------------------------------------------------*/

.plate--lit {
  opacity: 0;
  transition: opacity 1.1s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: opacity;
}

/* The lit render carries the actual window light — grading it up
   here is what makes the interiors read as vivid rather than dim. */
.plate--lit.is-on { opacity: 1; filter: saturate(1.18) brightness(1.09) contrast(1.05); }

.plate--lit.is-preview {
  opacity: 0.26;
  transition: opacity 0.4s ease-out;
}

/* The three horizontal slices. Reused by the reflections below. */
.plate--lit[data-house="left"],
.reflect__img[data-house="left"] {
  -webkit-mask-image: linear-gradient(to right, transparent 2%, #000 7%, #000 36%, transparent 41%);
          mask-image: linear-gradient(to right, transparent 2%, #000 7%, #000 36%, transparent 41%);
}
.plate--lit[data-house="middle"],
.reflect__img[data-house="middle"] {
  -webkit-mask-image: linear-gradient(to right, transparent 34%, #000 39%, #000 62%, transparent 67%);
          mask-image: linear-gradient(to right, transparent 34%, #000 39%, #000 62%, transparent 67%);
}
.plate--lit[data-house="right"],
.reflect__img[data-house="right"] {
  -webkit-mask-image: linear-gradient(to right, transparent 61%, #000 66%, #000 91%, transparent 96%);
          mask-image: linear-gradient(to right, transparent 61%, #000 66%, #000 91%, transparent 96%);
}

/* ---------------------------------------------------------------
   Reflection
   The lit plate, mirrored about the kerb line and dropped into the
   wet asphalt. Blur and a downward fade do the rest: a reflection
   loses detail with distance from the object, never the other way.
----------------------------------------------------------------*/

.reflect {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 73%, rgba(0,0,0,0.38) 80%, rgba(0,0,0,0.14) 88%, transparent 96%);
          mask-image: linear-gradient(to bottom,
    transparent 73%, rgba(0,0,0,0.38) 80%, rgba(0,0,0,0.14) 88%, transparent 96%);
}

.reflect__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* mirror about y = 74% of the frame — the kerb line */
  transform: translateY(48%) scaleY(-1);
  filter: blur(11px) saturate(1.1) brightness(0.78);
  transition: opacity 1.2s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.reflect__img.is-on { opacity: 0.19; animation: ripple 9s ease-in-out infinite; }
.reflect__img.is-preview { opacity: 0.05; transition: opacity 0.4s ease-out; }

/* water never sits perfectly still */
@keyframes ripple {
  0%, 100% { transform: translateY(48%)    scaleY(-1)     scaleX(1);      }
  50%      { transform: translateY(48.35%) scaleY(-1.012) scaleX(1.004);  }
}

/* ---------------------------------------------------------------
   Light on surfaces
   spill  — the pool thrown forward onto lawn and driveway
   bloom  — atmospheric haze around the whole house
   panes  — the hard bright cores in the windows themselves
----------------------------------------------------------------*/

.spill, .panes { position: absolute; pointer-events: none; opacity: 0; }

.spill {
  width: 30%;
  height: 13%;
  transform: translate(-50%, -50%) scaleX(0.8);
  border-radius: 50%;
  filter: blur(10px);
  mix-blend-mode: screen;
  background: radial-gradient(ellipse at center,
    rgba(255, 208, 142, 0.46) 0%,
    rgba(255, 188, 112, 0.20) 44%,
    transparent 72%);
  transition: opacity 1.2s cubic-bezier(0.22, 0.61, 0.36, 1),
              transform 1.4s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.spill[data-house="left"]   { left: 20%;   top: 73%; }
.spill[data-house="middle"] { left: 49.5%; top: 72%; }
.spill[data-house="right"]  { left: 78%;   top: 73%; }

.spill.is-on { opacity: 1; transform: translate(-50%, -50%) scaleX(1); }

@keyframes warmup {
  0%   { opacity: 0;    }
  14%  { opacity: 0.55; }
  20%  { opacity: 0.12; }
  30%  { opacity: 0.78; }
  38%  { opacity: 0.38; }
  52%  { opacity: 0.92; }
  70%  { opacity: 0.72; }
  100% { opacity: 1;    }
}

/* Tight highlights right at the glass — this is what stops lit
   windows reading as flat orange rectangles. */
.panes {
  width: 30%;
  height: 24%;
  transform: translate(-50%, -50%);
  mix-blend-mode: screen;
  filter: blur(1.6px);
  transition: opacity 1.3s cubic-bezier(0.22, 0.61, 0.36, 1);
  background:
    radial-gradient(circle 7px at 22% 28%, rgba(255,236,198,0.86), transparent 62%),
    radial-gradient(circle 6px at 44% 24%, rgba(255,232,188,0.76), transparent 62%),
    radial-gradient(circle 7px at 68% 30%, rgba(255,236,198,0.82), transparent 62%),
    radial-gradient(circle 6px at 32% 62%, rgba(255,226,176,0.72), transparent 62%),
    radial-gradient(circle 7px at 58% 66%, rgba(255,232,186,0.78), transparent 62%),
    radial-gradient(circle 5px at 80% 58%, rgba(255,226,176,0.66), transparent 62%);
}

.panes[data-house="left"]   { left: 21%;   top: 57%; }
.panes[data-house="middle"] { left: 49.5%; top: 54%; }
.panes[data-house="right"]  { left: 77.5%; top: 56%; }

.panes.is-on { opacity: 1; animation: shimmer 6.5s ease-in-out infinite; }

/* The strike-up flicker now lives on the glass itself rather than on
   a haze around the house — the filaments are what stutter. */
.panes.is-on.is-warming {
  animation: warmup 1.15s steps(1, end) 1,
             shimmer 6.5s ease-in-out infinite 1.15s;
}

/* filament breathing — a couple of percent, just enough to be alive */
@keyframes shimmer {
  0%, 100% { opacity: 1;    }
  30%      { opacity: 0.93; }
  55%      { opacity: 1;    }
  78%      { opacity: 0.96; }
}

/* ---------------------------------------------------------------
   Atmosphere
----------------------------------------------------------------*/

.haze {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  transition: opacity 1.1s ease;
  background:
    linear-gradient(to bottom,
      rgba(28, 42, 74, 0.11) 0%,
      rgba(24, 36, 66, 0.055) 26%,
      rgba(20, 30, 56, 0.02) 42%,
      transparent 58%);
  mix-blend-mode: screen;
}

.mist {
  position: absolute;
  left: -10%;
  width: 120%;
  z-index: 4;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0.22;
  transition: opacity 1.1s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.mist--far {
  top: 58%;
  height: 16%;
  filter: blur(30px);
  background: linear-gradient(to right,
    transparent 0%, rgba(150, 172, 205, 0.024) 18%, rgba(160, 180, 210, 0.048) 38%,
    rgba(140, 165, 200, 0.018) 62%, rgba(155, 175, 208, 0.042) 82%, transparent 100%);
  animation: drift-far 46s linear infinite;
}

.mist--near {
  top: 74%;
  height: 22%;
  filter: blur(44px);
  opacity: 0.16;
  background: linear-gradient(to right,
    transparent 0%, rgba(140, 160, 195, 0.038) 22%, rgba(125, 148, 185, 0.018) 48%,
    rgba(150, 170, 200, 0.042) 70%, transparent 100%);
  animation: drift-near 64s linear infinite reverse;
}

@keyframes drift-far  { from { background-position: 0 0; } to { background-position: 140% 0; } }
@keyframes drift-near { from { background-position: 0 0; } to { background-position: 160% 0; } }

.grade {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  transition: opacity 1.1s ease;
  background:
    radial-gradient(ellipse 82% 74% at 50% 52%, transparent 52%, rgba(2, 5, 14, 0.20) 84%, rgba(1, 3, 10, 0.42) 100%),
    linear-gradient(to bottom, rgba(3, 7, 18, 0.28) 0%, transparent 16%),
    linear-gradient(to top,    rgba(2, 4, 12, 0.32) 0%, transparent 22%);
}

/* ---------------------------------------------------------------
   Click zones
----------------------------------------------------------------*/

.zones { position: absolute; inset: 0; z-index: 6; }

.zone {
  position: absolute;
  top: 34%;
  height: 46%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.zone[data-house="left"]   { left: 6%;    width: 30%; }
.zone[data-house="middle"] { left: 37.5%; width: 24%; }
.zone[data-house="right"]  { left: 65.5%; width: 25%; }

.zone__glow {
  position: absolute;
  inset: 2% -4% -6%;
  border-radius: 14px;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse at 50% 66%,
    rgba(255, 216, 158, 0.13) 0%,
    rgba(255, 198, 130, 0.055) 44%,
    transparent 74%);
  transition: opacity 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

.zone__sill {
  position: absolute;
  left: 8%;
  right: 8%;
  bottom: -2%;
  height: 12%;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  filter: blur(9px);
  background: radial-gradient(ellipse at center,
    rgba(255, 206, 140, 0.30) 0%,
    rgba(255, 190, 120, 0.10) 46%,
    transparent 74%);
  transform: translateY(6px) scaleX(0.82);
  transition: opacity 0.45s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}

.zone:hover .zone__glow,
.zone:focus-visible .zone__glow { opacity: 1; }

.zone:hover .zone__sill,
.zone:focus-visible .zone__sill { opacity: 1; transform: translateY(0) scaleX(1); }

.zone.is-on .zone__glow,
.zone.is-on .zone__sill { opacity: 0 !important; }

.zone:focus-visible {
  outline: 1px solid rgba(255, 214, 150, 0.45);
  outline-offset: -2px;
  border-radius: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .mist, .panes.is-on, .panes.is-on.is-warming, .reflect__img.is-on { animation: none; }
  .plate--lit, .spill, .panes { transition-duration: 0.3s; }
  .stars, .scene, .zones, .mist { transition: opacity 0.3s ease; }
}
