/* Base reset */
*, *::before, *::after { box-sizing: border-box; }
:root { padding-top: env(safe-area-inset-top, 0px); padding-bottom: env(safe-area-inset-bottom, 0px); }
html, body { overflow-x: clip; }
body { margin: 0; -webkit-font-smoothing: antialiased; }
img { max-width: 100%; }
[hidden] { display: none !important; }

/* Layout: an intro headline over a quiet page; scrolling lifts a window onto a live East Village
   miniature that grows to fill the screen. The work list floats on the left of the map.
   Phones stack: intro, then the map pinned on top with the list scrolling under it. */
:root {
  --paper: #FAF8F3;
  --panel: #FFFFFF;
  --ink: #17171A;
  --muted: #6A6A72;
  --line: #E8E5DE;
  --accent: #5A6FE6;
  --accent-ink: #FFFFFF;
  --cab: #F2B92B;
  --stage: #E7ECEA;
  --serif: "Instrument Serif", "Iowan Old Style", "Times New Roman", serif;
  --script: "Ms Madi", "Snell Roundhand", "Apple Chancery", cursive;
  --body: "Geist", "Helvetica Neue", system-ui, -apple-system, sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --float: 0 10px 30px rgba(20, 22, 26, 0.10), 0 1px 2px rgba(20, 22, 26, 0.06);
  /* the glossy white of the floating pills and the dock (after cindyly.design): a faint top-down sheen, a lit top edge, a soft drop */
  --gloss: linear-gradient(180deg, #FFFFFF 0%, #F6F6F5 100%);
  --gloss-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.95), inset 0 -1px 0 rgba(20, 22, 26, 0.04), 0 0 0 1px rgba(20, 22, 26, 0.06), 0 12px 28px rgba(20, 22, 26, 0.10), 0 2px 6px rgba(20, 22, 26, 0.05);
  --dock-on: var(--accent);
  --sky-load: linear-gradient(180deg, #CBDDEA 0%, #EEF1EC 100%);
  --p: 0;
  /* the orb (the map clipped to a circle under the name): radius and centre, shared by the clip, the shine and the shadow; main.js mirrors these in orbGeom() */
  --uo: min(1vw, 1.6vh);
  --orb-r: calc(10.07 * var(--uo));
  --orb-cy: calc(50% + 13.96 * var(--uo));
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #121214; --panel: #1B1B1E; --ink: #F2F1EE; --muted: #A3A3AB; --line: #2C2C31;
    --accent: #A9B8FF; --accent-ink: #14151F; --cab: #F2B92B; --stage: #1F2427; color-scheme: dark;
    --sky-load: linear-gradient(180deg, #1A2233 0%, #23272E 100%); --gloss: linear-gradient(180deg, #2A2A2F 0%, #212125 100%); --gloss-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 0 0 1px rgba(255, 255, 255, 0.06), 0 12px 28px rgba(0, 0, 0, 0.4);
  }
}
:root[data-theme="dark"] {
  --paper: #121214; --panel: #1B1B1E; --ink: #F2F1EE; --muted: #A3A3AB; --line: #2C2C31;
  --accent: #A9B8FF; --accent-ink: #14151F; --cab: #F2B92B; --stage: #1F2427; color-scheme: dark;
  --sky-load: linear-gradient(180deg, #1A2233 0%, #23272E 100%); --gloss: linear-gradient(180deg, #2A2A2F 0%, #212125 100%); --gloss-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 0 0 1px rgba(255, 255, 255, 0.06), 0 12px 28px rgba(0, 0, 0, 0.4);
}
body { background: var(--paper); color: var(--ink); font-family: var(--body); font-size: 15px; line-height: 1.5; }
body::before { content: ""; position: fixed; inset: 0 0 auto; height: env(safe-area-inset-top, 0px); z-index: 60; background: var(--paper); }
a:focus-visible, button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- loader ---------- */
.loader { position: fixed; inset: 0; z-index: 70; display: grid; place-content: center; justify-items: center; gap: 14px; background: var(--paper); transition: opacity .7s ease .15s, visibility .7s .15s; }
.loaded .loader { opacity: 0; visibility: hidden; }
.ld-label { font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em; color: var(--muted); }
.ld-bar { width: 180px; height: 2px; border-radius: 2px; background: var(--line); overflow: hidden; }
.ld-bar i { display: block; height: 100%; width: 0; background: var(--ink); border-radius: 2px; transition: width .9s cubic-bezier(.3,.7,.2,1); }

/* ---------- intro: toy clouds, the name, and the town in an orb ----------
   Everything is laid out in --u (1/14.4 of the 1440x900 design, capped by height) from the centre of the screen,
   so the composition holds at any size. The orb is the live map (#stageWrap) clipped to a circle; scrolling or
   clicking it grows the circle until the map fills the screen. */
.loader { display: none; }
.hero { position: fixed; inset: 0; z-index: 4; overflow: hidden; pointer-events: none; --u: min(1vw, 1.6vh); }
.cloud-defs { position: absolute; width: 0; height: 0; }
.clouds { position: absolute; inset: 0; }
.cl, .gcloud { position: absolute; left: calc(50% + var(--x) * var(--u)); top: calc(50% + var(--y) * var(--u)); width: calc(var(--w) * var(--u)); opacity: calc(1 - var(--p) * 2.4); transform: translate3d(calc(var(--side) * var(--p) * 38vw), calc(var(--p) * -7vh), 0) scale(calc(1 + var(--p) * 0.6)); }
.cl svg, .gcloud-puff { display: block; width: 100%; height: auto; overflow: visible; filter: drop-shadow(0 calc(0.7 * var(--u)) calc(0.9 * var(--u)) rgba(20, 22, 26, 0.09)); }
.cl svg, .gcloud-drift { animation: drift var(--t) ease-in-out var(--d) infinite alternate; will-change: transform; }
@keyframes drift { from { transform: translateX(calc(var(--dx) * -1)); } to { transform: translateX(var(--dx)); } }
.hero-inner { position: absolute; left: 50%; top: calc(50% - 26.11 * var(--u)); width: min(94vw, 760px); display: flex; flex-direction: column; align-items: center; gap: calc(1.11 * var(--u)); text-align: center; opacity: calc(1 - var(--p) * 3.6); transform: translate(-50%, calc(var(--p) * -60px)); }
.hi-greet { margin: 0; font: 400 16px/1.3 var(--mono); letter-spacing: 0.02em; color: var(--ink); animation: rise .8s cubic-bezier(.22, 1, .36, 1) both; }
.hi-hello { display: inline-block; min-width: 7ch; text-align: right; font-weight: 600; color: var(--cab); }
.caret { display: inline-block; width: 1.5px; height: 1em; margin-left: 2px; vertical-align: -0.12em; background: var(--cab); animation: blink 1.05s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.hi-name { margin: 0; font-family: var(--serif); font-weight: 400; font-size: calc(15.28 * var(--u)); line-height: 0.9; letter-spacing: -0.03em; color: var(--ink); animation: nameRise 1.05s cubic-bezier(.22, 1, .36, 1) both; }
/* the name rises without fading, so it is on screen (and counts as painted) from the first frame */
@keyframes nameRise { from { transform: translateY(0.14em); } to { transform: none; } }
.hi-now { margin: 0; display: flex; flex-direction: column; align-items: center; font-size: 16px; line-height: 1.5; color: var(--muted); animation: rise .9s cubic-bezier(.22, 1, .36, 1) .2s both; }
.hi-label { font-size: 13px; }
.hi-co { pointer-events: auto; color: var(--cab); font-weight: 600; text-decoration: none; }
.hi-line { margin: 0; font-size: 16px; line-height: 1.5; color: var(--ink); text-wrap: balance; animation: rise .9s cubic-bezier(.22, 1, .36, 1) .35s both; }
.hi-cue { pointer-events: auto; color: inherit; text-decoration: none; }
.hi-cue:hover, .hi-co:hover { color: var(--accent); }
@keyframes rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
/* the giraffe rides its own cloud; hover (or tap) and it says hi, typed fast */
.gcloud { --x: 17.36; --y: -18.06; --w: 20; --side: 1; }
.gcloud-drift { position: relative; --t: 42s; --d: -15s; --dx: 30px; }
.gpop { position: absolute; left: calc(5.28 * var(--u)); top: calc(-4.86 * var(--u)); width: calc(8.33 * var(--u)); padding: 0; border: 0; background: none; color: var(--cab); cursor: pointer; pointer-events: auto; }
.gpop .giraffe { display: block; width: 100%; height: auto; overflow: visible; }
.gpop .lg { transform-box: fill-box; transform-origin: 50% 0; }
.gpop:hover .giraffe, .gpop:focus-visible .giraffe { animation: bob .5s ease-in-out infinite; }
.gpop:hover .lg1, .gpop:hover .lg4, .gpop:focus-visible .lg1, .gpop:focus-visible .lg4 { animation: stride .5s ease-in-out infinite; }
.gpop:hover .lg2, .gpop:hover .lg3, .gpop:focus-visible .lg2, .gpop:focus-visible .lg3 { animation: stride .5s ease-in-out -.25s infinite; }
.gcloud-puff { position: relative; }
.gsay { position: absolute; left: calc(1.6 * var(--u) + var(--nudge, 0px)); top: calc(-7.71 * var(--u)); padding: 8px 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--panel); color: var(--ink); font: 400 12px/1.3 var(--mono); white-space: nowrap; box-shadow: var(--float); opacity: 0; transform: translateY(4px) scale(.96); transform-origin: 30% 100%; transition: opacity .18s, transform .18s; }
.gsay:empty { padding: 0; border: 0; }
.gcloud.talking .gsay { opacity: 1; transform: none; }
/* the orb: shading and a highlight that follows the cursor, inside the clipped map; a soft shadow under it */
.orb-shine { position: absolute; left: calc(50% - var(--orb-r)); top: calc(var(--orb-cy) - var(--orb-r)); width: calc(2 * var(--orb-r)); height: calc(2 * var(--orb-r)); border-radius: 50%; pointer-events: none; z-index: 3; --u: min(1vw, 1.6vh); opacity: calc(1 - var(--p) * 2.5);
  background: radial-gradient(circle at calc(34% + var(--mx, 0) * 10%) calc(27% + var(--my, 0) * 8%), rgba(255, 255, 255, 0.78), rgba(255, 255, 255, 0.2) 18%, rgba(255, 255, 255, 0) 40%), radial-gradient(circle at 50% 46%, rgba(0, 0, 0, 0) 56%, rgba(20, 22, 26, 0.16) 80%, rgba(20, 22, 26, 0.42) 100%);
  box-shadow: inset 0 calc(-1.2 * var(--u)) calc(2.4 * var(--u)) rgba(20, 22, 26, 0.28), inset 0 calc(0.6 * var(--u)) calc(1.2 * var(--u)) rgba(255, 255, 255, 0.4); }
body:not(.loaded) .orb-shine { animation: orbWait 1.6s ease-in-out infinite; }
@keyframes orbWait { 50% { filter: brightness(1.08); } }
.orb-shadow { position: fixed; z-index: 1; left: 50%; top: calc(var(--orb-cy) + 1.06 * var(--orb-r)); width: calc(1.69 * var(--orb-r)); height: calc(0.26 * var(--orb-r)); --u: min(1vw, 1.6vh); transform: translate(-50%, 0); border-radius: 50%; background: radial-gradient(closest-side, rgba(20, 22, 26, 0.22), rgba(20, 22, 26, 0)); opacity: calc(1 - var(--p) * 3); pointer-events: none; }
.scroll-room { height: 190vh; }
.orb-cue { display: none; position: fixed; z-index: 4; left: 50%; top: calc(var(--orb-cy) + 1.38 * var(--orb-r)); margin: 0; transform: translateX(-50%); font: 400 14px/1 var(--body); color: var(--muted); white-space: nowrap; pointer-events: none; opacity: calc(1 - var(--p) * 4); }
.sheet-grab { display: none; }

/* ---------- map and chrome ---------- */
.stage-wrap {
  position: fixed; inset: 0; z-index: 2; background: var(--sky-load); overflow: hidden;
  --u: min(1vw, 1.6vh);
  clip-path: circle(calc(var(--orb-r) + (75vmax - var(--orb-r)) * var(--p)) at 50% calc(var(--orb-cy) * (1 - var(--p)) + 50% * var(--p)));
}
.app:not(.entered) .stage-wrap { cursor: pointer; }
.app.entered .stage-wrap { clip-path: none; }
.app.entered .orb-shine { display: none; }
/* during the intro the nav shows and the corner name hides (the big name is the brand) */
.app:not(.entered) header.top nav { opacity: 1; visibility: visible; transform: none; }
.app:not(.entered) .brand { opacity: 0; pointer-events: none; }
#stage { position: absolute; inset: 0; }
#stage canvas { display: block; width: 100%; height: 100%; cursor: grab; touch-action: pan-y; opacity: 0; transition: opacity 1.8s cubic-bezier(.4, 0, .2, 1); }
/* while the town builds the orb is a patch of sky with an arc tracing its rim; then the world fades in (see the reveal in main.js) */
body.world-in #stage canvas { opacity: 1; }
.orb-load { position: absolute; left: calc(50% - var(--orb-r) - 5px); top: calc(var(--orb-cy) - var(--orb-r) - 5px); width: calc(2 * var(--orb-r) + 10px); height: calc(2 * var(--orb-r) + 10px); z-index: 3; pointer-events: none; overflow: visible; transition: opacity .8s ease; animation: orbSpin 1.8s linear infinite; }
.orb-load circle { fill: none; stroke: var(--ink); stroke-opacity: .32; stroke-width: .9; stroke-linecap: round; stroke-dasharray: 46 260; }
@keyframes orbSpin { to { transform: rotate(360deg); } }
body.world-in .orb-load, .app.entered .orb-load { opacity: 0; animation-play-state: paused; }
#stage canvas.dragging { cursor: grabbing; }
.app.entered #stage canvas { touch-action: none; }
.app:not(.entered) #stage canvas { pointer-events: none; }
.stage-wrap.walking #stage canvas { cursor: default; }

header.top nav, aside, .panel-open, .placard, .controls { transition: opacity .5s ease, transform .5s cubic-bezier(.2,.8,.2,1), visibility .5s; }
.app:not(.entered) aside, .app:not(.entered) .panel-open, .app:not(.entered) .placard, .app:not(.entered) .controls { opacity: 0; visibility: hidden; transform: translateY(8px); }

header.top {
  position: fixed; z-index: 8; top: calc(16px + env(safe-area-inset-top, 0px));
  left: max(16px, env(safe-area-inset-left, 0px)); right: max(16px, env(safe-area-inset-right, 0px));
  display: flex; align-items: center; justify-content: space-between; gap: 12px; pointer-events: none;
}
header.top > * { pointer-events: auto; }
.brand { font-family: var(--serif); font-size: 26px; line-height: 1; color: var(--ink); text-decoration: none; padding: 8px 4px; transition: background .3s, box-shadow .3s, padding .3s; }
.app.entered .brand { padding: 9px 16px; border-radius: 999px; background: var(--gloss); box-shadow: var(--gloss-shadow); font-size: 22px; }
.dock { display: flex; align-items: flex-start; gap: 2px; padding: 9px 10px 8px; border-radius: 26px; background: var(--gloss); box-shadow: var(--gloss-shadow); }
.dock-i { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 64px; padding: 2px 4px; border-radius: 14px; color: var(--ink); text-decoration: none; }
.dock-ic { display: grid; place-items: center; width: 46px; height: 46px; border-radius: 12px; overflow: hidden; background: var(--panel); box-shadow: inset 0 0 0 1px rgba(20, 22, 26, 0.06), 0 2px 5px rgba(20, 22, 26, 0.10); transition: transform .25s cubic-bezier(.2, .8, .2, 1); }
.dock-ic img { display: block; width: 100%; height: 100%; object-fit: cover; }
.dock-glyph { background: none; box-shadow: none; overflow: visible; }
.dock-glyph svg { display: block; width: 48px; height: 40px; filter: drop-shadow(0 2px 3px rgba(60, 90, 180, 0.22)); }
.dock-i:hover .dock-ic, .dock-i:focus-visible .dock-ic { transform: translateY(-2px); }
.dock-l { padding: 1px 7px; border-radius: 6px; font: 600 14px/1.25 var(--body); white-space: nowrap; transition: background .2s, color .2s; }
.dock-i.on .dock-l { background: var(--dock-on); color: #FFFFFF; }

aside {
  position: fixed; z-index: 7; top: calc(78px + env(safe-area-inset-top, 0px)); bottom: 16px; left: max(16px, env(safe-area-inset-left, 0px)); width: 340px;
  overflow-y: auto; overscroll-behavior: contain; padding: 14px 10px 10px; border-radius: 16px; background: var(--panel); box-shadow: var(--float);
}
.app.panel-closed aside { transform: translateX(calc(-100% - 40px)); opacity: 0; visibility: hidden; }
.panel-x { position: absolute; top: 10px; right: 10px; width: 30px; height: 30px; display: grid; place-items: center; border: none; border-radius: 999px; background: transparent; color: var(--muted); cursor: pointer; z-index: 1; }
.panel-x:hover { background: var(--paper); color: var(--ink); }
.panel-open { display: none !important; position: fixed; z-index: 7; top: calc(78px + env(safe-area-inset-top, 0px)); left: max(16px, env(safe-area-inset-left, 0px)); height: 40px; padding: 0 18px; border: none; border-radius: 999px; background: var(--panel); color: var(--ink); font: 500 14px var(--body); cursor: pointer; box-shadow: var(--float); }
#list { display: flex; flex-direction: column; gap: 18px; }
.group { display: flex; flex-direction: column; gap: 2px; }
.group h2 { margin: 0 0 4px; padding: 0 10px; font-weight: 500; font-size: 12.5px; color: var(--muted); }
/* a row: name and street sign, the role, and a one-line drawer that slides open on hover (or when its place is open).
   The other rows step back instead of the active one getting a box. */
.item {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; align-items: center;
  width: 100%; padding: 10px; border: none; border-radius: 10px;
  background: transparent; color: var(--ink); font: inherit; text-align: left; cursor: pointer;
  transition: opacity .3s ease;
}
.item .name { font-family: var(--serif); font-size: 23px; line-height: 1.05; transition: transform .35s cubic-bezier(.2, .8, .2, 1); }
.item .meta { grid-column: 1 / -1; font-size: 13px; color: var(--muted); transition: transform .35s cubic-bezier(.2, .8, .2, 1); }
.item .line { grid-column: 1 / -1; display: grid; grid-template-rows: 0fr; opacity: 0; font-size: 13.5px; line-height: 1.5; transition: grid-template-rows .42s cubic-bezier(.2, .8, .2, 1), opacity .3s ease, margin-top .42s cubic-bezier(.2, .8, .2, 1), transform .35s cubic-bezier(.2, .8, .2, 1); }
.item .line > span { overflow: hidden; }
.chip { padding: 3px 7px 2px; border-radius: 3px; background: var(--accent); color: var(--accent-ink); font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.04em; white-space: nowrap; }
.item .chip { grid-row: 1; grid-column: 2; }
/* a case study that isn't written yet: a quiet tag, and the row doesn't open anything */
.item .soon { grid-row: 1; grid-column: 2; padding: 3px 8px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--line); color: var(--muted); font-size: 11.5px; white-space: nowrap; }
.item.is-soon { cursor: default; }
.item:hover .line, .item.on .line, .item:focus-visible .line { grid-template-rows: 1fr; opacity: 1; margin-top: 6px; }
.item:hover .name, .item.on .name, .item:hover .meta, .item.on .meta, .item:hover .line, .item.on .line { transform: translateX(4px); }
#list:hover .item:not(:hover), #list.has-on:not(:hover) .item:not(.on) { opacity: 0.42; }

.hint { position: absolute; top: calc(108px + env(safe-area-inset-top, 0px)); right: max(16px, env(safe-area-inset-right, 0px)); padding: 8px 12px; border-radius: 999px; background: var(--panel); color: var(--muted); font-size: 12.5px; box-shadow: var(--float); pointer-events: none; transition: opacity .6s; }
.hint.gone { opacity: 0; }
.placard {
  position: absolute; left: 372px; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); display: flex; align-items: center; gap: 10px; padding: 8px 16px 8px 8px;
  border: none; border-radius: 999px; background: var(--gloss); color: var(--ink); text-align: left; cursor: pointer; box-shadow: var(--gloss-shadow);
}
.app.panel-closed .placard { left: max(16px, env(safe-area-inset-left, 0px)); }
.compass { display: grid; place-items: center; width: 34px; height: 34px; }
#dial { transition: transform .12s linear; }
.pl-text { display: flex; flex-direction: column; gap: 0; }
.pl-text b { font-family: var(--serif); font-weight: 400; font-size: 18px; line-height: 1.1; }
.pl-text span { font-family: var(--mono); font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.controls { position: absolute; right: max(16px, env(safe-area-inset-right, 0px)); bottom: calc(16px + env(safe-area-inset-bottom, 0px)); display: flex; align-items: flex-end; gap: 8px; }
.ctl { width: 42px; height: 42px; display: grid; place-items: center; border: none; border-radius: 999px; background: var(--gloss); color: var(--ink); cursor: pointer; box-shadow: var(--gloss-shadow); }
/* sound is for walking the streets, so its button only shows in walk mode */
.app:not(.walking) #soundBtn { display: none; }
.ctl .wave { display: none; } .ctl[aria-pressed="true"] .wave { display: inline; } .ctl[aria-pressed="true"] .mute { display: none; }
.zoom { display: flex; flex-direction: column; border-radius: 999px; overflow: hidden; background: var(--gloss); box-shadow: var(--gloss-shadow); }
.zoom button { width: 42px; height: 40px; border: none; background: none; color: var(--ink); font: 400 20px/1 var(--body); cursor: pointer; }
.zoom button + button { border-top: 1px solid var(--line); }
.zoom button:hover { background: var(--paper); }
/* Walk: a little giraffe. On hover it walks and the label rolls over to italic serif. */
.walkbtn { height: 42px; padding: 0 18px 0 10px; display: flex; align-items: center; gap: 6px; border: none; border-radius: 999px; background: var(--ink); color: var(--paper); font: 500 14px var(--body); cursor: pointer; box-shadow: var(--float); }
.walkbtn[aria-pressed="true"] { background: var(--cab); color: #17171A; }
.giraffe { display: block; overflow: visible; }
.giraffe .lg { transform-box: fill-box; transform-origin: 50% 0; }
.walkbtn:hover .giraffe, .walkbtn:focus-visible .giraffe, .walkbtn[aria-pressed="true"] .giraffe { animation: bob .5s ease-in-out infinite; }
.walkbtn:hover .lg1, .walkbtn:hover .lg4, .walkbtn:focus-visible .lg1, .walkbtn:focus-visible .lg4, .walkbtn[aria-pressed="true"] .lg1, .walkbtn[aria-pressed="true"] .lg4 { animation: stride .5s ease-in-out infinite; }
.walkbtn:hover .lg2, .walkbtn:hover .lg3, .walkbtn:focus-visible .lg2, .walkbtn:focus-visible .lg3, .walkbtn[aria-pressed="true"] .lg2, .walkbtn[aria-pressed="true"] .lg3 { animation: stride .5s ease-in-out -.25s infinite; }
@keyframes stride { 0%, 100% { transform: rotate(16deg); } 50% { transform: rotate(-16deg); } }
@keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.2px); } }
.wl { display: grid; height: 22px; overflow: hidden; line-height: 22px; }
.wl > span { grid-area: 1 / 1; display: block; white-space: nowrap; transition: transform .45s cubic-bezier(.6,0,.2,1), opacity .45s; }
.wl-b { padding-right: 2px; font-family: var(--serif); font-style: italic; font-weight: 400; font-size: 19px; letter-spacing: 0.01em; transform: translateY(105%); opacity: 0; }
.walkbtn:hover .wl-a, .walkbtn:focus-visible .wl-a { transform: translateY(-105%); opacity: 0; }
.walkbtn:hover .wl-b, .walkbtn:focus-visible .wl-b { transform: none; opacity: 1; }
.pin { position: absolute; left: 0; top: 0; transform: translate(-50%, -100%); pointer-events: none; display: flex; flex-direction: column; align-items: center; }
.pin b { padding: 6px 12px; border-radius: 999px; background: var(--panel); color: var(--ink); font-family: var(--serif); font-weight: 400; font-size: 18px; white-space: nowrap; box-shadow: var(--float); }
.pin i { width: 1.5px; height: 20px; background: var(--ink); opacity: .6; }
.me { position: absolute; left: 0; top: 0; transform: translate(-50%, -100%); pointer-events: none; }
.honk, .say { position: absolute; left: 0; top: 0; transform: translate(-50%, -100%) rotate(-3deg); pointer-events: none; padding: 5px 11px; border-radius: 999px; background: var(--cab); color: #17171A; font: 600 14px var(--body); white-space: nowrap; box-shadow: var(--float); }
.say { background: var(--panel); color: var(--ink); transform: translate(-50%, -100%) rotate(2deg); }
/* townspeople's small talk, and the yellow line they shout when you bump into them */
.say.chatter { padding: 4px 10px; font-size: 13px; animation: pop .18s ease-out both; }
.say.chatter.bump { background: var(--cab); color: #17171A; transform: translate(-50%, -100%) rotate(-3deg); }
@keyframes pop { from { opacity: 0; } }
.prompts { position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%); display: flex; gap: 8px; }
.enter { display: flex; align-items: center; gap: 10px; min-height: 46px; padding: 0 8px 0 20px; border: none; border-radius: 999px; background: var(--ink); color: var(--paper); font: 400 15px var(--body); cursor: pointer; box-shadow: var(--float); white-space: nowrap; }
.enter b { font-family: var(--serif); font-weight: 400; font-size: 19px; }
.enter kbd { padding: 5px 10px; border-radius: 999px; background: var(--cab); color: #17171A; font: 500 12px var(--mono); }
.talkbtn { background: var(--panel); color: var(--ink); }
.talkbtn kbd { background: var(--ink); color: var(--paper); }
/* touch: "Talk to …" floats over the person's head as a small speech chip */
.enter.chip { position: absolute; left: 0; top: 0; z-index: 4; min-height: 36px; padding: 0 14px; gap: 6px; font-size: 13px; transform: translate(-50%, calc(-100% - 8px)); animation: rise .25s cubic-bezier(.2, .7, .2, 1) both; }
.enter.chip b { font-size: 17px; }
.enter.chip::after { content: ""; position: absolute; left: 50%; bottom: -4px; width: 10px; height: 10px; margin-left: -5px; border-radius: 2px; background: inherit; transform: rotate(45deg); }

/* talking to someone in town */
.talk { position: absolute; left: 50%; bottom: 84px; transform: translateX(-50%); width: min(460px, calc(100% - 32px)); padding: 16px 16px 12px; border-radius: 18px; background: var(--panel); color: var(--ink); box-shadow: var(--float); display: flex; flex-direction: column; gap: 10px; animation: rise .35s cubic-bezier(.2,.7,.2,1); }
.talk-head { display: flex; align-items: baseline; gap: 10px; padding-right: 30px; }
.talk-head b { font-family: var(--serif); font-weight: 400; font-size: 24px; line-height: 1; }
.talk-head span { font-family: var(--mono); font-size: 11px; color: var(--muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.talk-x { position: absolute; top: 10px; right: 10px; width: 28px; height: 28px; display: grid; place-items: center; border: none; border-radius: 999px; background: transparent; color: var(--muted); cursor: pointer; }
.talk-x:hover { background: var(--paper); color: var(--ink); }
.talk-text { margin: 0; font-size: 16px; line-height: 1.5; min-height: 1.5em; }
.talk-text.wait { color: var(--muted); }
.talk-text.wait::after { content: "…"; display: inline-block; animation: dots 1s steps(3) infinite; overflow: hidden; vertical-align: bottom; width: 1.2em; }
@keyframes dots { from { width: 0; } to { width: 1.2em; } }
.talk-sugg { display: flex; flex-wrap: wrap; gap: 6px; }
.talk-sugg button { padding: 5px 11px; border: 1px solid var(--line); border-radius: 999px; background: transparent; color: var(--ink); font: 400 13px var(--body); cursor: pointer; }
.talk-sugg button:hover { background: var(--paper); }
.talk-form { display: flex; gap: 6px; }
.talk-form input { flex: 1; min-width: 0; height: 38px; padding: 0 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--paper); color: var(--ink); font: 400 14px var(--body); }
.talk-form input:focus { outline: 2px solid var(--accent); outline-offset: 0; }
.talk-form button { height: 38px; padding: 0 16px; border: none; border-radius: 999px; background: var(--ink); color: var(--paper); font: 500 13px var(--body); cursor: pointer; }
.joy { position: absolute; left: 18px; bottom: 18px; width: 112px; height: 112px; border-radius: 50%; background: rgba(23,23,26,0.22); touch-action: none; }
.joy span { position: absolute; left: 50%; top: 50%; width: 46px; height: 46px; margin: -23px 0 0 -23px; border-radius: 50%; background: var(--panel); box-shadow: var(--float); }
.nogl { position: absolute; inset: 0; display: grid; place-items: center; padding: 24px; text-align: center; color: var(--muted); }

/* ---------- case study card ----------
   The map stays live on the left; the story sits in a rounded card on the right,
   after meliketurgut.com: a big year range, The Premise / The Reality, then The Plot. */
.cs { position: fixed; z-index: 50; top: calc(16px + env(safe-area-inset-top, 0px)); bottom: calc(16px + env(safe-area-inset-bottom, 0px)); right: max(16px, env(safe-area-inset-right, 0px));
  width: min(640px, 47vw); border-radius: 24px; background: var(--panel); box-shadow: 0 24px 60px rgba(20,22,26,.18), 0 2px 6px rgba(20,22,26,.08); overflow: hidden;
  transform: translateX(calc(100% + 40px)); transition: transform .8s cubic-bezier(.7,0,.2,1); }
.cs.in { transform: none; }
.app.cs-open .controls, .app.cs-open header.top nav, .app.cs-open .hint, .app.cs-open .panel-open, .app.cs-open .prompts, .app.cs-open .talk { opacity: 0; visibility: hidden; }
.cs-bar { position: absolute; top: 0; left: 0; right: 0; z-index: 3; display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 14px 14px 18px; pointer-events: none; transition: background .3s; }
.cs.scrolled .cs-bar { background: linear-gradient(var(--panel) 62%, transparent); }
.cs-bar button { pointer-events: auto; min-height: 38px; padding: 0 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--panel); color: var(--ink); font: 500 13px var(--body); cursor: pointer; }
.cs-bar .cs-x { width: 38px; padding: 0; display: grid; place-items: center; }
.cs-bar button:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.cs-scroll { position: absolute; inset: 0; overflow-y: auto; overscroll-behavior: contain; }
.cc { padding: 26px clamp(22px, 3.2vw, 44px) 0; display: flex; flex-direction: column; gap: 30px; }
.cc-eyebrow { display: flex; align-items: center; min-height: 38px; padding-right: 150px; font-size: 13px; color: var(--muted); }
.cc-years { margin: 0; text-wrap: balance; font-family: var(--serif); font-weight: 400; font-size: clamp(76px, 9vw, 132px); line-height: 0.86; letter-spacing: -0.03em; font-variant-numeric: lining-nums; }
.cc-years.ph-y { color: var(--muted); font-size: clamp(44px, 5vw, 64px); }
.cc-name { display: flex; flex-direction: column; gap: 4px; margin-top: -10px; }
.cc-head { display: flex; flex-direction: column; gap: 10px; }
.cc-title { margin: 0; font-family: var(--serif); font-weight: 400; font-size: clamp(56px, 6.2vw, 92px); line-height: 0.95; letter-spacing: -0.025em; text-wrap: balance; }
.cc-meta { margin: 0; font-size: 15px; color: var(--muted); }
.cc-name h1 { margin: 0; text-wrap: balance; font-family: var(--serif); font-weight: 400; font-style: italic; font-size: clamp(34px, 3.4vw, 46px); line-height: 1; }
.cc-name p { margin: 0; color: var(--muted); }
.cc-pr { margin: 0; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 22px; padding-block: 22px; border-block: 1px solid var(--line); }
.cc-pr dt, .cc-label { font-size: 13px; font-weight: 500; color: var(--muted); margin-bottom: 8px; }
.cc-pr dd { margin: 0; font-family: var(--serif); font-size: 22px; line-height: 1.2; text-wrap: pretty; }
.cc-pr ul { margin: 10px 0 0; padding: 0; list-style: none; display: grid; gap: 6px; font-family: var(--body); font-size: 13.5px; line-height: 1.45; color: var(--muted); }
.cc-plot p { margin: 0 0 14px; font-size: 16.5px; line-height: 1.65; max-width: 62ch; }
.cc-plot ul { margin: 0 0 14px; padding-left: 18px; display: grid; gap: 6px; font-size: 15.5px; line-height: 1.55; }
.cc-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.cc-stats div { display: flex; flex-direction: column; gap: 4px; }
.cc-stats b { font-family: var(--serif); font-weight: 400; font-size: 44px; line-height: 1; }
.cc-stats span { font-size: 13px; color: var(--muted); }
.browser { margin: 0; border-radius: 14px; overflow: hidden; background: var(--paper); box-shadow: 0 0 0 1px var(--line), 0 18px 40px rgba(20,22,26,.12); }
.browser-bar { display: flex; align-items: center; gap: 6px; height: 34px; padding: 0 12px; background: var(--paper); border-bottom: 1px solid var(--line); }
.browser-bar span { margin: 0 auto; padding: 3px 14px; border-radius: 999px; background: var(--panel); font-family: var(--mono); font-size: 11px; color: var(--muted); max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.browser img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: cover; object-position: top; background: var(--stage); }
.browser figcaption, .cc-shots figcaption { padding: 8px 12px; font-family: var(--mono); font-size: 11px; color: var(--muted); background: var(--panel); border-top: 1px solid var(--line); }
.cc-shots { display: grid; gap: 14px; }
.cc-shots figure { margin: 0; border-radius: 14px; overflow: hidden; box-shadow: 0 0 0 1px var(--line); background: var(--paper); }
.cc-shots img, .cc-shots video { display: block; width: 100%; height: auto; }
.cc-photo { margin: 0; position: relative; aspect-ratio: 4 / 3; border-radius: 14px; overflow: hidden; background: var(--paper); }
.cc-photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cc-photo .ph { display: none; position: absolute; inset: 0; }
.cc-photo.empty img { display: none; }
.cc-photo.empty .ph { display: grid; }
.cc-links { display: flex; flex-wrap: wrap; gap: 8px; }
.cc-links a { display: inline-flex; align-items: center; min-height: 38px; padding: 0 16px; border-radius: 999px; background: var(--paper); color: var(--ink); text-decoration: none; font-weight: 500; font-size: 14px; box-shadow: inset 0 0 0 1px var(--line); }
.cc-links a:first-child { background: var(--ink); color: var(--paper); box-shadow: none; }
.cc-phones { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.cc-phones img { display: block; width: 100%; height: auto; border-radius: 12px; box-shadow: 0 0 0 1px var(--line); transition: transform .25s cubic-bezier(.2, .8, .2, 1); }
.cc-phones:hover img { transform: translateY(-2px); }
.cc-hero { margin: 0; border-radius: 16px; overflow: hidden; background: var(--stage); }
.cc-hero img { display: block; width: 100%; height: auto; }
.cc-portrait { margin: 0; width: 100%; aspect-ratio: 3 / 2; border-radius: 16px; overflow: hidden; background: var(--stage); }
.cc-portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gal { display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; }
.gal figure { margin: 0; display: flex; flex-direction: column; gap: 6px; }
.gal img, .gal .ph { width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; border-radius: 14px; display: block; }
.gal figcaption { font-size: 13px; color: var(--muted); }
.ph { max-width: 100%; border-radius: 14px; background: var(--paper); border: 1px dashed var(--line); display: grid; place-items: center; color: var(--muted); font-family: var(--mono); font-size: 11px; text-align: center; padding: 14px; }
.cs-next { display: block; margin-top: 18px; padding: 30px clamp(22px, 3.2vw, 44px) 34px; background: var(--ink); color: var(--paper); text-decoration: none; }
.cs-next small { display: block; font-size: 13px; opacity: 0.7; margin-bottom: 6px; }
.cs-next b { display: block; font-family: var(--serif); font-weight: 400; font-size: clamp(40px, 4.4vw, 60px); line-height: 0.95; transition: color .2s; }
.cs-next em { display: block; margin-top: 8px; font-style: normal; font-size: 14px; opacity: 0.7; }
.cs-next:hover b { color: var(--cab); }
/* .reveal is kept as a hook, but sections no longer fade up one by one */

/* ---------- phones ----------
   The same intro as desktop: the name, then the town in an orb under it; scroll, or tap the orb, and it opens
   into a full-screen map. The work list is a bottom sheet over the map (peek, or pulled up), a case card is a
   taller sheet, and walk mode tucks the list away and shows a joystick. */
@media (max-width: 760px) {
  /* the orb sits in svh (the screen with the browser bars showing), which doesn't change as they slide away mid-scroll */
  :root { --orb-r: min(31vw, 15vh); --orb-cy: calc(88svh - var(--orb-r)); --gut: max(18px, env(safe-area-inset-left, 0px)); --sheet-peek: 38svh; --sheet-full: 82svh; }
  .hero { --u: 1.75vw; }
  /* the 3D canvas is as tall as the full screen, so it never resizes (and flashes) when the browser bars move */
  #stage { inset: 0 0 auto 0; height: 100lvh; }
  .hero { bottom: auto; height: 100svh; }
  .hero-inner { top: calc(env(safe-area-inset-top, 0px) + 92px); width: calc(100vw - 2 * var(--gut) - 12px); gap: 10px; }
  .hi-greet, .hi-now, .hi-line { font-size: 15px; }
  .hi-line br { display: none; }
  .gcloud { --x: 6; --y: 3.5; --w: 21; }
  .gsay { left: auto; right: calc(2 * var(--u) - var(--nudge, 0px)); white-space: normal; width: max-content; max-width: calc(100vw - 2 * var(--gut)); }
  .orb-cue { display: block; }
  /* the toy clouds frame the orb instead of crossing it: the orb sits on the long one (!important beats the inline desktop positions) */
  .cl:nth-child(1) { --x: -52 !important; --y: -40 !important; --w: 24 !important; } .cl:nth-child(2) { --x: 20 !important; --y: -17 !important; --w: 8 !important; } .cl:nth-child(3) { --x: -25 !important; --y: -6 !important; --w: 10 !important; }
  .cl:nth-child(4) { --x: -31 !important; --y: 22 !important; --w: 9 !important; } .cl:nth-child(5) { --x: -38 !important; --y: 41 !important; --w: 42 !important; } .cl:nth-child(6) { --x: 2 !important; --y: 39 !important; --w: 24 !important; }
  .orb-shadow { opacity: calc(0.8 - var(--p) * 3); }
  header.top { top: calc(12px + env(safe-area-inset-top, 0px)); left: var(--gut); right: var(--gut); gap: 8px; }
  .app.entered .brand { padding: 9px 14px; font-size: 20px; }
  .dock { padding: 7px 6px 6px; border-radius: 20px; gap: 0; }
  .dock-i { min-width: 54px; gap: 4px; }
  .dock-ic { width: 36px; height: 36px; border-radius: 10px; }
  .dock-glyph svg { width: 38px; height: 32px; }
  .dock-l { font-size: 12px; padding: 1px 5px; }
  .panel-x, .panel-open { display: none !important; }
  .app.cs-open header.top nav { opacity: 1; visibility: visible; }
  /* the work list: a bottom sheet with a handle */
  aside, .app.panel-closed aside {
    top: auto; left: 0; right: 0; bottom: 0; width: auto; height: var(--sheet-peek); padding: 0;
    display: flex; flex-direction: column; overflow: hidden; border-radius: 24px 24px 0 0;
    transform: none; opacity: 1; visibility: visible; box-shadow: 0 -10px 34px rgba(20, 22, 26, 0.14);
    transition: height .45s cubic-bezier(.2, .8, .2, 1), transform .45s cubic-bezier(.2, .8, .2, 1), visibility .45s;
  }
  .app:not(.entered) aside, .app.cs-open aside, .app.walking aside { transform: translateY(calc(100% + 24px)); visibility: hidden; }
  .sheet-grab { display: grid; place-items: center; flex: none; width: 100%; height: 34px; padding: 0; border: 0; background: none; cursor: grab; touch-action: none; }
  .sheet-grab i { width: 40px; height: 5px; border-radius: 3px; background: var(--line); }
  #list { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; gap: 26px; padding: 2px calc(var(--gut) - 6px) calc(36px + env(safe-area-inset-bottom, 0px)); }
  .group { gap: 4px; }
  .group h2 { padding: 0 6px; margin-bottom: 6px; }
  .item { padding: 12px 6px; }
  .item .meta { margin-top: 2px; }
  /* map chrome floats above the sheet */
  .controls { bottom: calc(var(--sheet-h, var(--sheet-peek)) + 14px); transition: bottom .45s cubic-bezier(.2, .8, .2, 1), opacity .3s, visibility .3s; }
  .app.sheet-dragging aside, .app.sheet-dragging .controls, .app.sheet-dragging .placard { transition: none; }
  .placard { transition: bottom .45s cubic-bezier(.2, .8, .2, 1), opacity .3s, visibility .3s; }
  .app.sheet-up .placard, .app.sheet-up .controls { opacity: 0; visibility: hidden; }
  .app.walking .controls { bottom: calc(22px + env(safe-area-inset-bottom, 0px)); }
  .placard, .app.panel-closed .placard { left: var(--gut); bottom: calc(var(--sheet-h, var(--sheet-peek)) + 14px); padding: 6px 14px 6px 6px; }
  .app.cs-open .placard { opacity: 0; visibility: hidden; }
  .pl-text b { display: none; }
  .stage-wrap.walking .placard { display: none; }
  /* the map controls stack at the right: zoom above Walk */
  .controls { right: var(--gut); gap: 10px; flex-direction: column; align-items: flex-end; }
  .ctl, .zoom button, .walkbtn { height: 44px; } .ctl, .zoom button { width: 44px; }
  .hint { top: calc(88px + env(safe-area-inset-top, 0px)); left: var(--gut); right: var(--gut); text-align: center; }
  /* walk mode: a joystick bottom-left, prompts and conversations above it */
  .joy { left: var(--gut); bottom: calc(22px + env(safe-area-inset-bottom, 0px)); width: 128px; height: 128px; background: rgba(23, 23, 26, 0.18); box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.8); }
  .joy::before { content: ""; position: absolute; inset: 22%; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5); }
  .joy span { width: 56px; height: 56px; margin: -28px 0 0 -28px; transition: transform .12s ease-out; }
  .joy.held span { transition: none; }
  .prompts { left: var(--gut); right: var(--gut); bottom: calc(240px + env(safe-area-inset-bottom, 0px)); transform: none; flex-wrap: wrap; justify-content: center; }
  .enter { min-width: 0; max-width: 100%; }
  .enter b { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .talk { left: var(--gut); right: var(--gut); width: auto; transform: none; bottom: calc(236px + env(safe-area-inset-bottom, 0px)); }
  .talk.typing { bottom: auto; top: calc(66px + env(safe-area-inset-top, 0px)); }
  .app.talking .prompts, .app.talking .hint { display: none; }
  .talk-form input { font-size: 16px; height: 44px; }
  .talk-form button { height: 44px; }
  /* case cards: a taller sheet, the place shows in the strip above it */
  /* the card is a full-height sheet that rests with its top three quarters showing; dragging it up (main.js cardDrag)
     slides it to the top, and only then does the text scroll. Moving the sheet with a transform, not its height,
     keeps the text from jumping under your finger */
  .cs { top: auto; left: 0; right: 0; bottom: 0; width: auto; height: calc(100svh - 8px - env(safe-area-inset-top, 0px)); border-radius: 24px 24px 0 0; transform: translateY(105%); transition: transform .5s cubic-bezier(.2, .8, .2, 1); }
  .cs.in { transform: translateY(calc(26svh - 8px - env(safe-area-inset-top, 0px))); }
  .cs.in.tall { transform: none; }
  .cs.dragging { transition: none; }
  header.top { will-change: transform; } /* main.js pushHeader() slides it up as the card's edge reaches it */
  .cs-scroll { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
  .cs-bar { padding: 14px var(--gut) 18px; }
  .cs-bar button { min-height: 44px; } .cs-bar .cs-x { width: 44px; }
  .cc { padding: 30px var(--gut) 0; gap: 34px; }
  .cc-eyebrow { min-height: 44px; padding-right: 160px; }
  .cc-years { font-size: clamp(64px, 19vw, 100px); }
  .cc-title { font-size: clamp(46px, 13vw, 68px); }
  .cc-pr, .cc-stats { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .cc-plot p { font-size: 16px; line-height: 1.7; }
  .cc-links a { min-height: 44px; }
  .cs-next { margin-top: 24px; padding: 34px var(--gut) calc(44px + env(safe-area-inset-bottom, 0px)); }
}
/* once inside the map on a phone, the page behind stays put, so a swipe that runs off a card can't scroll back to the intro */
html.page-locked, html.page-locked body { overflow: hidden; overscroll-behavior: none; }
@media (prefers-reduced-motion: reduce) {
  .cs, .reveal, .item, .loader, .ld-bar i, .wl > span { transition: none; }
  .walkbtn .giraffe, .walkbtn .lg { animation: none !important; }
  #stage canvas { transition: none; } .orb-load { animation: none; }
  .reveal { transform: none; opacity: 1; }
  .cl svg, .gcloud-drift, .caret, .hi-greet, .hi-name, .hi-now, .hi-line, .gpop .giraffe, .gpop .lg, body:not(.loaded) .orb-shine { animation: none !important; }
}
