/* Town: page chrome, the street scene, the room frame and every ambient loop.
   Chrome is Kai (../kai/kai.css). The illustration is product content with its own palette:
   the sky, ground and building colours below, plus each room's palette in its rooms/*.js. */

:root {
  --t-s1: #8fcbe6; --t-s2: #aad8ec; --t-s3: #c6e5f0; --t-s4: #e3f1ee;
  --t-hill1: #b4d9bf; --t-hill2: #94c8a6; --t-hill3: #76b690;
  --t-grass: #86c496; --t-grass2: #6fb083;
  --t-stone: #d9c3a8; --t-stone2: #c4a98a; --t-road: #8a8092; --t-road2: #a39aa9; --t-line: #fff3d6;
  --t-ground: #dccdbd; --t-curb: #cdbca8; --t-pave: #b8a99d;
  --t-cloud: #ffffff; --t-sun: #ffd86b;
  --t-tree1: #5aa070; --t-tree2: #7bbd86; --t-trunk: #8b5d3f;
  --t-lamp-glow: 0;
  --t-star: 0;
  --t-p1: #9acb94; --t-p2: #b9d98f; --t-p3: #7fc0a0; --t-p4: #c9dc92;
}
:root[data-theme=dark] {
  --t-s1: #151a3f; --t-s2: #1d2252; --t-s3: #282e66; --t-s4: #373c7c;
  --t-hill1: #2d3770; --t-hill2: #252e62; --t-hill3: #1e2755;
  --t-grass: #2c4a5e; --t-grass2: #254052;
  --t-stone: #4a4568; --t-stone2: #3d3858; --t-road: #2e2b42; --t-road2: #3c3856; --t-line: #8a82a8;
  --t-ground: #2f2a45; --t-curb: #413b5e; --t-pave: #3f3950;
  --t-cloud: #566096; --t-sun: #fff3c4;
  --t-tree1: #2a5367; --t-tree2: #356478; --t-trunk: #4a3a4e;
  --t-lamp-glow: 1;
  --t-star: 1;
  --t-p1: #2f5566; --t-p2: #365f66; --t-p3: #2a4a60; --t-p4: #3a6068;
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body { margin: 0; min-height: 100dvh; background: var(--kai-paper); overflow-x: clip; }
svg { display: block; }

/* ------------------------------------------------------------ masthead and rail */
.mast {
  position: fixed; inset: 0 0 auto 0; z-index: 20; display: flex; justify-content: space-between; align-items: center;
  padding: calc(24px + env(safe-area-inset-top)) clamp(16px, 4vw, 48px) 0; pointer-events: none;
}
.mast > * { pointer-events: auto; }
.mast svg { width: 18px; height: 18px; }
.rail {
  position: fixed; right: clamp(16px, 2.4vw, 32px); top: 50%; transform: translateY(-50%); z-index: 20;
  display: none; flex-direction: column; gap: 8px; align-items: center;
}
.rail-item {
  position: relative; display: grid; place-items: center; width: 44px; height: 44px; padding: 0;
  border: 1px solid var(--kai-line); border-radius: 999px; background: var(--kai-paper); color: var(--kai-ink);
  cursor: pointer; text-decoration: none; transition: background-color var(--kai-fast) ease, border-color var(--kai-fast) ease, color var(--kai-fast) ease;
}
.rail-item svg { width: 22px; height: 22px; }
.rail-item:hover { background: var(--kai-wash); border-color: var(--kai-control); }
.rail-item:focus-visible { outline: 2px solid var(--kai-focus); outline-offset: 3px; }
.rail-item[aria-current=true] { background: var(--kai-ink); color: var(--kai-paper); border-color: var(--kai-ink); }
.rail-item.is-shut { border-style: dashed; color: var(--kai-muted); }
.rail-item.is-shut:hover { color: var(--kai-ink); }
.rail-sep { width: 14px; height: 1px; background: var(--kai-line); margin: 2px 0; }
.rail-item::after {
  content: attr(data-label); position: absolute; right: calc(100% + 10px); top: 50%; translate: 0 -50%;
  padding: 6px 10px; border-radius: 10px; background: var(--kai-ink); color: var(--kai-paper); font: 400 12.5px/1 var(--kai-body);
  white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity var(--kai-fast) ease;
}
.rail-item:hover::after, .rail-item:focus-visible::after { opacity: 1; }
@media (min-width: 1100px) { .rail { display: flex; } .mast-rooms { display: none; } }
/* Phones: the way between rooms sits under the thumb, bottom right (the rooms sheet opens from the bottom too); the theme stays top left */
@media (max-width: 700px) {
  .mast-rooms { position: fixed; right: calc(16px + env(safe-area-inset-right)); bottom: calc(16px + env(safe-area-inset-bottom)); }
}
@media (max-height: 640px) { .rail { gap: 4px; } }

/* ------------------------------------------------------------ the street (overview) */
.street { position: relative; background: var(--t-s1); overflow: clip; aspect-ratio: 1490 / 854; }
@media (max-width: 639.98px) { .street { aspect-ratio: 596 / 1874; } }
.street svg.map { width: 100%; height: auto; }
.street .bld { cursor: pointer; outline: none; }
.bld-lift { transition: transform .32s var(--kai-ease); }
.bld:hover .bld-lift, .bld:focus-visible .bld-lift { transform: translateY(-7px); }
.bld .hit { fill: transparent; stroke: transparent; stroke-width: 3px; rx: 14; vector-effect: non-scaling-stroke; }
.bld:focus-visible .hit { stroke: var(--kai-focus); }
.bld.is-nope .shutter { animation: a-rattle .46s var(--kai-ease); }
@keyframes a-rattle { 0%, 100% { transform: translateY(0); } 15% { transform: translateY(-5px); } 30% { transform: translateY(2px); } 45% { transform: translateY(-3px); } 60% { transform: translateY(1px); } 78% { transform: translateY(-1px); } }
.street-sun { transition: transform .9s var(--kai-ease); }

/* ------------------------------------------------------------ a lot: one building in section */
.lot { position: relative; background: var(--t-s1); padding-top: calc(86px + env(safe-area-inset-top)); overflow: clip; }
.lot-sky { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.lot-sky svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.lot-scenery { position: absolute; left: 0; right: 0; bottom: 0; height: 800px; pointer-events: none; overflow: hidden; }
.lot-scenery svg { position: absolute; left: 50%; bottom: 0; width: 2000px; height: 800px; translate: -50% 0; max-width: none; }
.lot-stage { position: relative; margin: 0 auto; width: min(1180px, calc((100svh - 150px) * 1.3636), calc(100% - 32px)); aspect-ratio: var(--ar); } /* the aspect-ratio is the room's own: the stage keeps its height while the room is not drawn (see .is-drawn below) */
.room { position: relative; line-height: 0; border-radius: 6px 6px 0 0; aspect-ratio: var(--ar); } /* the room's own size, so it is the same drawn or not (on phones the camera's, below) */
.room-cam { position: relative; width: 100%; isolation: isolate; }
.room-stack { position: relative; width: 100%; aspect-ratio: var(--ar); }
/* every layer is its own composited surface: static ones are painted once, live ones repaint alone */
.layer { position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; will-change: transform; }
/* Draw only what is near the screen. A lot holds about 5 000 elements (the room 4 000, the sky and the ground the rest) and the page eleven of them, so app.js puts .is-drawn on the lots that are near
   the screen (and on the ones next to the one in view, while the page is idle) and the sky, the ground and the room of every other lot are content-visibility: hidden: not styled, laid out or painted,
   but what was worked out the last time is kept, so a lot that comes back costs its paint, not its style (display: none, used before, threw it away: 50 to 110 ms a lot here, four times that on a
   slow phone). The size of each box does not depend on what it holds (the stage and the room have the room's aspect-ratio, the sky and the ground are fixed boxes), so nothing moves when a lot is
   drawn or let go, and the clip that comes with hidden only applies while nothing is shown. The street gets it too, but only once it is well above the screen (.is-away; the theme
   switch was checked from every lot with it hidden, desktop and phone, no crash). Never `auto`: with content-visibility: auto on the street Chrome 154 crashed the tab on the theme switch, and
   auto reports a layout shift for everything it draws while in view. */
.lot:not(.is-drawn) :is(.lot-sky, .lot-scenery, .room) { content-visibility: hidden; }
.street.is-away > * { content-visibility: hidden; } /* the street, once it is well above the screen (app.js): its layers are absolute boxes of a fixed size */
.shared-defs { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; } /* the drawing definitions every room points at (app.js), always drawn */
@media print { .lot:not(.is-drawn) :is(.lot-sky, .lot-scenery, .room) { content-visibility: visible; } }
.grain {
  position: absolute; inset: 0; pointer-events: none; background: url(tex.png) 0 0 / 384px 384px; opacity: .62;
  mix-blend-mode: overlay; border-radius: inherit; clip-path: inset(12.2% 0 0 0);
}
@media (max-width: 700px) {
  .room { aspect-ratio: var(--car); overflow: hidden; }
  .lot-stage { aspect-ratio: var(--car); }
  .room-cam { width: calc(100% * var(--cam)); will-change: transform; }
}
.replay { position: absolute; right: 14px; bottom: 56px; }
@media (max-width: 700px) { .replay { right: auto; left: 14px; } } /* the rooms pill holds the bottom right corner of a phone screen */
.lot-foot {
  position: relative; background: var(--t-ground); border-top: 5px solid var(--t-curb);
  padding: 34px clamp(16px, 4vw, 48px) 48px;
}
.lot-cap { width: min(920px, 100%); margin: 0 auto; display: grid; gap: 8px; justify-items: start; }
.lot-cap h2 { margin: 0; font-size: clamp(34px, 4.4vw, 48px); letter-spacing: -.045em; }
.lot-cap p { margin: 0; max-width: 46ch; font-size: 15.5px; line-height: 1.5; color: var(--kai-secondary); }
.lot-cap .kai-pill { margin-top: 10px; }
.lot-cap h2.kai-title.has-stop::after { color: var(--kai-accent-ink); }

/* ------------------------------------------------------------ rooms dialog (phones) */
.rooms-list { list-style: none; margin: 8px 0 0; padding: 0; display: grid; }
.rooms-list li + li { border-top: 1px solid var(--kai-line); }
.room-row {
  display: grid; grid-template-columns: 44px 1fr; align-items: center; gap: 14px; min-height: 64px; padding: 10px 0;
  width: 100%; background: none; border: 0; color: var(--kai-ink); font: 500 16px/1.2 var(--kai-body); text-align: left; cursor: pointer; text-decoration: none;
}
.room-row .tile { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 10px; background: var(--kai-wash); }
.room-row .tile svg { width: 22px; height: 22px; }
.room-row.is-shut { color: var(--kai-muted); }
.room-row:focus-visible { outline: 2px solid var(--kai-focus); outline-offset: 2px; border-radius: 8px; }
/* the room in view, as on the rail */
.room-row[aria-current=true] .tile { background: var(--kai-ink); color: var(--kai-paper); }

/* building colours: day by default, mixed night values in the dark theme */
.fx { --w: var(--w-d); --t: var(--t-d); --r: var(--r-d); --a: var(--a-d); }
:root[data-theme=dark] .fx { --w: var(--w-n); --t: var(--t-n); --r: var(--r-n); --a: var(--a-n); }
/* night light. Driven by a variable, not by a descendant selector on :root: the headlamp beams of the street's
   vehicles sit inside <use> copies, where a selector that starts at :root does not match (they stayed off all night) */
.nglow { opacity: var(--nglow, 0); transition: opacity .45s var(--kai-ease); }
:root[data-theme=dark] { --nglow: 1; }
.stars { opacity: 0; transition: opacity .45s var(--kai-ease); }
:root[data-theme=dark] .stars { opacity: 1; }
/* what only shows at night does not run by day (night glows, pulses and the stars sit at opacity 0 until dark); a variable, so the street copies follow too */
:root .nglow, :root .nglow *, :root .stars .a-twk { animation-play-state: var(--nplay, paused); }
:root[data-theme=dark] { --nplay: running; }
/* and by day they have no loop at all: a paused loop is still a compositor layer (about a hundred a lot), and nothing of it shows (the copies inside <use> keep the paused rule above) */
:root:not([data-theme=dark]) :is(.nglow, .nglow *, .stars .a-twk) { animation-name: none !important; }

/* ------------------------------------------------------------ light and the day/night swap */
.day-only { display: inline; }
.night-only { display: none; }
:root[data-theme=dark] .day-only { display: none; }
:root[data-theme=dark] .night-only { display: inline; }
.glow { opacity: .5; transition: opacity .45s var(--kai-ease); }
:root[data-theme=dark] .glow { opacity: 1; }
.glow.plain { opacity: 1; }
/* Kai room layers: night wash multiplies, warm pools and the sun beam are screened on top */
/* the night wash and the warm pools are plain alpha shapes inside each layer (no blend layers to composite) */
.k-neon { opacity: .5; } :root[data-theme=dark] .k-neon { opacity: 1; animation: a-flick 5s steps(1) infinite; }
@keyframes a-flick { 0%, 92%, 96%, 100% { opacity: 1; } 93%, 97% { opacity: .35; } }
.g-sun { opacity: .5; }

/* ------------------------------------------------------------ ambient loops (rooms) */
@keyframes a-sway { 0%, 100% { transform: rotate(-1.8deg); } 50% { transform: rotate(2.2deg); } }
.a-sway { animation: a-sway 5.2s ease-in-out infinite; }
.a-sway.b { animation-duration: 6.9s; animation-delay: -2.4s; }
@keyframes a-swing { 0%, 100% { transform: rotate(-1.4deg); } 50% { transform: rotate(1.4deg); } }
.swing { animation: a-swing 4.6s ease-in-out infinite; }
@keyframes a-fan { to { transform: rotate(360deg); } }
.a-fan { animation: a-fan 1.15s linear infinite; }
@keyframes a-twk { 0%, 100% { opacity: 1; } 50% { opacity: .42; } }
.a-twk { animation: a-twk 3.4s ease-in-out infinite; animation-delay: var(--d, 0s); }
@keyframes a-cloud { from { transform: translateX(-46px); } to { transform: translateX(112px); } }
.a-cloud { animation: a-cloud 38s linear infinite; }
.a-cloud.b { animation-duration: 54s; animation-delay: -24s; }
@keyframes a-curtain { 0%, 100% { transform: skewX(0deg); } 50% { transform: skewX(1.8deg); } }
.a-curtain { animation: a-curtain 7s ease-in-out infinite; }
.a-curtain.b { animation-delay: -3.4s; animation-direction: reverse; }
@keyframes a-mote { 0%, 100% { transform: translate(0, 0); opacity: .15; } 35% { opacity: .95; } 55% { transform: translate(9px, -16px); opacity: .7; } 80% { opacity: .25; } }
.a-mote { animation: a-mote var(--t, 7s) ease-in-out infinite; animation-delay: calc(var(--d, 0s) * -1); }
@keyframes a-orbit { to { transform: rotate(360deg); } }
.a-lobe.l1 { animation: a-orbit 9s linear infinite; }
.a-lobe.l2 { animation: a-orbit 13s linear infinite reverse; }
.a-lobe.l3 { animation: a-orbit 17s linear infinite; }
@keyframes a-drift { from { transform: translateX(-90px); } to { transform: translateX(var(--span, 1500px)); } }
.a-drift { animation: a-drift var(--dur, 160s) linear infinite; animation-delay: calc(var(--d, 0s) * -1); }
@keyframes a-smoke { 0% { transform: translate(0, 0) scale(.5); opacity: 0; } 20% { opacity: .75; } 100% { transform: translate(10px, -34px) scale(1.5); opacity: 0; } }
.a-smoke { animation: a-smoke 4.8s ease-out infinite; animation-delay: var(--d, 0s); }
@keyframes a-bird { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(.35); } }
.a-bird { animation: a-bird .7s ease-in-out infinite; }
@keyframes a-walk { from { transform: translateX(-60px); } to { transform: translateX(var(--span, 1400px)); } }
.a-walk { animation: a-walk var(--dur, 90s) linear infinite; animation-delay: calc(var(--d, 0s) * -1); }
@keyframes a-step { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
.a-step { animation: a-step .55s ease-in-out infinite; }
@keyframes a-ring { 0% { r: 7px; opacity: .9; } 100% { r: 15px; opacity: 0; } }
.a-ring { animation: a-ring 1.9s ease-out infinite; }
@keyframes a-pulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
.a-pulse { animation: a-pulse 3.6s ease-in-out infinite; }

@keyframes a-note { 0% { transform: translate(0, 0) rotate(0); opacity: 0; } 14% { opacity: .95; } 100% { transform: translate(30px, -78px) rotate(16deg); opacity: 0; } }
.a-note { animation: a-note 4.8s ease-out infinite; animation-delay: var(--d, 0s); }
@keyframes a-foam { 0% { transform: translate(0, 0) scale(.5); opacity: 0; } 20% { opacity: .9; } 100% { transform: translate(var(--x, 0px), -36px) scale(1.25); opacity: 0; } }
.a-foam { animation: a-foam 3.6s ease-out infinite; animation-delay: var(--d, 0s); }
@keyframes a-air { 0%, 100% { opacity: .85; transform: translateX(0); } 50% { opacity: .25; transform: translateX(-9px); } }
.a-air { animation: a-air .5s ease-in-out infinite; animation-delay: var(--d, 0s); }
@keyframes a-clip { 0% { transform: translate(0, 0) rotate(0); opacity: 0; } 14% { opacity: .9; } 100% { transform: translate(var(--dx, 8px), 78px) rotate(210deg); opacity: 0; } }
.a-clip { animation: a-clip 5s ease-in infinite; animation-delay: var(--d, 0s); }
@keyframes a-blink { 0%, 46% { opacity: 1; } 54%, 100% { opacity: .18; } }
.a-blink { animation: a-blink 1.6s ease-in-out infinite; }
@keyframes a-fish { from { transform: translateX(0); } to { transform: translateX(var(--s, 30px)); } }
.a-fish { animation: a-fish var(--d, 5s) ease-in-out infinite alternate; animation-delay: calc(var(--dl, 0s) * -1); }
@keyframes a-flip { 0%, 49.99% { transform: scaleX(1); } 50%, 100% { transform: scaleX(-1); } }
.a-flip { animation: a-flip calc(var(--d, 5s) * 2) step-end infinite; animation-delay: calc(var(--dl, 0s) * -1); transform-origin: 0px 0px; }
@keyframes a-peck { 0%, 52%, 100% { transform: rotate(0deg); } 57%, 65% { transform: rotate(17deg); } 61% { transform: rotate(5deg); } 70% { transform: rotate(14deg); } 76% { transform: rotate(0deg); } }
.a-peck { animation: a-peck 5.6s ease-in-out infinite; transform-origin: 0px -4px; }
.a-peck.b { animation-duration: 7.1s; animation-delay: -3s; }
@keyframes a-vane { 0%, 100% { transform: rotate(-24deg); } 50% { transform: rotate(26deg); } }
.a-vane { animation: a-vane 7s ease-in-out infinite; }

/* Kai room: ambient loops tick in small steps so a still moment costs nothing to repaint */
.room-kai .a-twk { animation-timing-function: steps(8); }
.room-kai .a-sway, .room-kai .swing, .room-kai .a-curtain { animation-timing-function: steps(40); }
.room-kai .a-fan { animation-timing-function: steps(23); }
.room-kai .a-mote, .room-kai .a-foam, .room-kai .a-note, .room-kai .a-clip, .room-kai .a-smoke { animation-timing-function: steps(30); }
.room-kai .a-fish { animation-timing-function: steps(70); }
.room-kai .a-air { animation-timing-function: steps(4); }

/* Off screen, or while the page scrolls: every loop is frozen. JS toggles .is-paused (a lot, the street), .st-off (a row of the street) and
   .is-scrolling (<html>); the rule that does it is in rest.css (generated, one class per loop; a universal `.is-paused *` here made every flip restyle thousands of elements). */

/* reduced motion: one still frame, no loops. Kai's own transitions already collapse. */
@media (prefers-reduced-motion: reduce) {
  .room *, .street *, .lot-sky *, .lot-scenery * { animation: none !important; }
  .bld-lift, .street-sun { transition: none; }
}
