/* Urban Vault · landing v2 · part 1: shared pieces, the hero → descent → Nagpur map sequence, the shift.
   Loaded after the page's inline styles. Colours and fonts come from uv-brand.css tokens.
   Only transform and opacity ever animate. */
:root{--gut:clamp(16px,5vw,2.5rem)}

/* ---------- shared ---------- */
.band{padding:clamp(3.5rem,6vw,5.5rem) 0}
.uv-anchor{position:absolute;left:0;top:0;width:1px;height:1px;pointer-events:none}
/* (waiting = opacity .001, not 0: invisible, but already drawn, so a reveal's first frame has nothing new to paint) */
.js:not(.rm) [data-rv]{opacity:.001;transform:translate3d(0,20px,0);transition:opacity .8s var(--ease),transform .9s var(--out);transition-delay:var(--rd,0ms)}
.js:not(.rm) [data-rv].in{opacity:1;transform:none}
/* each revealed block keeps its own layer for good: promoting or dropping a layer mid-scroll repaints the page tiles under it, and that showed up as a long frame */
.js:not(.rm) [data-rv]{will-change:transform,opacity}
.band:not(.cta-band) .sec-eyebrow{margin-bottom:1.1rem}
.eyebrow{font-size:13px}
/* nav links: a full-height target, not just the word */
.nav-links a{display:inline-flex;align-items:center;min-height:48px}
.nav-links a.nav-admin-link{min-height:44px}

/* ---------- 1 · the signature: hero layer over the real map ---------- */
/* overflow-anchor: when a zone's rate panel opens and the stage grows, the browser must not shift the scroll to compensate */
.sig{position:relative;overflow-anchor:none}
.stage{position:relative;background:var(--paper)}
.sig-run{display:none}
.hero{padding:7.5rem 0 5rem}
.hero-terrain canvas{height:100%;-webkit-mask-image:none;mask-image:none}
/* WebGL on: the stage pins, the dark hero is a layer on top of the map and fades away (opacity only) */
.gl .stage{position:sticky;top:0;min-height:100vh;min-height:100lvh}
.gl .sig-run{display:block;height:170vh}
.gl .hero{position:absolute;left:0;right:0;top:0;height:100vh;height:100lvh;min-height:0;z-index:6;will-change:opacity}
/* faded out: it stays in place at opacity 0 (hiding it would drop its layers and stall the way back up) */
.gl .hero.gone{pointer-events:none}
.gl #heroInner.off{pointer-events:none}
.gl #heroInner{will-change:transform,opacity}

.hero-inner{grid-template-columns:minmax(0,1.3fr) minmax(0,.7fr);gap:2.5rem}
.hero-sub{max-width:34ch;margin-bottom:2rem}
/* the old stat strip, as one quiet line */
.hero-stats{display:flex;flex-wrap:wrap;gap:.3rem 1.6em;margin-top:1.7rem;max-width:none;font-family:var(--fm);font-size:13px;letter-spacing:.03em;line-height:1.55;color:var(--bone-2)}
.hero-stats span{white-space:nowrap}
.hero-stats span::before{content:"";display:inline-block;width:5px;height:5px;margin-right:.7em;background:var(--ochre);vertical-align:.12em;transform:rotate(45deg)}
.hero-stats b{font-weight:500;color:var(--ochre-hi)}

/* the sample portfolio: a small, secondary card */
.pcard{justify-self:end;width:100%;max-width:356px;padding:1.15rem 1.2rem 1rem;box-shadow:0 30px 60px -30px rgba(0,0,0,.7)}
.pcard-label,.pcard-live{font-size:11.5px}
.pcard-value{font-size:1.45rem}
.pcard-delta{font-size:13px;margin:.2rem 0 .5rem}
.pcard-delta .rng{font-size:11.5px}
.spark{height:44px;margin:.2rem 0 .6rem}
.hold{padding:.5rem 0}
.hold-name{font-size:14.5px}
.hold-loc{font-size:10.5px}
.hold-val{font-size:13px}
.hold-chg{font-size:11.5px}
.pcard-foot{margin-top:.6rem;padding-top:.6rem;font-size:10.5px}

/* altitude readout + scroll cue */
.hero-hud{bottom:1.6rem;z-index:3}
.hero-hud .k{font-size:12px}
.hero-hud b{font-size:26px}
.hero-hud .co{font-size:12px}
.hero-cue{display:none}
.gl .hero-cue{display:flex;position:absolute;left:50%;bottom:22px;transform:translateX(-50%);z-index:3;flex-direction:column;align-items:center;gap:9px;font-family:var(--fm);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--bone-2);transition:opacity .5s;pointer-events:none}
.hero-cue b{display:block;width:1px;height:28px;background:rgba(201,154,75,.3);overflow:hidden;position:relative}
.hero-cue b::after{content:"";position:absolute;left:0;top:0;width:1px;height:12px;background:var(--ochre-hi);animation:uvCue 2.2s var(--ease) infinite}
@keyframes uvCue{0%{transform:translateY(-14px)}100%{transform:translateY(30px)}}
.hero-cue.off{opacity:0}
.hero-cue.off b::after{animation:none}

/* the viewfinder: settles exactly on the map frame, so the cut from terrain to map keeps its framing */
.hero-reticle{display:none}
.gl .hero-reticle{display:block;position:absolute;left:0;top:0;width:10px;height:10px;z-index:2;opacity:0;border:1px solid rgba(224,186,114,.5);pointer-events:none;will-change:transform,opacity}
.hero-reticle i{position:absolute;width:20px;height:20px;border:2px solid var(--ochre-hi)}
.hero-reticle i:nth-child(1){left:-2px;top:-2px;border-right:0;border-bottom:0}
.hero-reticle i:nth-child(2){right:-2px;top:-2px;border-left:0;border-bottom:0}
.hero-reticle i:nth-child(3){left:-2px;bottom:-2px;border-right:0;border-top:0}
.hero-reticle i:nth-child(4){right:-2px;bottom:-2px;border-left:0;border-top:0}
.hero-reticle span{position:absolute;left:14px;top:12px;font-family:var(--fm);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ochre-hi);white-space:nowrap}

/* ---------- the map section, as the landing ground ---------- */
#map{padding:96px 0 clamp(2.5rem,5vw,4rem);scroll-margin-top:0}
.gl #map{min-height:100vh;min-height:100lvh}
#map>.wrap{display:grid;grid-template-columns:minmax(0,1fr)}
#map .map-head .sec-title{font-size:clamp(1.7rem,2.8vw,2.55rem);max-width:none}
#map .map-head .sec-intro{font-size:17px;line-height:1.6;margin-top:1rem}
#mapShell{display:contents}
#map .map-foot{margin-top:1.6rem}
@media(min-width:961px){
  #map>.wrap{grid-template-columns:minmax(300px,390px) minmax(0,1fr);grid-template-rows:auto 1fr;column-gap:clamp(1.6rem,3.2vw,3.2rem);align-items:start}
  #map .map-head{grid-column:1;grid-row:1}
  #mapShell>.map-frame{grid-column:2;grid-row:1/span 2;height:min(640px,calc(100vh - 150px));min-height:440px}
  #mapShell>.map-panel{grid-column:1;grid-row:2;margin-top:1.3rem}
  #map>.wrap>div[id$="MapShell"],#map .map-foot{grid-column:1/-1}
}
@media(max-width:960px){
  #map{padding-top:88px}
  #map>.wrap{display:flex;flex-direction:column}
  #map .map-head{display:contents}
  #mapEyebrow{order:1;margin-bottom:.7rem!important}
  #map .map-head .sec-title{order:2}
  #map .map-scope{order:3;font-size:12px;letter-spacing:.06em}
  #mapShell>.map-frame{order:4;margin-top:1rem;height:min(460px,calc(100vh - 330px));height:min(460px,calc(100lvh - 330px));min-height:300px}
  #mapShell>.map-panel{order:5;margin-top:1rem}
  #mapIntro{order:6}
  #map>.wrap>div[id$="MapShell"]{order:7}
  #map .map-foot{order:8}
}
#map .panel-empty{padding:1.2rem 0}
#map .panel-empty p{max-width:24ch}

/* the landing: until the descent arrives, the pins wait above the map and the map takes no input */
/* (waiting = opacity .001, not 0: invisible, but already drawn, so the landing's first frame has nothing new to paint) */
.gl #frame .uv-zone-mark::before,.gl #frame .uv-zone-mark::after{animation:none}
.gl .stage:not(.settled) #frame .uv-zone-mark{will-change:transform,opacity}
.gl .stage:not(.landed) #frame .uv-zone-mark{opacity:.001}
.gl .stage.landed #frame .uv-zone-mark{animation:uvPinDrop .95s cubic-bezier(.55,0,1,.45) backwards}
.gl .stage.landed #frame .leaflet-marker-icon:nth-child(3n+1) .uv-zone-mark{animation-delay:.14s}
.gl .stage.landed #frame .leaflet-marker-icon:nth-child(3n+2) .uv-zone-mark{animation-delay:.28s}
.gl .stage:not(.live) #frame{pointer-events:none}
.gl #map .map-head>*,.gl #mapShell>.map-panel{transition:opacity .7s var(--ease),transform .9s var(--out)}
.gl #map .map-head>*:nth-child(2){transition-delay:.08s}
.gl #map .map-head>*:nth-child(3){transition-delay:.14s}
.gl #map .map-head>*:nth-child(4),.gl #mapShell>.map-panel{transition-delay:.2s}
.gl .stage:not(.settled) #map .map-head>*,.gl .stage:not(.settled) #mapShell>.map-panel{will-change:transform,opacity}
.gl .stage:not(.landed) #map .map-head>*,.gl .stage:not(.landed) #mapShell>.map-panel{opacity:.001;transform:translate3d(0,16px,0)}

/* no WebGL / reduced motion: the hero is an ordinary dark block and the map follows it */
html:not(.gl) #map{padding-top:clamp(4rem,7vw,6rem)}

@media(max-width:960px){
  .hero-inner{grid-template-columns:1fr}
  .pcard{display:none}
}
@media(max-width:640px){
  .hero{padding:6.5rem 0 5.5rem}
  .hero-stats{display:none}
  .hero-hud{left:var(--gut);right:auto;justify-content:flex-start;text-align:left;bottom:1.2rem}
  .hero-hud b{font-size:22px;min-width:0}
  .hero-hud .co{font-size:11px}
  .gl .hero-cue{left:auto;right:var(--gut);transform:none;align-items:flex-end;font-size:11px;bottom:18px}
  .hero-reticle span{font-size:11px;left:10px;top:9px}
}

/* ---------- 2 · the shift: the usual way is struck through, the Urban Vault way settles beside it ---------- */
.shift-head{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);gap:1.2rem clamp(2rem,5vw,5rem);align-items:end}
.shift-head .sec-title{max-width:22ch}
.shift-head .sec-intro{margin-top:0;max-width:46ch}
.pairs-k{display:grid;grid-template-columns:1fr 1fr;gap:0 clamp(1.5rem,4vw,4rem);margin-top:clamp(2.2rem,4vw,3.2rem);padding-bottom:.8rem;font-family:var(--fm);font-size:13px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--text-3)}
.pairs-k span+span{color:var(--ochre-text)}
.pairs{list-style:none;border-top:1px solid var(--line-l2)}
.pair{display:grid;grid-template-columns:1fr 1fr;gap:.35rem clamp(1.5rem,4vw,4rem);align-items:baseline;padding:1.1rem 0;border-bottom:1px solid var(--line-l)}
.was{font-family:var(--fd);font-stretch:106%;font-weight:480;font-size:clamp(18px,1.5vw,20px);line-height:1.4;color:var(--text-3);text-decoration:line-through;text-decoration-color:var(--lat);text-decoration-thickness:2px}
.now{font-family:var(--fd);font-stretch:108%;font-weight:640;font-size:clamp(19px,1.7vw,23px);line-height:1.3;color:var(--text)}
/* with script: the strike is drawn word by word (transform only), so it also works when the line wraps */
.was.split{text-decoration:none}
.was .w{position:relative;display:inline-block}
.was .w::after{content:"";position:absolute;left:-.17em;right:-.17em;top:56%;height:2px;background:var(--lat);transform-origin:left center}
.js:not(.rm) .was .w::after{transform:scaleX(0);transition:transform .32s var(--ease);transition-delay:calc(var(--rd,0ms) + .3s + var(--w) * 50ms)}
.js:not(.rm) .pair.in .was .w::after{transform:scaleX(1)}
.js:not(.rm) .pair .now{opacity:.001;transform:translate3d(0,10px,0);transition:opacity .7s var(--ease),transform .8s var(--out);transition-delay:calc(var(--rd,0ms) + .7s)}
.js:not(.rm) .pair.in .now{opacity:1;transform:none}
@media(max-width:760px){
  .shift-head{grid-template-columns:1fr}
  .pairs-k{display:flex;flex-wrap:wrap;gap:.2rem .6rem;font-size:12.5px;letter-spacing:.08em}
  .pairs-k span+span::before{content:"→";margin-right:.6rem;color:var(--text-3)}
  .pair{grid-template-columns:1fr;padding:1rem 0}
}

/* desktop: once a zone is selected, drop the intro text so the rate panel and its "Explore this area" button sit above the fold */
@media(min-width:961px){
  #map>.wrap:has(#panelLive.show) #mapIntro,#map>.wrap:has(#panelLive.show) .map-head .sec-title,#map>.wrap:has(#panelLive.show) .map-head .map-scope{display:none}
  #map .map-panel .panel-note{font-size:16px;line-height:1.5}
  #map .map-panel .panel-cta{margin-top:1rem}
  #map>.wrap:has(#panelLive.show) .map-panel{margin-top:.8rem}
  #map .map-panel .panel-zone-name{margin-bottom:.7rem}
}
