/* ============================================================
   מפנה — page layout. Tokens live in tokens.css (three layers).
   Hebrew / RTL. No text-transform anywhere: it is meaningless in
   Hebrew and mangles our unit strings (kWh -> KWH).
   ============================================================ */

/* ---------- base ---------- */
/* overflow-x on BOTH. With it only on body, the html element keeps its own
   scroll width and the document still scrolls sideways while every child
   measures inside the viewport — which is exactly what the probe showed:
   zero offenders, and still 24px of overflow. */
html{scroll-behavior:smooth;overflow-x:hidden;max-width:100%}
body{background:var(--ground);color:var(--ink);font-family:var(--face);
  font-variant-numeric:tabular-nums lining-nums;   /* every digit. see below. */
  overflow-x:hidden}

/* ⚠️ tabular is not cosmetic. Adapter's default figures are proportional, and
   measured across this site's real values the spread is 65.5px per 100px of
   type — at the 94px display numeral the value crawls ~62px sideways while the
   visitor drags the roof. tnum flattens it to 0.0px. */
.numeral,.numeral-sm,.facts__n,.angles td,.angles th,.layers__t{
  font-variant-numeric:tabular-nums lining-nums}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* ⚠️ `right:-9999px` is the LTR idiom and it is a bug in RTL: `right` is
   PHYSICAL, so a negative value pushes the element off the RIGHT edge and the
   document grows sideways. Measured: 24px of horizontal overflow, which the
   gate flags because in RTL a sideways document clips the first character of
   every line. Move it off the block axis instead — no axis confusion, and it
   still reaches the keyboard. */
.skip{position:absolute;inset-inline-start:0;inset-block-start:-100px;z-index:100;
  background:var(--ink);color:var(--ground);padding:.75rem 1.25rem}
.skip:focus{inset-block-start:0}
.nowrap{white-space:nowrap}

:where(a,button,input):focus-visible{outline:2px solid var(--focus);outline-offset:3px}

/* ---------- type ---------- */
.display{font:var(--w-bold) var(--t-5)/1.1 var(--face);
  letter-spacing:var(--track-display);max-width:18ch}
.display--quiet{font-weight:var(--w-light)}
.numeral{font:var(--w-bold) var(--t-6)/1 var(--face);letter-spacing:var(--track-display);
  color:var(--numeral,var(--ink));display:block;
  transition:none}                 /* tracks the drag 1:1. never eased. */
.numeral-sm{font:var(--w-bold) var(--t-3)/1 var(--face)}
.eyebrow{font:var(--w-book) var(--t-1)/1 var(--face);color:var(--ink-muted);
  letter-spacing:.08em;margin-bottom:1.1rem}
.lede{font:var(--w-book) var(--t-2)/1.58 var(--face);color:var(--ink-muted);
  letter-spacing:var(--track-body);
  max-width:44ch;margin-top:1.1rem}
.kicker{font:var(--w-light) var(--t-1)/1.4 var(--face);color:var(--ink-muted)}
.cap{font:var(--w-bold) var(--t-2)/1.44 var(--face);margin-top:1rem;max-width:34ch}
.note{font:var(--w-light) var(--t-1)/1.55 var(--face);color:var(--ink-muted);
  max-width:40ch;margin-top:1.4rem}
.note b{font-weight:var(--w-bold);color:var(--ink)}
.src{font:var(--w-book) var(--t-1)/1.6 var(--face);color:var(--ink-muted);margin-top:1.5rem}
.src a{text-decoration:underline;text-underline-offset:.25em}

/* ---------- chrome ---------- */
.chrome{position:fixed;inset-block-start:0;inset-inline:0;z-index:50;
  display:flex;align-items:center;gap:2rem;padding:1.1rem var(--pad);
  color:var(--ink);mix-blend-mode:normal;pointer-events:none}
.chrome > *{pointer-events:auto}
.chrome__spacer{flex:1}
.wordmark{font:var(--w-bold) var(--t-3)/1 var(--face);letter-spacing:-.01em}
.nav{display:flex;gap:1.4rem;font:var(--w-book) var(--t-1)/1 var(--face)}
.nav a{color:var(--ink-muted)}
.nav a:hover,.nav a[aria-current]{color:var(--ink)}
.badge{font:var(--w-book) var(--t-1)/1 var(--face);color:var(--ink);
  border:var(--hair) solid currentColor;padding:.42rem .8rem;border-radius:var(--radius)}

/* the carried number — visible from act 2 to the printed table.
   This is the mechanism made legible; SCENE.md names its absence as the
   single risk most likely to sink the build. */
/* width:max-content — as a plain grid it stretched to the viewport and pushed
   the document 56px wide. A fixed element with no intrinsic width is a
   horizontal-overflow bug waiting for a long value. */
.carried{position:fixed;inset-block-start:4.6rem;inset-inline-end:var(--pad);z-index:49;
  display:grid;gap:.28rem;justify-items:end;text-align:end;width:max-content;
  color:var(--ink);opacity:0;transition:opacity var(--dur) var(--ease)}
.carried[data-on]{opacity:1}
.carried__label,.carried__angle{font:var(--w-book) var(--t-1)/1 var(--face);color:var(--ink-muted)}

/* ---------- acts ---------- */
/* ⚠️ [data-scene] only REDEFINES tokens — it paints nothing. Without an explicit
   background the film acts render on the paper ground and the whole tonal
   journey (L*3 -> L*96 at ~70% depth) silently disappears. */
/* ⚠️ The FILM acts must be TRANSPARENT or they hide the fixed stage behind them —
   an opaque background here is the difference between a 3D site and a canvas
   nobody ever sees. The document's own ground carries the film black, and only
   the flat third paints. */
body{background:var(--night-1)}
.act{background:transparent;color:var(--ink)}
/* ⭐ THE PAPER IS A VEIL, NOT A LID (19.8, user's decision).
   This was a solid fill, and that single declaration is why the roof world
   below it was invisible — which in turn is why main.js used to stop rendering
   altogether once the last act left the screen. The standing law is that after
   the entry film the WHOLE site keeps responding, so the paper now sits OVER
   the scene instead of replacing it: the sun goes on sweeping and the reading
   happens on a page that light is falling across.
   ⚠️ Legibility is not up for negotiation here. The veil is heavy and the scene
   behind it is blurred to a smooth field, so the ground under the type has
   almost no local variance. Without the blur, an 8% bleed of a roof carrying a
   dark array and a bright sky swings the local ground by tens of L* and the
   single contrast number stops describing anything.
   ⛔ And it is deliberately NOT a full-bleed canvas under the text. Covering the
   business third in canvas would take live3DSustainedPct to ~90 while making the
   page worse to read — gate.sh's own note records that a still photograph in a
   canvas scores 99.7 on that metric. Alive is the goal; filling the frame is
   the metric-faking this department keeps a ledger for. */
.flat,.foot{background:var(--ground);color:var(--ink)}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .flat,.foot{
    background:color-mix(in srgb, var(--ground) 92%, transparent);
    -webkit-backdrop-filter:blur(30px) saturate(1.06);
    backdrop-filter:blur(30px) saturate(1.06)}
}
.silence{background:var(--night-1)}
.act{position:relative;min-height:100svh;display:grid;align-content:center;
  padding:0 var(--pad);isolation:isolate}
/* ONE fixed stage behind the whole document. z-index -1 keeps it under the
   content without needing a stacking context on every act. */
/* ⚠️ NOT 100vw. `100vw` includes the scrollbar, so on any page tall enough to
   scroll — i.e. this one — the canvas is exactly one scrollbar wider than the
   viewport and the document scrolls sideways. Measured: 24px, which is this
   machine's scrollbar to the pixel. `inset:0` on a fixed element already means
   "the whole viewport", correctly. */
#stage{position:fixed;inset:0;z-index:-1;display:block;pointer-events:none}

/* ⭐ THE FILM PLATE — the opening is a photograph.
   Sits at the same layer as the canvas but after it in the DOM, so it covers the
   geometry while the film is running and uncovers it as the visitor descends.
   `object-fit:cover` because these are 16:9 and the viewport is not. */
#plate{position:fixed;inset:0;z-index:-1;pointer-events:none;background:var(--night-1)}
#plate picture{position:absolute;inset:0;display:block}
#plate .plate__f{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;will-change:opacity}
#plate .plate__f[data-i="0"]{opacity:1}

/* ⭐ the cinematic layer — grain + vignette, over the stage, under the type.
   A clean render reads as CG; every photograph has noise and falloff. Cheap:
   one repeating SVG and one gradient, no per-frame cost.
   ⚠️ Grain alone is NOT life — REJECTIONS measured a grain layer on a dead page
   at 0.264% motion. This is finish, not the pulse. */
body::after{content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(130% 100% at 50% 42%, transparent 46%,
      color-mix(in srgb, var(--night-1) 55%, transparent) 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.055'/%3E%3C/svg%3E");
  mix-blend-mode:normal}
/* ⚠️ `.carried` is NOT in this list. It is `position:fixed`, and putting it in a
   blanket `position:relative` rule silently un-fixed it — it dropped into flow
   and pushed the document 56px sideways. It already has its own z-index. */
main,.chrome,.foot,.sound{position:relative;z-index:1}
/* ⭐ the readability layer. Measured on the live page: the headline sat on a
   lit panel and the contrast collapsed. A scrim behind the copy — not a box, a
   soft directional falloff — keeps the type readable without putting a panel
   over the 3D. Film acts only; the flat third has its own ground. */
.act[data-scene="film"] .act__copy{position:relative;isolation:isolate}
.act[data-scene="film"] .act__copy::before{
  /* ⚠️ THE FALLOFF IS SIZED IN rem, NOT IN PERCENT — and that is the whole fix.
     Twice in a row this scrim went wrong for the same reason: its gradient was a
     PERCENTAGE of its own box. At `inset:-6rem` the tail was cut off and left a
     hard vertical seam down the photograph; widening the box to -100vw then
     scaled the gradient with it and blacked out the entire frame. A falloff
     measured against the thing it is drawn on cannot be tuned — it moves every
     time the box does. Absolute lengths: the ellipse completes at 42rem from the
     copy's edge, and the box reaches 46rem, so the tail always lands inside it.
     ⭐ And this is calibrated against a PHOTOGRAPH now, not the old dark panel. */
  content:"";position:absolute;inset:-16rem -3rem -16rem -46rem;z-index:-1;
  background:radial-gradient(42rem 30rem at 100% 50%,
    color-mix(in srgb, var(--night-1) 93%, transparent) 0%,
    color-mix(in srgb, var(--night-1) 78%, transparent) 46%,
    color-mix(in srgb, var(--night-1) 32%, transparent) 74%,
    transparent 100%);
  pointer-events:none}
.act__copy{max-width:min(56ch,100%)}
.act__copy--left{justify-self:start;text-align:start}
.act__copy--right{justify-self:end;text-align:end}
/* ⭐ PINS. SCENE.md: act 2 gets 2.0vh, acts 4 and 6 get 1.5 — and nothing is
   ever pinned longer than 2.0, because past about two wheel gestures a beat
   stops being a moment and becomes a wait. Never pin a reading task.
   Mechanism: the act becomes TALL and its copy is sticky inside it. No library,
   no scroll hijack, and the page stays measurable — REJECTIONS records twice
   that scrollTo is a no-op on a smooth-scroll page and our own gates then
   measured a page they had never navigated. */
.act--pin{align-content:start;padding-block-start:0}   /* height set in main.js — see the note there */
/* ⭐ COMPOSITION. Centred copy over a centred panel is a collision, and it read
   exactly like one: the headline sat on the array and the PVGIS table crossed
   the separated layers. The pinned acts put their type in a COLUMN on one side
   and leave the other side to the 3D — which is also what the chosen style
   frame (C · הצלילה) does. */
.act--pin > .act__copy,
.act--pin > .angles,
.act--pin > .src{position:sticky;justify-self:end;
  width:min(46ch,50%);inset-block-start:22svh}
.act--pin > .angles{inset-block-start:calc(22svh + 13rem);width:min(46ch,50%)}
/* the source line is NOT sticky. Pinning it at a fixed offset landed it on top
   of the table — three sticky siblings at hand-picked offsets will always
   collide the moment one of them changes length. It belongs at the end of the
   beat, in flow. */
.act--pin > .src{position:static;justify-self:end;width:min(46ch,50%);
  align-self:end;margin-block-end:8svh}

/* the readability layer has to cover the TABLE too — it was only behind the
   copy, so the numbers kept sitting bare on the panel. */
.act[data-scene="film"] .angles{position:relative;isolation:isolate}
.act[data-scene="film"] .angles::before{
  content:"";position:absolute;inset:-1rem -1.5rem;z-index:-1;
  background:color-mix(in srgb, var(--night-1) 82%, transparent);
  pointer-events:none}

@media (max-width:768px){
  /* on a phone a 2.0 pin is most of a minute of thumb. Halve them. */
  .act--pin > .act__copy{inset-block-start:12svh}
  .act--pin > .angles,.act--pin > .src{position:static}
}

.cue{position:absolute;inset-block-end:2.4vh;inset-inline-start:50%;translate:50% 0;
  font:var(--w-book) var(--t-1)/1 var(--face);color:var(--ink-muted)}

/* the four PVGIS angles */
.angles{margin-top:2rem;border-collapse:collapse;
  font:var(--w-book) var(--t-1)/1 var(--face)}
.angles th,.angles td{padding:.6rem 1.1rem;text-align:start;
  border-block-end:var(--hair) solid var(--rule-hair)}
.angles thead th{color:var(--ink-muted);font-weight:var(--w-book)}
.angles tbody th{color:var(--ink)}
.angles td{color:var(--ink-muted);text-align:end}
.angles tr.is-optimal td,.angles tr.is-optimal th{color:var(--ink);font-weight:var(--w-bold)}
.angles tr[data-active]{background:var(--ground-alt)}

.layers{margin-top:1.6rem;display:grid;gap:.55rem;list-style:none;
  font:var(--w-book) var(--t-1)/1.5 var(--face);color:var(--ink-muted)}
.layers__t{display:inline-block;min-width:5.5em;color:var(--ink);font-weight:var(--w-bold)}

/* ⬛ the paid silence. 0.3vh, no canvas, no text. Do not fill this. */
.silence{height:30svh;background:var(--night-1)}

/* ---------- the flat third. 3D stops here by decision. ---------- */
.flat{padding:12svh var(--pad);max-width:var(--maxw);margin-inline:auto}
/* ⛔ ROOF MOMENTS — BUILT, MEASURED, REMOVED (19.8). Do not rebuild them before
   the roof has an environment.
   Three full-screen `.roofbeat` sections were inserted between the reading blocks,
   at the user's choice, to make the 3D dominant below the fold. Measured result:
   live3DSustainedPct 26.9 -> 26.9 (92svh) -> 27.0 (124svh). **0.1 of a point**,
   because the median lands by AREA and the paper below the fold is 2,905px against
   2,244px of scene. Growing the beats moves the sample points with them.
   ⛔ The heights were NOT tuned to make the samples land on scene: that is tuning
   to the instrument, and the visitor would experience something other than what
   was measured.
   ⭐ And the screenshot said what the number could not: at full screen the roof is
   an OBJECT IN A VOID — flat panel, white cylinder, hard shadow, near-black ground,
   no roof and no sky. It survives as a passing beat in act 6; it cannot hold the
   frame for 124svh three times. The concept is right and the asset is not ready.
   Rebuild these once the roof has a sourced environment (roof HDRI + built horizon
   + a real roof surface) — never a studio HDRI, which is the RoomEnvironment
   failure under another name. */
.flat + .flat{border-block-start:var(--hair) solid var(--rule-hair)}

.ledger{margin-top:2.4rem;display:grid;gap:2.4rem;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start}
.ledger__inputs{display:grid;grid-template-columns:auto 1fr;gap:.6rem 1.6rem;
  font:var(--w-book) var(--t-1)/1 var(--face)}
.ledger__inputs dt{color:var(--ink-muted)}
.ledger__inputs dd{color:var(--ink);font-weight:var(--w-bold)}
.ledger__of{display:block;font:var(--w-book) var(--t-1)/1.4 var(--face);
  color:var(--ink-muted);margin-top:.6rem}

.facts{margin-top:2.4rem;display:grid;gap:2.4rem;list-style:none;
  grid-template-columns:repeat(auto-fit,minmax(15rem,1fr))}
.facts__n{display:block;font:var(--w-bold) var(--t-4)/1 var(--face);}
.facts__u{display:block;font:var(--w-book) var(--t-1)/1 var(--face);
  color:var(--ink-muted);margin-top:.4rem}
.facts p{margin-top:.9rem;font:var(--w-book) var(--t-1)/1.6 var(--face);color:var(--ink-muted)}
.facts b{color:var(--ink);font-weight:var(--w-bold)}

.refusals{margin-top:2.4rem;display:grid;gap:1.3rem;list-style:none;max-width:62ch;
  font:var(--w-book) var(--t-2)/1.58 var(--face);color:var(--ink-muted)}
.refusals b{color:var(--ink);font-weight:var(--w-bold)}

/* ---------- the ask. One field. ---------- */
.ask{margin-top:3rem;display:flex;flex-wrap:wrap;align-items:end;gap:.8rem}
.ask label{flex-basis:100%;font:var(--w-book) var(--t-1)/1 var(--face);color:var(--ink-muted)}
.ask input{flex:1 1 22rem;min-width:0;background:transparent;color:var(--ink);
  border:0;border-block-end:var(--hair) solid var(--border-field);
  padding:.7rem .2rem;font:var(--w-book) var(--t-2)/1.2 var(--face);border-radius:0}
.ask input::placeholder{color:var(--ink-muted);opacity:.7}
.ask button{background:var(--accent);color:var(--ink-on-accent);border:0;
  padding:.85rem 1.6rem;border-radius:var(--radius);cursor:pointer;
  font:var(--w-bold) var(--t-1)/1 var(--face);
  transition:background var(--dur) var(--ease)}
.ask button:hover{background:var(--accent-hover)}
.ask__note{flex-basis:100%;font:var(--w-book) var(--t-1)/1.5 var(--face);color:var(--ink-muted)}

/* ---------- footer. Starts at 91.6% depth, gets a full 1.0vh. ---------- */
.foot{padding:10svh var(--pad) 4svh;max-width:var(--maxw);margin-inline:auto;
  border-block-start:var(--hair) solid var(--rule-hair)}
.handoff{margin-top:3rem;font:var(--w-bold) var(--t-3)/1.3 var(--face)}
.handoff a{text-decoration:underline;text-underline-offset:.3em}
.foot__legal{margin-top:4rem;display:grid;gap:1.2rem;
  border-block-start:var(--hair) solid var(--rule-hair);padding-block-start:1.6rem}
.foot__demo{font:var(--w-book) var(--t-1)/1.6 var(--face);color:var(--ink-muted);max-width:70ch}
.foot__demo b{color:var(--ink)}
.foot__links{display:flex;flex-wrap:wrap;gap:1.4rem;
  font:var(--w-book) var(--t-1)/1 var(--face);color:var(--ink-muted)}
.foot__links a{text-decoration:underline;text-underline-offset:.25em}

.sound{position:fixed;inset-block-end:1.4rem;inset-inline-start:1.4rem;z-index:50;
  width:2.4rem;height:2.4rem;border-radius:999px;cursor:pointer;
  background:transparent;color:var(--ink);border:var(--hair) solid currentColor}

/* ---------- mobile. Verified at 375 as each section is built. ---------- */
@media (max-width:768px){
  .nav{display:none}
  .act{min-height:100svh;padding-block:14svh 10svh;align-content:start}
  .act__copy--left,.act__copy--right{justify-self:stretch;text-align:start}
  .carried{inset-block-start:auto;inset-block-end:1.2rem;inset-inline-start:var(--pad);
    inset-inline-end:auto;justify-items:start;text-align:start}
  .ledger{grid-template-columns:1fr;gap:1.6rem}
  .angles th,.angles td{padding:.5rem .6rem}
  .flat{padding-block:9svh}
  .silence{height:22svh}
}

/* ---------- the fallback, decided now rather than retrofitted ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  #stage{display:none}
  .act{background:var(--night-2)}
  /* no scene behind it, so the veil would be a tint over the body colour */
  .flat,.foot{background:var(--ground);backdrop-filter:none;-webkit-backdrop-filter:none}
  .numeral{transition:none}
}
