/* ============================================================
   Mental World Modeling · project page v2
   Palette: warm ivory paper · gold armillary · physical blue ×
   mental violet. Roles are colour-coded consistently everywhere:
   world model = indigo, target = coral, Ω = teal, π = coral, Γ = indigo.
   ============================================================ */

:root{
  /* paper */
  --paper:#FBF8F2;
  --paper-2:#F6F1E7;
  --paper-3:#EFE7D8;
  --paper-4:#E6DCC8;
  --white:#FFFDF9;

  /* ink */
  --ink:#191C24;
  --ink-2:#3A4152;
  --ink-3:#6B7386;
  --ink-4:#98A0B0;

  /* accents */
  --gold:#C0912F;
  --gold-2:#E3BB63;
  --gold-3:#8A6414;
  --gold-glow:#F5DFA6;

  --phy:#2F6DAE;         /* physical channel */
  --phy-dk:#1E4374;
  --phy-lt:#E7F1FC;
  --phy-lt2:#F3F8FE;

  --ment:#6F4BB7;        /* mental channel */
  --ment-dk:#4A2E85;
  --ment-lt:#F1ECFD;
  --ment-lt2:#F8F5FE;

  --teal:#1F8A86;        /* observation Ω */
  --teal-lt:#E4F4F2;
  --coral:#C2543D;       /* target / action π */
  --coral-lt:#FCEDE8;
  --green:#2E8B57;
  --red:#B03030;

  --line:#E2D9C6;
  --line-2:#D6CAB1;

  --r-s:10px; --r-m:16px; --r-l:24px; --r-xl:34px;
  --sh-1:0 1px 2px rgba(60,44,20,.05), 0 6px 18px -8px rgba(60,44,20,.14);
  --sh-2:0 2px 6px rgba(60,44,20,.06), 0 22px 50px -22px rgba(60,44,20,.28);
  --sh-3:0 30px 80px -40px rgba(60,44,20,.45);

  --ff-d:"Fraunces","Iowan Old Style",Georgia,serif;
  --ff-b:"Inter","Helvetica Neue",Arial,sans-serif;
  --ff-m:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --ff-x:"Newsreader",Georgia,"Times New Roman",serif;

  --wrap:1180px;
  --wrap-w:1420px;
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--ff-b);font-size:17px;line-height:1.62;
  font-weight:400;letter-spacing:-.005em;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
em{font-style:normal}

/* paper grain. Multiplies over the whole page, which is why .hero-video has to
   be kept off the hardware overlay path - see the note there. */
.grain{
  position:fixed;inset:-50%;z-index:9999;pointer-events:none;opacity:.4;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E");
  mix-blend-mode:multiply;
}

/* ─────────────── math typography ─────────────── */
.mx,.sc-sym,.fx{font-family:var(--ff-x);font-style:italic;font-weight:400}
.mx sub,.mx sup,.fx sub,.fx sup{font-size:.62em;font-style:normal;letter-spacing:0}
.mx sub i,.mx sup i,.fx sub i,.fx sup i{font-family:var(--ff-x);font-style:italic}
.mx .up,.fx .up{font-family:var(--ff-b);font-style:normal}
.mx .op,.fx .op{font-style:normal;font-family:var(--ff-b);color:var(--ink-3);padding:0 .18em}
.mx .mid,.fx .mid{font-style:normal;color:var(--ink-4);padding:0 .28em}
.mx .cal,.fx .cal{font-family:var(--ff-x);font-style:italic}

/* ══════════════════════════ TOP BAR ══════════════════════════ */
.topbar{
  position:fixed;top:0;left:0;right:0;z-index:800;
  display:flex;align-items:center;gap:24px;
  padding:14px clamp(18px,3.4vw,44px);
  transition:background .5s,box-shadow .5s,backdrop-filter .5s,padding .4s;
}
.topbar.is-stuck{
  background:rgba(251,248,242,.82);backdrop-filter:blur(14px) saturate(1.3);
  box-shadow:0 1px 0 rgba(214,202,177,.7),0 10px 30px -22px rgba(60,44,20,.5);
  padding-top:10px;padding-bottom:10px;
}
/* One lockup, hard right: wordmark, rule, links. The bar's left half is now
   deliberately empty — the observer sits in that corner of the sky directly
   below, and a wordmark over him was two things competing for the same look. */
.tstack{margin-left:auto;display:flex;flex-direction:column;align-items:flex-end;gap:5px}
.brand{display:flex;align-items:center;gap:13px;flex:0 0 auto}
.brand-mark{width:38px;height:38px;filter:saturate(1.04) drop-shadow(0 1px 2px rgba(120,88,26,.22))}
/* deep gold rather than black: it sits over the hero's cream sky, and the same
   ink the kickers and the beam use reads as part of that world instead of as
   chrome laid on top of it */
.brand-text{
  margin:0;font-family:var(--ff-d);font-weight:400;
  font-size:clamp(19px,1.55vw,25px);line-height:1.1;letter-spacing:-.028em;
  font-variation-settings:"SOFT" 0,"WONK" 1,"opsz" 40;
  color:var(--gold-3);
}
/* the only two links in the top bar; everything else lives further down */
.tlinks{
  display:flex;align-items:center;justify-content:flex-end;
  gap:clamp(18px,2.2vw,32px);
  align-self:stretch;padding-top:5px;
  border-top:1px solid rgba(160,124,52,.34);
}
.tlinks a{
  position:relative;padding:5px 1px;
  font-family:var(--ff-m);font-size:11.5px;font-weight:500;
  letter-spacing:.17em;text-transform:uppercase;color:var(--ink-2);
  transition:color .3s;
}
.tlinks a::after{
  content:"";position:absolute;left:0;right:0;bottom:1px;height:1px;background:var(--gold);
  transform:scaleX(0);transform-origin:left;transition:transform .4s cubic-bezier(.2,.8,.2,1);
}
.tlinks a:hover{color:var(--ink)}
.tlinks a:hover::after{transform:scaleX(1)}

a.is-soon{position:relative}
a.is-soon::before{
  content:"soon";position:absolute;left:50%;top:-17px;transform:translateX(-50%) translateY(3px);
  font-family:var(--ff-m);font-size:8.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold-3);opacity:0;transition:.3s;pointer-events:none;white-space:nowrap;
}
a.is-soon:hover::before{opacity:1;transform:translateX(-50%)}
a.shake{animation:nudge .45s cubic-bezier(.36,.07,.19,.97)}
@keyframes nudge{10%,90%{transform:translateX(-1.5px)}30%,70%{transform:translateX(2.5px)}50%{transform:translateX(-2px)}}

/* ══════════════════════════ HERO ══════════════════════════ */
/* A miniature city, running. The film is full-bleed and pinned to the bottom of
   the stage so the diorama's base never gets cut; whatever height is left over
   becomes sky above it. That sky is painted the film's own sky colour, so the
   seam is invisible and the gap reads as part of the same world. The gap is
   the observer's room: the cloud bank sits in it and spills a little way down
   into the film's own painted sky, which is empty on that side. */
.hero{
  /* The fallback sky: the average of the film's own top row, used until the
     script has a decoded frame to sample and whenever it cannot get one.
     city.png is graded ~10 levels brighter (#F7EADB); if the film is ever
     regraded to match the still, change this one value. */
  --hero-sky:#EDE2D4;
  /* What actually gets painted. The film's top row is not one colour: it holds
     near #EBDED1 for the left seven eighths and then lifts to #EDE4D8 at the
     right edge, a step of six levels of blue. Matching the average leaves both
     ends a little wrong, so the script samples that row through whatever crop
     `cover` is currently applying and writes the profile back here as a
     gradient; the flat value above is only what shows before it can. See
     paintSky() in hero.js. Note this is a refinement, not the reason the join
     used to be visible - that was the grain, see .hero-video. */
  --hero-sky-band:var(--hero-sky);
  --hero-gap:clamp(118px,15vh,198px);
  --wm-top:clamp(38px,5vh,70px);
  /* The sky the observer has to live in: the gap, plus the strip of painted sky
     at the top of the film it is allowed to hang into. Squash the window and
     both shrink, and a full-size cloud bank would end up sitting on the streets
     rather than above them, so the observer is sized against this and not
     against the window width alone. */
  --wm-sky:calc(var(--hero-gap) * .7 + 30svh - var(--wm-top));
  /* the observer's own box, hoisted here because the analysis panel has to be
     placed off its right edge and the script has to reserve its footprint */
  /* cloud-upper.png is painted right up to its own left edge (0% internal
     margin), so a negative offset here crops the bank rather than revealing
     more of it. This is as far left as the observer goes before it looks cut. */
  --wm-left:clamp(4px,.7vw,14px);
  --wm-w:min(clamp(232px,25.5vw,388px),calc(var(--wm-sky) / .72));
  position:relative;isolation:isolate;overflow:hidden;
  min-height:100svh;
  /* --hero-gap is a viewport function, but the claim written into the gap is
     sized off the window's *width*, so on any short window the text outgrew the
     sky and the film began underneath it: the cue line ended up printed on the
     skyline. clearSay() in hero.js measures the block and publishes the floor.
     It lands here and not on --hero-gap on purpose — --wm-sky reads --hero-gap,
     so raising this cannot feed back into the observer's size and set the two
     chasing each other. Named because the stage's min-height is its complement. */
  --sky-pad:max(var(--hero-gap),var(--say-floor,0px));
  padding-top:var(--sky-pad);
  display:flex;flex-direction:column;justify-content:flex-start;
  background:var(--hero-sky-band);
}
/* The film hangs from the gap, and its foot is allowed off the bottom of the
   screen.

   It used to be the other way round — the stage took whatever height was left
   and `cover` kept the film's foot, which meant a short window cropped the top
   instead. That crop is what broke the hero on other machines: at 1512x760 it
   ate 27.8% of the frame, so the first visible row was no longer the film's own
   sky but the brown haze over the rooftops — and paintSky() samples that row to
   paint the gap. The whole band above the city went muddy.

   aspect-ratio pins the box to the frame, so the film is never cropped
   vertically and row 0 is always what the sampler reads; min-height keeps it
   filling the screen on a tall window, where `cover` crops the sides instead.
   Losing the foot below the fold costs a small scroll and nothing else. */
.hero-stage{
  position:relative;width:100%;flex:0 0 auto;
  aspect-ratio:3840 / 2160;
  min-height:calc(100svh - var(--sky-pad));
}
/* The same problem as .hero-seam, at the other end: the film simply stopped and
   the paper began. Under everything the hero puts on top of the film (z-index 3
   and up) so it never veils the observer or the caption. */
.hero::after{
  content:'';position:absolute;left:0;right:0;bottom:0;z-index:2;pointer-events:none;
  height:clamp(64px,9vh,130px);
  background:linear-gradient(180deg,rgba(251,248,242,0) 0%,rgba(251,248,242,.5) 52%,var(--paper) 100%);
}
/* not a player: no controls, no focus, no pointer target. it just runs.
   absolute so the height resolves against the flex-sized stage. */
.hero-video{
  position:absolute;inset:0;display:block;width:100%;height:100%;
  /* top-anchored, to match the stage: the frame's own sky row has to stay the
     first visible one, because paintSky() samples it to colour the gap. Keep
     this in step with `oy` in crop(). */
  object-fit:cover;object-position:50% 0%;
  pointer-events:none;user-select:none;-webkit-user-select:none;
  background:var(--hero-sky);
  /* This is what was making the sky gap look like a different colour from the
     film. .grain is a full-page multiply, and a plain <video> gets promoted to a
     hardware overlay plane that the compositor draws past the blend - so the gap
     came out grain-darkened and the film did not, and the join between them read
     as a hard line about a dozen levels wide. No colour on either side was
     wrong. A fractional opacity forces the video into an ordinary composited
     layer, which the grain then multiplies like everything else. Remove this and
     the seam comes straight back. */
  opacity:.999;
}
/* Belt and braces on the join: the gap's own paint, laid over the film's first
   rows and faded out, so whatever levels the sampler is still off by are spread
   over 30px instead of landing on a straight edge. The stops are a smoothstep,
   not a straight line - a linear alpha ramp ends in two corners and the eye
   finds those the same way it finds a seam. Keep the height inside the film's
   own painted sky: it is flat down to source row ~260, which is about 50px at
   the shallowest crop, so this must not grow much past 34px. */
.hero-seam{
  position:absolute;left:0;right:0;top:0;height:clamp(20px,2.6vh,34px);
  background:var(--hero-sky-band);pointer-events:none;
  -webkit-mask-image:linear-gradient(to bottom,#000 0%,rgba(0,0,0,.96) 18%,rgba(0,0,0,.78) 38%,rgba(0,0,0,.44) 62%,rgba(0,0,0,.14) 84%,transparent 100%);
  mask-image:linear-gradient(to bottom,#000 0%,rgba(0,0,0,.96) 18%,rgba(0,0,0,.78) 38%,rgba(0,0,0,.44) 62%,rgba(0,0,0,.14) 84%,transparent 100%);
}
.hero-video::-webkit-media-controls,
.hero-video::-webkit-media-controls-enclosure,
.hero-video::-webkit-media-controls-panel,
.hero-video::-webkit-media-controls-start-playback-button{display:none!important;-webkit-appearance:none}

/* ─────────── the observer above the city ───────────
   Three plates in a fixed stacking order: cloud (back) · bust · cloud (front).
   Everything inside is sized in percentages of .wm, so the whole assembly
   scales as one object and never drifts apart. Idle, the bust is parked below
   the front bank and invisible; on the first flashpoint the two banks part and
   it rises between them, still cut off at the chest by the front cloud. */
.wm{
  --rise:46%;          /* how far down the bust is parked while hidden      */
  --gaze:0;            /* -1 west · +1 east, set by the script on activation */
  --open:0;            /* 0 closed, 1 parted — drives both banks at once     */
  position:absolute;z-index:3;pointer-events:none;
  left:var(--wm-left);
  top:var(--wm-top);
  width:var(--wm-w);
  aspect-ratio:1/.72;
}
.wm>*{position:absolute}

/* soft light behind the bust; the only "effect", and it stays low */
.wm-halo{
  left:20%;top:1%;width:60%;height:58%;border-radius:50%;
  background:radial-gradient(circle,rgba(255,246,224,.85) 0%,rgba(250,226,170,.38) 38%,rgba(250,226,170,0) 70%);
  opacity:calc(var(--open) * .9);transform:scale(calc(.8 + var(--open) * .2));
  transition:opacity 1.1s ease,transform 1.4s cubic-bezier(.2,.8,.2,1);
}
.wm-cloud{
  width:auto;filter:drop-shadow(0 10px 24px rgba(150,116,62,.13));
  transition:transform 1.5s cubic-bezier(.22,.9,.24,1),opacity .9s ease;
}
.wm-cloud-back{
  left:8%;top:-4%;width:104%;z-index:1;
  transform:translate3d(calc(var(--open) * -4%),calc(var(--open) * -6%),0);
  animation:wm-drift-a 34s ease-in-out infinite;
}
/* Box fitted to the art, not to the file. god-v2 is a square canvas cropped tight
   to the figure — it bleeds off its own top and bottom edges — where the previous
   asset was portrait with ~3% of margin all round. Dropping the new file into the
   old box would have rendered the same bust about a tenth smaller and sitting high.
   These three numbers put the visible figure back where it was: 45.5% keeps the
   shoulder span, 28.8% keeps its centre line, 6.5% splits the difference between
   matching the halo's top and the bust's waterline in the cloud. */
.wm-god{
  left:28.8%;top:6.5%;width:45.5%;z-index:2;
  transform:translate3d(calc(var(--gaze) * 2%),var(--rise),0) rotate(calc(var(--gaze) * 2.2deg));
  opacity:0;
  filter:drop-shadow(0 16px 30px rgba(120,88,26,.24));
  transition:transform 1.6s cubic-bezier(.19,.9,.22,1),opacity .8s ease .3s;
}
.wm-cloud-front{
  left:14%;bottom:-2%;width:106%;z-index:3;
  transform:translate3d(calc(var(--open) * 4%),calc(var(--open) * 5%),0);
  animation:wm-drift-b 41s ease-in-out infinite;
}
.wm[data-state=watching]{--open:1}
.wm[data-state=watching] .wm-god{--rise:0%;opacity:1;animation:wm-float 8.5s ease-in-out 1.6s infinite}

/* the banks never sit perfectly still: it should read as weather, not as a
   sticker. tiny amplitudes only — the city below is the thing being watched. */
@keyframes wm-drift-a{
  0%,100%{margin-left:0;margin-top:0}
  50%{margin-left:1.6%;margin-top:-1.1%}
}
@keyframes wm-drift-b{
  0%,100%{margin-left:0;margin-top:0}
  50%{margin-left:-1.9%;margin-top:.9%}
}
/* margin, not transform: the transform is already carrying rise and gaze */
@keyframes wm-float{
  0%,100%{margin-top:0}
  50%{margin-top:-1.5%}
}
/* Between opening an event and the clip running out he is doing nothing but
   watching it, and that stretch is now as long as the clip. A slow breath on
   the halo is the whole of the tell — no spinner, no label. The keyframe begins
   and ends on the resting values so that dropping the animation when the beam
   fires does not snap. */
.hero.is-open:not(.is-cast) .wm-halo{animation:wm-think 3.4s ease-in-out infinite}
@keyframes wm-think{
  0%,100%{opacity:calc(var(--open) * .9);transform:scale(1)}
  48%    {opacity:calc(var(--open) * .5);transform:scale(.93)}
}

/* ─────────── the standing claim ───────────
   Set in the display face at headline size because it is the first thing on the
   page and has to carry before the city does. Three sizes, one column: the
   assertion, the distinction, the invitation. It leaves on `is-open`, upward and
   slightly back, as if the beam had displaced it.

   Centred in the open sky rather than butted up against the observer. The band
   is bounded on the left by the cloud bank — which overhangs .wm's box by about
   a tenth of --wm-w, hence the wider inset than the readout uses — and on the
   right by the brand block. Setting left, right and a shrink-wrapped width all
   at once leaves the two auto margins to split what is left over, which is what
   does the centring; the text inside stays ranged left. */
.hero-say{
  /* the inset on the right clears the brand block; the open sky is what is left
     between the two, and .hs-line is sized off it because a line that will not
     wrap has to be told how much room it has */
  --hs-edge:clamp(34px,3.6vw,60px);
  /* the brand block's own width, rebuilt from the rules that set it — the bar's
     right padding, the mark and its gap, and the wordmark, which sets 10.2 times
     its size wide — plus clearance. A vw guess was 10px off the lockup at 1024
     and 200px shy of it at 1680; this tracks it at every width. */
  --hs-brand:calc(clamp(18px,3.4vw,44px) + 51px
                  + 10.2 * clamp(19px,1.55vw,25px) + clamp(28px,3vw,52px));
  --hs-band:calc(100vw - var(--wm-left) - var(--wm-w) - var(--hs-edge) - var(--hs-brand));
  position:absolute;z-index:5;pointer-events:none;
  /* level with the readout that replaces it, so nothing shifts vertically when
     one gives way to the other */
  top:calc(var(--wm-top) - .02 * var(--wm-w));
  left:calc(var(--wm-left) + var(--wm-w) + var(--hs-edge));
  right:var(--hs-brand);
  width:fit-content;margin-inline:auto;
  opacity:1;transform:none;
  transition:opacity .42s ease,transform .52s cubic-bezier(.3,.85,.3,1);
}
.hero-say>*{margin:0}
/* Sized against the sky, not against the viewport: the whole block has to clear
   the skyline, which at desktop starts about 220px down, and type that runs into
   the buildings is worse than type that is a size smaller. */
/* One line, always. 14.75 is this sentence in this face: it sets 14.26 times its
   own size wide, and the rest is slack for a scrollbar 100vw does not know
   about. Re-word the line and this number has to be re-measured, or a long
   enough sentence will run under the brand block instead of shrinking. */
.hs-line{
  font-family:var(--ff-d);font-weight:600;
  font-size:min(37px,var(--hs-band) / 14.75);
  line-height:1.1;letter-spacing:-.026em;
  color:var(--ink);white-space:nowrap;
}
.hs-sub{
  margin-top:clamp(9px,1.3vh,15px);max-width:34em;
  font-size:clamp(12.4px,.93vw,14.6px);line-height:1.5;
  letter-spacing:.004em;color:var(--ink-3);
}
.hs-cue{
  margin-top:clamp(10px,1.5vh,17px);
  display:flex;align-items:center;gap:8px;
  font-size:clamp(9.6px,.76vw,11.4px);font-weight:600;
  letter-spacing:.17em;text-transform:uppercase;color:var(--gold-3);
}
/* the same pulse the flashpoints use, so the sentence points at them */
.hs-dot{
  width:6px;height:6px;border-radius:50%;background:var(--gold);
  box-shadow:0 0 0 0 rgba(192,145,47,.5);
  animation:hs-ping 2.6s cubic-bezier(.2,.7,.3,1) infinite;
}
@keyframes hs-ping{
  0%{box-shadow:0 0 0 0 rgba(192,145,47,.5)}
  70%,100%{box-shadow:0 0 0 9px rgba(192,145,47,0)}
}
.hero.is-open .hero-say{
  opacity:0;transform:translate3d(-6px,-14px,0);
  transition-duration:.3s,.36s;
}

/* ─────────── the beam ───────────
   A fan of gold thrown from beside the figure's head onto the readout's left
   edge, so the panel is visibly cast rather than merely adjacent. The geometry
   in the markup is a unit fan in a 0–100 box; the script sets the origin, the
   aim and the two lengths, and preserveAspectRatio:none stretches it to
   whatever gap it has to cross.

   z 5 — over the cloud banks (3) and the flashpoints (4), under the two panels
   (6, 7). It sat at 2 to begin with, on the theory that the rays should come
   out from behind the observer, but the front cloud bank runs almost the whole
   way to the panel and cut the shaft in half. Light that stops at the first
   cloud it meets isn't lighting anything. It reads correctly anyway because the
   gradient holds fully transparent for its first seventh, which is the width of
   the bust: the fan still starts behind him, it just no longer ends there.

   Painted, not blended. Multiply looked better over the empty sky and then
   disappeared entirely the moment the beam crossed the skyline. Fixed alpha
   holds over cream and over rooftops alike, which is the whole job. */
.aw-ray{
  position:absolute;left:0;top:0;z-index:5;pointer-events:none;overflow:visible;
  width:var(--ry-w,0px);height:var(--ry-h,0px);
  transform-origin:0 50%;
  transform:translate3d(var(--ry-x,0px),calc(var(--ry-y,0px) - var(--ry-h,0px) / 2),0)
            rotate(var(--ry-r,0deg));
  /* it does not fade up, it reaches: clipped back to nothing at the origin and
     wiped outward along its own axis, so the light travels to the panel and the
     panel opens behind it. Cast on `is-cast`, which the script only sets once
     the observer has had his few seconds over the scene. */
  opacity:0;clip-path:inset(-30% 100% -30% 0);
  transition:opacity .3s ease,clip-path .82s cubic-bezier(.25,.7,.3,1);
}
.hero.is-cast .aw-ray{opacity:1;clip-path:inset(-30% 0 -30% 0)}
/* the fan never sits perfectly still — same reasoning as the cloud drift, and
   the same tiny amplitude. two periods so the rays never pulse in unison. */
.aw-ray-f path:nth-child(odd){animation:aw-ray-a 7.5s ease-in-out infinite}
.aw-ray-f path:nth-child(even){animation:aw-ray-a 9.5s ease-in-out infinite reverse}
@keyframes aw-ray-a{0%,100%{opacity:1}50%{opacity:.58}}

/* ─────────── flashpoints ───────────
   Not map pins: a perception node the system has already locked onto. One
   shared vocabulary for all three. Placement comes from the script. */
.fp-layer{position:absolute;inset:0;z-index:4;pointer-events:none}
.fp{
  /* one dial. every part below is a ratio of it, so shrinking the node on a
     small screen shrinks the whole thing instead of leaving a big halo around
     a small dot. */
  --d:52px;
  --tx:-50%;   /* tag anchor; the script flips it near the stage edges */
  /* Signal amber, as rgb parts so every layer can pick its own alpha off one
     value. The city's own oranges are muted terracotta; this is hotter and far
     more saturated, so it reads as something burning on top of the world
     rather than as another roof. */
  --c:255,138,18;
  position:absolute;left:0;top:0;width:var(--d);height:var(--d);margin:calc(var(--d) / -2);
  padding:0;border:0;background:none;border-radius:50%;
  pointer-events:auto;cursor:pointer;-webkit-tap-highlight-color:transparent;
  transform:translate3d(var(--x,-999px),var(--y,-999px),0);
  opacity:1;transition:opacity .5s ease;
}
.fp[hidden],.fp.is-out{opacity:0;pointer-events:none}
.fp>span{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);pointer-events:none}

/* The pool of light the event casts on the city. It burns at rest too, not
   only on approach: three points on a wide diorama have to be found before
   they can be hovered. */
.fp-glow{
  width:calc(var(--d) * 3.4);height:calc(var(--d) * 3.4);border-radius:50%;
  background:radial-gradient(circle,
    rgba(var(--c),.46) 0%,
    rgba(var(--c),.26) 30%,
    rgba(var(--c),.10) 52%,
    rgba(var(--c),0) 72%);
  opacity:.72;transition:opacity .5s ease,transform .6s cubic-bezier(.2,.8,.2,1);
  transform:translate(-50%,-50%) scale(.78);
  animation:fp-bloom 3.6s ease-in-out infinite;
}
.fp:hover .fp-glow,.fp:focus-visible .fp-glow,.fp.is-on .fp-glow{opacity:1;transform:translate(-50%,-50%) scale(1)}
.fp.is-on .fp-glow{animation:none}
/* the pool breathes with the core, a beat behind it */
@keyframes fp-bloom{
  0%,100%{opacity:.62}
  50%{opacity:.9}
}

/* white-hot at the centre. the city is warm, so the point cannot win on hue
   alone; it wins on the brightness step from amber to white. */
.fp-core{
  width:calc(var(--d) * .25);height:calc(var(--d) * .25);border-radius:50%;
  background:radial-gradient(circle at 36% 32%,#fff 0 34%,#FFC24A 60%,rgb(var(--c)) 100%);
  box-shadow:
    0 0 0 2.4px rgba(255,255,255,.85),
    0 0 14px 4px rgba(var(--c),.95),
    0 0 34px 12px rgba(var(--c),.55),
    0 1px 4px rgba(60,30,4,.4);
  animation:fp-breathe 3.6s ease-in-out infinite;
  transition:transform .35s cubic-bezier(.2,.8,.2,1);
}
.fp:hover .fp-core,.fp:focus-visible .fp-core{transform:translate(-50%,-50%) scale(1.28)}
/* locked on: the search pulse stops and the node holds a steady ring */
.fp.is-on .fp-core{
  animation:none;transform:translate(-50%,-50%) scale(1.3);
  box-shadow:
    0 0 0 2.8px rgba(255,255,255,.95),
    0 0 18px 5px rgba(var(--c),1),
    0 0 44px 15px rgba(var(--c),.6),
    0 1px 4px rgba(60,30,4,.45);
}
@keyframes fp-breathe{
  0%,100%{transform:translate(-50%,-50%) scale(1);filter:brightness(1)}
  50%{transform:translate(-50%,-50%) scale(1.16);filter:brightness(1.18)}
}

/* the energy pulse: two rings, offset, so it reads as a signal repeating
   rather than a single decorative ripple */
.fp-ring{
  width:calc(var(--d) * .34);height:calc(var(--d) * .34);border-radius:50%;
  /* a bright rim, not an amber one: the ring travels out through its own amber
     pool, and amber on amber is invisible. white edged in amber carries. */
  border:2.5px solid rgba(255,247,230,.98);
  box-shadow:0 0 9px 1.5px rgba(var(--c),.9),inset 0 0 6px rgba(var(--c),.65);
  animation:fp-pulse 3.6s cubic-bezier(.18,.7,.3,1) infinite;
}
.fp-ring-b{animation-delay:1.8s}
.fp.is-on .fp-ring{
  animation:none;opacity:1;
  transform:translate(-50%,-50%) scale(1.85);
  border-color:rgba(255,247,230,.85);
}
.fp.is-on .fp-ring-b{opacity:0}
/* the ring holds most of its way out instead of fading immediately: motion at
   a legible contrast is what finds the eye on a busy diorama, not hue */
@keyframes fp-pulse{
  0%{transform:translate(-50%,-50%) scale(.5);opacity:0}
  10%{opacity:1}
  58%{opacity:.62}
  100%{transform:translate(-50%,-50%) scale(3.6);opacity:0}
}

/* corner brackets: the system framing the event, shown only on approach */
.fp-ret{
  width:calc(var(--d) * .77);height:calc(var(--d) * .77);background:rgb(var(--c));
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 34' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M2 11V2h9M23 2h9v9M32 23v9h-9M11 32H2v-9'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 34' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M2 11V2h9M23 2h9v9M32 23v9h-9M11 32H2v-9'/%3E%3C/svg%3E") center/contain no-repeat;
  opacity:0;transform:translate(-50%,-50%) rotate(-8deg) scale(.62);
  transition:.42s cubic-bezier(.2,.8,.2,1);
}
.fp:hover .fp-ret,.fp:focus-visible .fp-ret,.fp.is-on .fp-ret{opacity:.92;transform:translate(-50%,-50%) rotate(0) scale(1)}

/* scene three sits inside a tower, so its marker carries the room: a lit pane
   skewed onto the facade's own plane (+11deg matches the film's isometry), a
   little wider than the brackets so it reads as a window behind the frame */
.fp-room{
  /* larger than the reticle in both axes, so the brackets read as framing a
     window rather than the window reading as a fill inside the brackets */
  width:calc(var(--d) * 1.28);height:calc(var(--d) * .86);border-radius:2px;
  transform:translate(-50%,-50%) skewY(11deg) scale(.7);
  /* Lit from inside, in the cool white of an office ceiling rather than the
     amber of the marker: if the pane and the reticle burn at the same
     temperature they merge into one blob and the room stops reading as a room.
     Opaque, with a hard edge, so it survives the marker's bloom on top of it. */
  background:linear-gradient(148deg,rgba(255,253,247,.97),rgba(255,242,214,.99));
  box-shadow:
    inset 0 0 0 1.5px rgba(255,255,255,1),
    0 0 22px 6px rgba(255,236,190,.7),
    0 2px 10px rgba(70,44,10,.28);
  opacity:0;transition:.55s cubic-bezier(.2,.8,.2,1);
}
.fp-win .fp-room{opacity:.78;transform:translate(-50%,-50%) skewY(11deg) scale(.86)}
/* the pool of light would flood the pane and flatten it, so this one node
   keeps its bloom tighter than the two out in the open */
.fp-win .fp-glow{width:calc(var(--d) * 2.7);height:calc(var(--d) * 2.7)}
.fp-win:hover .fp-room,.fp-win:focus-visible .fp-room,.fp-win.is-on .fp-room{
  opacity:1;transform:translate(-50%,-50%) skewY(11deg) scale(1);
}

/* beats .fp>span on specificity, which owns top/transform for the other parts */
.fp .fp-tag{
  left:50%;top:auto;bottom:calc(50% + var(--d) * .55);
  transform:translate(var(--tx),6px);
  white-space:nowrap;
  font-family:var(--ff-m);font-size:10px;font-weight:500;letter-spacing:.15em;text-transform:uppercase;
  color:#7A3A00;background:rgba(255,252,246,.92);
  -webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);
  padding:5px 11px 4px;border-radius:999px;border:1px solid rgba(var(--c),.4);
  box-shadow:0 8px 22px -12px rgba(70,38,8,.6);
  opacity:0;transition:.34s cubic-bezier(.2,.8,.2,1);
}
.fp:hover .fp-tag,.fp:focus-visible .fp-tag,.fp.is-on .fp-tag{opacity:1;transform:translate(var(--tx),0)}
.fp.is-on .fp-tag{border-color:rgba(var(--c),.75);box-shadow:0 8px 24px -12px rgba(var(--c),.9)}
/* a point near the stage edge would push a centred label off the film, so the
   label hangs off the near side instead of being clipped */
.fp.is-tag-e{--tx:-16px}
.fp.is-tag-w{--tx:calc(-100% + 16px)}
.fp.is-tag-s .fp-tag{bottom:auto;top:calc(50% + var(--d) * .55)}
.fp:focus{outline:none}
.fp:focus-visible .fp-ret{box-shadow:none}
.fp:focus-visible::after{
  content:"";position:absolute;inset:-4px;border-radius:50%;
  border:1.5px dashed rgba(var(--c),.85);
}

/* ─────────── the tether ───────────
   The frame must never read as a modal that happened to appear; it is a feed
   opened from one point on the map, and the line is what says so. The script
   tweens the geometry between scenes so the line swings across rather than
   blinking out and back. */
/* these three carry a display of their own, and an author `display` beats the
   UA sheet's [hidden] rule whatever the specificity, so say it here or they
   never actually go away */
.ev[hidden],.aw[hidden],.ev-replay[hidden]{display:none}

.ev-link{
  position:absolute;inset:0;z-index:5;pointer-events:none;
  width:100%;height:100%;overflow:visible;
  opacity:0;transition:opacity .45s ease;
}
.ev-link.is-on{opacity:1}
.ev-link path{
  fill:none;stroke:rgba(var(--c),.85);stroke-width:1.6;
  stroke-linecap:round;
  filter:drop-shadow(0 0 5px rgba(var(--c),.6));
  /* drawn on first open; the script sets the two custom lengths */
  stroke-dasharray:var(--len,600);
  stroke-dashoffset:var(--off,600);
  transition:stroke-dashoffset .62s cubic-bezier(.22,.85,.28,1);
}
.ev-link circle{
  fill:#fff;stroke:rgba(var(--c),.95);stroke-width:1.4;
  filter:drop-shadow(0 0 6px rgba(var(--c),.85));
  opacity:0;transition:opacity .3s ease .34s;
}
.ev-link.is-on circle{opacity:1}

/* ─────────── the observation terminal ───────────
   Warm glass rather than a dark HUD: the page is paper and the city is warm,
   and a black console dropped on top of them would read as a different site.
   The instrument feeling comes from the fittings — a hairline rail, corner
   brackets borrowed from the flashpoint reticle, a seek line, mono labels —
   not from making the panel dark. */
.ev{
  /* big enough to read faces in, small enough that the city stays the subject.
     Grown any wider and it stops being a feed opened on a diorama and starts
     being a video player with a diorama behind it. */
  --ev-w:clamp(292px,31vw,476px);
  position:absolute;z-index:7;
  left:0;top:0;width:var(--ev-w);
  transform:translate3d(var(--ex,0),var(--ey,0),0);
  opacity:0;pointer-events:none;
  transition:opacity .5s ease,transform .66s cubic-bezier(.2,.85,.25,1);
}
.ev.is-on{opacity:1;pointer-events:auto}
/* the body carries the open/close scale so the position transform above stays
   free to tween between scenes without fighting it */
.ev-body{
  border-radius:15px;overflow:hidden;
  background:linear-gradient(176deg,rgba(255,253,248,.93),rgba(250,243,231,.9));
  -webkit-backdrop-filter:blur(18px) saturate(1.25);backdrop-filter:blur(18px) saturate(1.25);
  border:1px solid rgba(var(--c),.34);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.6) inset,
    0 2px 8px rgba(70,44,12,.1),
    0 34px 70px -34px rgba(60,38,10,.62),
    0 0 44px -12px rgba(var(--c),.34);
  transform:scale(.94);transform-origin:50% 88%;
  transition:transform .66s cubic-bezier(.2,.85,.25,1);
}
.ev.is-on .ev-body{transform:scale(1)}

/* the two switches, now riding the caption row rather than a rail of their own */
.ev-tools{display:flex;align-items:center;gap:4px;flex:0 0 auto;margin-left:auto}
/* the labels stay for anything that reads the page aloud, and for the blocked
   state, which needs to be able to say "unmute" in words */
.ev-btn-t{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;
}

/* square now that the words are gone — a label-width button with no label in it
   reads as a gap */
.ev-btn{
  position:relative;
  display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;padding:0;border-radius:7px;
  color:var(--ink-4);border:1px solid transparent;
  transition:.22s ease;
}
.ev-btn svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.ev-btn:hover{color:#8A4406;background:rgba(var(--c),.1);border-color:rgba(var(--c),.26)}
.ev-btn[aria-pressed=true]{color:#8A4406;background:rgba(var(--c),.16);border-color:rgba(var(--c),.42)}
.ev-btn:focus-visible{outline:2px solid rgba(var(--c),.7);outline-offset:1px}
/* one button, two glyphs: whichever state it is *not* in is hidden */
.ev-i-off{display:none}
#evMute[aria-pressed=true] .ev-i-on{display:none}
#evMute[aria-pressed=true] .ev-i-off{display:block}
/* autoplay-with-sound was refused: the button becomes the invitation */
.ev-btn.is-blocked{
  color:#8A4406;background:rgba(var(--c),.2);border-color:rgba(var(--c),.6);
  animation:ev-ask 1.8s ease-in-out 3;
}
@keyframes ev-ask{0%,100%{box-shadow:0 0 0 0 rgba(var(--c),.5)}50%{box-shadow:0 0 0 5px rgba(var(--c),0)}}

/* the screen */
.ev-screen{position:relative;aspect-ratio:16/9;background:#14110D;overflow:hidden}
.ev-video{width:100%;height:100%;object-fit:cover;display:block;background:#14110D}
.ev-video::-webkit-media-controls{display:none!important;-webkit-appearance:none}
/* a scan texture at the edge of visibility. any stronger and it stops being a
   surface quality and starts being a costume. */
.ev-scan{
  position:absolute;inset:0;pointer-events:none;opacity:.2;mix-blend-mode:overlay;
  background:repeating-linear-gradient(180deg,rgba(255,255,255,.42) 0 1px,rgba(0,0,0,0) 1px 3px);
}
.ev-vig{
  position:absolute;inset:0;pointer-events:none;
  box-shadow:inset 0 0 44px rgba(20,12,4,.44),inset 0 0 0 1px rgba(255,255,255,.1);
}

/* ── dialogue plate ──
   Inside the picture, sat on its bottom edge, in the frame's own dark rather
   than on a bar under it: these clips are evidence, and a caption that lives
   outside the image reads as commentary on it instead of part of the record.
   One line and no more — `nowrap` with an ellipsis rather than a wrap, because
   a plate that grows to two lines shifts the picture's whole lower third every
   time a long line lands. The cues in scenes.js are cut to fit at this size. */
.vs{
  position:absolute;z-index:3;pointer-events:none;
  left:5%;right:5%;bottom:5.5%;
  display:flex;justify-content:center;
}
.vs span{
  max-width:100%;padding:.3em .6em;border-radius:6px;
  font-family:var(--ff-b);font-weight:400;
  font-size:clamp(9.5px,.86vw,13px);line-height:1.3;letter-spacing:-.004em;
  color:#fff;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  background:rgba(18,13,5,.56);
  -webkit-backdrop-filter:blur(8px) saturate(1.2);backdrop-filter:blur(8px) saturate(1.2);
  border:1px solid rgba(255,255,255,.12);
  text-shadow:0 1px 2px rgba(18,13,5,.5);
  opacity:0;transform:translateY(4px);transition:opacity .18s ease,transform .18s ease;
}
.vs.is-on span{opacity:1;transform:none}
/* who is speaking, set apart rather than punctuated in: all three scenes are
   three people disagreeing, so the name is doing as much work as the line */
.vs span i{
  font-style:normal;margin-right:.55em;
  font-family:var(--ff-m);font-size:.78em;font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;color:rgba(255,226,160,.94);
}
/* the state panes are a quarter of the terminal's width and carry the same
   sentences, so the plate loses its padding before it loses its type size */
.vs-sm{left:3.5%;right:3.5%;bottom:4%}
.vs-sm span{padding:.22em .45em;border-radius:5px;font-size:clamp(8px,.66vw,10px)}
.vs-sm span i{margin-right:.45em;font-size:.8em}
/* Where the panel stacks, the panes fall to about 150px and no sentence in any
   of the three scenes fits one line at a legible size. Two lines there, because
   a wrapped line still says the whole thing and an ellipsis does not. The
   terminal keeps the single line at every width — it is wide enough. */
@media (max-width:1080px),(max-aspect-ratio:1 / 1){
  .vs-sm span{
    white-space:normal;text-wrap:balance;
    display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;
  }
  .vs-sm span i{display:inline}
}
/* the reticle vocabulary again, at frame scale: the terminal is the same
   instrument as the marker, one zoom level in */
.ev-brk{
  position:absolute;width:15px;height:15px;pointer-events:none;
  border:1.6px solid rgba(255,244,226,.82);
  filter:drop-shadow(0 0 4px rgba(var(--c),.7));
  opacity:0;transition:opacity .5s ease .3s;
}
.ev.is-on .ev-brk{opacity:1}
.ev-brk-a{left:9px;top:9px;border-right:0;border-bottom:0}
.ev-brk-b{right:9px;top:9px;border-left:0;border-bottom:0}
.ev-brk-c{left:9px;bottom:9px;border-right:0;border-top:0}
.ev-brk-d{right:9px;bottom:9px;border-left:0;border-top:0}

/* it plays once by design, so the end state has to offer the way back */
.ev-replay{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) scale(.9);
  display:inline-flex;align-items:center;gap:8px;
  padding:10px 17px;border-radius:999px;
  font-family:var(--ff-m);font-size:10px;letter-spacing:.16em;text-transform:uppercase;
  color:#5A2C02;background:rgba(255,251,244,.94);
  border:1px solid rgba(var(--c),.5);
  box-shadow:0 12px 30px -14px rgba(30,18,4,.8);
  opacity:0;transition:.3s cubic-bezier(.2,.8,.2,1);
}
.ev-replay svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.ev-replay:not([hidden]){opacity:1;transform:translate(-50%,-50%) scale(1)}
.ev-replay:hover{background:#fff;border-color:rgba(var(--c),.85)}

/* seek line: the only thing that tells you the clip is finite */
.ev-seek{height:2px;background:rgba(var(--c),.15)}
.ev-seek i{
  display:block;height:100%;width:0;
  background:linear-gradient(90deg,rgba(var(--c),.7),rgb(var(--c)));
  box-shadow:0 0 8px rgba(var(--c),.8);
}

/* the caption: the event's name, and the two switches, on one line. The mono
   sub-line that used to sit under the title repeated the place — which the
   picture is already showing and the readout above already names — so the band
   is now a single row of type high enough to hold a 26px button and no more. */
.ev-cap{display:flex;align-items:center;gap:10px;padding:7px 8px 7px 14px}
.ev-title{
  margin:0;flex:1 1 auto;min-width:0;
  font-family:var(--ff-d);font-size:16px;font-weight:600;
  letter-spacing:-.012em;line-height:1.2;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* ─────────── physical / mental readout ───────────
   Sits in the band of sky to the observer's right, at the far end of the beam,
   on the same glass as the terminal so the two read as one instrument in two
   parts. The two halves of the state run left to right, not top to bottom:
   §03 defines them as a product and not a sequence, and a stack of that height
   would have taken the whole sky, leaving the terminal nowhere to sit and the
   city nothing to breathe into. Each half is a screen rather than a list — the
   physical pass is the event from overhead, the mental pass is what each person
   in it is holding — so the panel stays one card tall whatever the scene says. */
.aw{
  position:absolute;z-index:6;
  left:calc(var(--wm-left) + var(--wm-w) + clamp(10px,1.4vw,24px));
  /* Level with the top of the cloud bank, derived rather than guessed. The bank
     is --wm-w wide and cloud-upper.png is 1536x803, so it stands 0.523 --wm-w
     tall; its box begins 4% of the stage height above --wm-top, its painted
     cloud begins 5.9% into its own height, and it lifts 6% of that height again
     when it parts. Those three terms collapse to about 0.03 --wm-w above
     --wm-top. Sitting lower than this left a band of empty sky between the
     observer and his own readout. */
  top:calc(var(--wm-top) - .03 * var(--wm-w));
  width:clamp(452px,44vw,690px);
  /* a guard, not a layout: the panes are sized off the viewport height below,
     so this only ever catches an extreme window */
  max-height:min(calc(100svh - 372px),500px);
  display:flex;flex-direction:column;
  border-radius:14px;overflow:hidden;
  background:linear-gradient(176deg,rgba(255,253,248,.91),rgba(249,242,230,.87));
  -webkit-backdrop-filter:blur(18px) saturate(1.2);backdrop-filter:blur(18px) saturate(1.2);
  border:1px solid rgba(210,190,152,.62);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.55) inset,
    0 26px 60px -32px rgba(60,38,10,.55),
    -14px 0 40px -26px rgba(150,104,10,.65);
  opacity:0;transform:translate3d(-10px,6px,0) scale(.97);transform-origin:0 12%;
  pointer-events:none;
  /* the leaving transition. Switching events sends the observer back to thinking
     for a couple of seconds, and this panel has to be gone before its text is
     swapped for the new scene's — so it goes out quickly and comes back at
     leisure, which is also the right shape for the gesture. */
  transition:opacity .2s ease,transform .26s ease;
}
/* the edge the beam lands on, lit from outside */
.aw::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:2.5px;z-index:1;
  background:linear-gradient(180deg,
    rgba(176,124,12,0),rgba(176,124,12,.8) 20%,rgba(224,179,85,.9) 50%,
    rgba(176,124,12,.8) 80%,rgba(176,124,12,0));
}
.aw.is-on{
  opacity:1;transform:none;pointer-events:auto;
  transition:opacity .5s ease,transform .6s cubic-bezier(.2,.85,.25,1);
}

.aw-bar{
  display:flex;align-items:baseline;gap:9px;flex:0 0 auto;
  padding:8px 13px 7px;border-bottom:1px solid rgba(210,190,152,.5);
  background:linear-gradient(180deg,rgba(255,250,240,.7),rgba(255,250,240,0));
  font-family:var(--ff-m);font-size:9.5px;letter-spacing:.15em;text-transform:uppercase;
}
.aw-k{color:#8A4406;font-weight:600;flex:0 0 auto}
.aw-t{color:var(--ink-4);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* two columns, fixed. they are the two factors of one state, so neither is
   allowed to be the wide one. */
.aw-panes{
  flex:1 1 auto;min-height:0;
  display:grid;grid-template-columns:1fr 1fr;gap:13px;
  padding:9px 14px 3px;
}
.aw-sec{display:flex;flex-direction:column;min-width:0}
.aw-h{
  display:flex;align-items:center;gap:7px;margin:0 0 5px;
  font-family:var(--ff-b);font-size:11.5px;font-weight:700;
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink-2);
}
.aw-h i{
  font-style:normal;margin-left:auto;
  font-family:var(--ff-m);font-size:8.5px;font-weight:400;
  letter-spacing:.13em;color:var(--ink-4);
}
.aw-dot{width:7px;height:7px;border-radius:2px;flex:0 0 auto}
.aw-phy .aw-dot{background:var(--phy);box-shadow:0 0 0 3px rgba(47,109,174,.14)}
.aw-ment .aw-dot{background:var(--ment);box-shadow:0 0 0 3px rgba(111,75,183,.14)}

/* Taller than the terminal's 16:9 and narrower than half this panel: these are
   not a second feed of the same kind.

   The cap is the one thing here that has to know about the window. Everything
   else in this panel — bar, headings, captions, claim — is about 167px of fixed
   chrome, so the two screens are the whole of its variable height, and the
   terminal underneath needs its own band along the bottom. Hence the affine
   form rather than a plain svh fraction: a straight percentage is right at one
   window height and wrong at every other, and being wrong here means the
   terminal has nowhere to go and parks on the panel's bottom edge. */
.aw-screen{
  position:relative;flex:0 0 auto;
  aspect-ratio:4/3;
  max-height:min(max(120px,calc(52svh - 280px)),208px);
  border-radius:9px;overflow:hidden;background:#131009;
}
.aw-phy .aw-screen{box-shadow:0 0 0 1px rgba(47,109,174,.34),0 10px 22px -14px rgba(20,40,70,.7)}
.aw-ment .aw-screen{box-shadow:0 0 0 1px rgba(111,75,183,.34),0 10px 22px -14px rgba(40,20,70,.7)}
.aw-video{width:100%;height:100%;object-fit:cover;display:block;background:#131009}
.aw-video::-webkit-media-controls{display:none!important;-webkit-appearance:none}
/* the whole frame takes the sound: at this size a 22px target is a poor thing
   to have to hit, and there is nothing else the picture could mean by a click */
.aw-screen{cursor:pointer}

/* ── per-channel switches ──
   In the picture, top right, because they act on this pass and not on the
   panel — and because the panel already has two of everything, a pair of
   controls under it would have to say which channel it meant. Dark glass over
   the film rather than the terminal's ink-on-paper: these sit on the video. */
.aw-ctl{position:absolute;top:5px;right:5px;z-index:4;display:flex;gap:3px}
.aw-btn{
  position:relative;
  display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;padding:0;border-radius:6px;
  color:rgba(255,246,232,.74);
  background:rgba(18,13,5,.44);border:1px solid rgba(255,255,255,.15);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  transition:.2s ease;
}
.aw-btn svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.aw-btn:hover{color:#FFF4DC;background:rgba(18,13,5,.66);border-color:rgba(255,255,255,.32)}
/* lit means on: this channel is the one holding the sound, or is repeating */
.aw-btn[aria-pressed=true]{color:#2C1C04;background:rgba(240,201,116,.92);border-color:rgba(255,238,196,.85)}
.aw-btn:focus-visible{outline:2px solid rgba(240,201,116,.9);outline-offset:1px}
/* one button, two glyphs: whichever state it is *not* in is hidden */
.aw-i-off{display:none}
.aw-snd[aria-pressed=false] .aw-i-on{display:none}
.aw-snd[aria-pressed=false] .aw-i-off{display:block}
/* autoplay-with-sound was refused this far from the click: the button asks */
.aw-btn.is-blocked{
  color:#2C1C04;background:rgba(240,201,116,.92);border-color:rgba(255,238,196,.9);
  animation:ev-ask 1.8s ease-in-out 3;
}
.aw-btn-t{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;
}
/* a survey grid on the physical pass, a soft field on the mental one: the same
   footage is standing in for both while the real pair is shot, and the overlay
   is what says which channel you are looking at */
.aw-mesh{position:absolute;inset:0;pointer-events:none;opacity:.34;mix-blend-mode:overlay}
.aw-phy .aw-mesh{
  background:
    repeating-linear-gradient(90deg,rgba(150,205,255,.5) 0 1px,rgba(0,0,0,0) 1px 22px),
    repeating-linear-gradient(180deg,rgba(150,205,255,.5) 0 1px,rgba(0,0,0,0) 1px 22px);
}
.aw-ment .aw-mesh{
  background:radial-gradient(120% 78% at 50% 108%,rgba(186,150,255,.75),rgba(186,150,255,0) 62%);
  opacity:.5;mix-blend-mode:screen;
}
.aw-vig{
  position:absolute;inset:0;pointer-events:none;
  box-shadow:inset 0 0 30px rgba(18,10,2,.5),inset 0 0 0 1px rgba(255,255,255,.09);
}
/* what the shot is, said once. Moved off the bottom edge when the dialogue
   plate took that band, and stopped short of the controls in the top right —
   the plate and the switches are the content, this is only the label. */
.aw-eye{
  position:absolute;left:6px;top:5px;right:58px;
  font-family:var(--ff-m);font-size:8px;letter-spacing:.11em;text-transform:uppercase;
  color:rgba(255,248,236,.9);text-shadow:0 1px 3px rgba(0,0,0,.85);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* one line of context under each screen, clipped rather than allowed to set the
   panel's height: the screens are the content now */
.aw-cap{
  margin:6px 0 0;font-size:11.4px;line-height:1.34;color:var(--ink-3);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
}
.aw-ment .aw-cap{color:var(--ment-dk)}

/* content arrives after the box does, per the reveal order */
.aw .aw-sec{opacity:0;transform:translateY(5px);transition:.44s cubic-bezier(.2,.85,.25,1)}
.aw.is-lit .aw-phy{opacity:1;transform:none;transition-delay:.06s}
.aw.is-lit .aw-ment{opacity:1;transform:none;transition-delay:.18s}

/* ── narrow desktop: the sky runs out before the panel does ──
   Below this the terminal and the readout cannot both hold their own column,
   so they stack into one centred card over a dimmed city and the tether is
   dropped: a line to a point behind a scrim explains nothing. */
@media (max-width:1080px),(max-aspect-ratio:1/1){
  .ev-link{display:none}
  /* the panel is a centred modal here, not something off to the observer's
     right, so there is no gap for a beam to cross */
  .aw-ray{display:none}
  /* Measured against the window, not the hero. In portrait the hero is only as
     tall as the observer's gap plus the film — shorter than the stack — so an
     absolutely positioned readout was being cut off at the section boundary
     and the mental half never appeared at all. Once there is a scrim and no
     tether this is a modal in everything but name, and a modal is measured
     against the viewport. */
  .ev,.aw{position:fixed}
  /* the hero isolates, so its z-indexes are private to it and the sections that
     follow paint over the whole overlay. Lifting the hero's own context clears
     them — and 40 keeps it under the topbar at 800, which should stay reachable */
  .hero.is-open{z-index:40}
  .hero.is-open::before{
    content:"";position:fixed;inset:0;z-index:5;
    background:rgba(38,26,12,.42);
    -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
    animation:ev-scrim .45s ease both;
  }
  @keyframes ev-scrim{from{opacity:0}to{opacity:1}}
  /* one positioning mechanism in both modes: the script owns the offsets, so
     the stack and the two-column layout cannot disagree about where things are */
  .ev{--ev-w:min(520px,calc(100vw - 32px))}
  .aw{
    left:0;top:0;
    width:min(520px,calc(100vw - 32px));
    max-height:var(--aw-cap,min(46svh,420px));
    transform:translate3d(var(--ax,0),calc(var(--ay,0px) + 8px),0) scale(.97);
  }
  .aw.is-on{transform:translate3d(var(--ax,0),var(--ay,0),0)}
  .aw-panes{gap:9px;padding:8px 11px 2px}
  .aw-screen{max-height:min(19svh,158px)}
}
@media (max-width:560px){
  .ev-cap{padding:9px 12px 11px}
  .ev-title{font-size:15px}
  .ev-btn span{display:none}
  .ev-btn{padding:5px 6px}
  .aw-cap{font-size:11.8px}
}

/* portrait: cropping to fill would eat the city sideways, so show the whole
   frame and shorten the hero instead of leaving a field of empty sky.
   The hero is then exactly gap + film: holding it to a share of the viewport
   would open a band of dead cream between the observer and the city, because
   the film's height is fixed by its own aspect ratio and cannot grow to meet
   it. The gap is sized to the observer instead, which is what it is for. */
@media (max-aspect-ratio:1/1){
  /* the gap is sized to the observer, plus - since the claim has nowhere else to
     go on a portrait screen - the block that now sits under him. Without the
     extra the cue lands on the skyline, and the film's own sky is barely 30px
     tall once the frame is letterboxed to 16/9. */
  .hero{min-height:0;--hero-gap:clamp(214px,30vh,296px)}
  /* min-height:0 undoes the landscape floor: here the hero is exactly gap plus
     film, and making the stage fill the screen is the dead-cream band above */
  .hero-stage{flex:0 0 auto;aspect-ratio:16/9;min-height:0}
  .hero-video{object-fit:contain;object-position:50% 0%}
  .wm{width:clamp(190px,44vw,300px);top:clamp(46px,5vh,64px)}
  /* A portrait screen has no band of sky to the observer's right, so the claim
     goes underneath him and centred, straddling the join into the film's own
     sky — which it can, because the two are now one colour. The offset is the
     bank's own height (0.72 of its width) rather than a guess. */
  .hero-say{
    left:clamp(16px,5vw,30px);right:clamp(16px,5vw,30px);width:auto;
    top:calc(var(--wm-top) + .72 * clamp(190px,44vw,300px) + clamp(8px,1.4vh,18px));
    text-align:center;
  }
  /* same trick as the desktop rule, against the column instead of the band: the
     line does not wrap, so its size is whatever will fit between the margins */
  .hs-line{font-size:min(32px,calc(100vw - 2 * clamp(16px,5vw,30px)) / 15.3)}
  .hs-sub{margin-left:auto;margin-right:auto;max-width:26em}
  .hs-cue{justify-content:center}
  .fp{--d:44px}
  .fp .fp-tag{font-size:9px;letter-spacing:.12em;padding:4px 9px 3px}
}
/* a phone's city is barely 200px tall; a 44px node with its halo would sit on
   it like a sticker rather than mark a place inside it */
@media (max-width:560px){
  .fp{--d:30px}
  .fp .fp-tag{font-size:8.5px;letter-spacing:.1em}
  /* the second line is the one that can go: the headline still states the claim
     and the cue still says what to do with it */
  .hs-sub{display:none}
}

/* ══════════════════════════ SECTION SHELL ══════════════════════════ */
.sec{position:relative;padding:clamp(84px,10vw,150px) 0;isolation:isolate}

/* Every section is tinted a little differently, and a tint that stops dead at
   the section's edge draws a line clean across the page — the boundaries used
   to read as rules, not as changes of key. So no section paints its own
   background any more: the tint goes on a layer behind the content that is
   masked away over the first and last stretch of the section, leaving the
   paper underneath to carry the seam. Two neighbours then dissolve into each
   other over ~2x the fade rather than meeting at a hard step.
   --ft / --fb are per-section escapes for the one pair (apps → cite) whose
   colours already agree at the join and which therefore wants no fade. */
:root{--sec-fade:clamp(80px,11vw,190px)}
.sec::before{
  content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;
  --ft:var(--sec-fade);--fb:var(--sec-fade);
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 var(--ft),
    #000 calc(100% - var(--fb)),transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 var(--ft),
    #000 calc(100% - var(--fb)),transparent 100%);
}
.wrap{width:min(var(--wrap),calc(100% - clamp(28px,6vw,80px)));margin-inline:auto;position:relative;z-index:2}
.wrap-wide{width:min(var(--wrap-w),calc(100% - clamp(24px,5vw,64px)))}
/* Fixed, not stretched over the section. #results is 4500px tall, and at the
   lattice pitch the field wants that would be ~17k dots and a 110MB backing
   store for one section. Pinned to the viewport the canvas is always ~1500x950
   whatever it sits in; the lattice is drawn in page coordinates so it still
   reads as glued to the page, and clip-path keeps each one inside its own
   section so neighbours do not paint over each other. */
/* z-index -1 puts the field above the section's tint (.sec::before, also -1 but
   earlier in paint order) and below every normal-flow block, so any panel with a
   background of its own — the carousel, cards, tables — masks it out on its own */
.bg-dots{position:fixed;inset:0;width:100%;height:100%;z-index:-1;pointer-events:none}

.sec-head{margin-bottom:clamp(44px,5vw,72px)}
.kicker{
  display:flex;align-items:center;gap:12px;margin:0 0 16px;
  font-family:var(--ff-m);font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--ink-3);
}
.kicker i{
  font-style:normal;display:grid;place-items:center;width:26px;height:26px;border-radius:50%;
  border:1px solid var(--line-2);color:var(--gold-3);font-size:10.5px;letter-spacing:0;
  background:linear-gradient(160deg,var(--white),var(--paper-2));
}
/* Every section title is one line. The size below is only a ceiling: initTitles()
   measures each one and steps it down until it fits its column, because the
   titles differ in length and a single clamp() that suited the longest would
   leave the short ones looking timid. nowrap is what makes that measurement
   honest — without it the range width is the wrapped width. */
.sec-title{
  margin:0;font-family:var(--ff-d);font-weight:400;letter-spacing:-.032em;
  font-size:clamp(2.05rem,4.4vw,3.5rem);line-height:1.06;white-space:nowrap;
  font-variation-settings:"SOFT" 0,"WONK" 1,"opsz" 90;
}
.sec-title em{
  font-style:italic;color:var(--gold-3);
  font-variation-settings:"SOFT" 40,"WONK" 1;
}
/* the lede runs as one continuous block across the full measure */
.lede{
  margin:20px 0 0;font-size:clamp(15.5px,1.35vw,18px);color:var(--ink-2);
  max-width:1180px;text-wrap:pretty;
}
/* in the wide sections the measure *is* the container; text-wrap:pretty is what
   pulls the last words down a line early, so it goes too */
.wrap-wide .lede{max-width:none;text-wrap:wrap}
.lede em{font-style:italic;color:var(--ink)}
.lede b{font-weight:600}
.u-wm{color:var(--phy-dk)}.u-tg{color:var(--coral)}

/* reveal */
.rv{opacity:0;transform:translateY(22px);transition:opacity .9s cubic-bezier(.16,1,.3,1),transform .9s cubic-bezier(.16,1,.3,1)}
.rv.in{opacity:1;transform:none}

/* ══════════════════════════ PREMISE ══════════════════════════ */
.sec-premise::before{background:linear-gradient(180deg,var(--paper) 0%,var(--paper-2) 46%,var(--paper) 100%)}
/* Wider than any text measure on the page, because the film is the section and
   everything else is a label on it — but not full-bleed: at 16/9 the extra width
   costs height, and the whole frame has to be on screen at once. The head shares
   this container rather than .wrap so the kicker sits on the film's left edge. */
.pf-wrap{
  width:min(1360px,78%);
  margin-inline:auto;position:relative;z-index:2;
}
.pf-wrap .sec-head{margin-bottom:clamp(12px,1.5vw,20px)}
.pfilm{margin:0}
.pf-frame{
  /* the control cluster's footprint, written down rather than guessed: the caption
     is laid out around it, so the two have to move together */
  --pf-inset:clamp(11px,1.2vw,20px);
  --pf-bot:clamp(13px,1.4vw,22px);
  --pf-btn:clamp(30px,2.3vw,36px);
  --pf-ctl-w:calc(2 * var(--pf-btn) + 16px);
  --pf-ctl-h:calc(var(--pf-btn) + 12px);
  position:relative;aspect-ratio:16/9;overflow:hidden;
  border-radius:var(--r-l);border:1px solid var(--line);box-shadow:var(--sh-2);
  /* the film is drawn on cream paper, so an unloaded frame should be cream too
     rather than the black rectangle a video player would default to */
  background:var(--paper-3);
}
.pf-video{display:block;width:100%;height:100%;object-fit:cover}

/* The frame itself is the transport — a film that fills the screen does not need
   a play button to tell you it can be stopped. A real <button> rather than a
   click handler on the video, so it is reachable from the keyboard. */
.pf-tap{
  position:absolute;inset:0;z-index:1;display:grid;place-items:center;
  cursor:pointer;background:none;
}
.pf-cue{
  display:grid;place-items:center;color:#fff;
  width:clamp(56px,5.4vw,80px);aspect-ratio:1;border-radius:50%;
  background:rgba(24,18,9,.42);border:1px solid rgba(255,255,255,.2);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  opacity:0;transform:scale(.86);
  transition:opacity .28s,transform .34s cubic-bezier(.2,.8,.2,1);
}
.pf-cue svg{width:38%;height:38%;margin-left:8%}
/* only while stopped: the glyph is the answer to "is it paused or just still?" */
.pf-frame.is-paused .pf-cue{opacity:1;transform:none}

/* bottom right, over the frame: sound and repeat, and nothing else */
.pf-ctl{
  position:absolute;z-index:2;right:calc(var(--pf-inset) + clamp(12px,1.7vw,28px));bottom:var(--pf-bot);
  display:flex;gap:4px;padding:5px;border-radius:100px;
  background:rgba(24,18,9,.44);border:1px solid rgba(255,255,255,.15);
  -webkit-backdrop-filter:blur(12px) saturate(1.3);backdrop-filter:blur(12px) saturate(1.3);
}
.pf-btn{
  position:relative;width:clamp(30px,2.3vw,36px);aspect-ratio:1;border-radius:50%;
  display:grid;place-items:center;color:rgba(255,255,255,.88);
  transition:background .25s,color .25s,transform .18s;
}
.pf-btn:hover{background:rgba(255,255,255,.17);color:#fff}
.pf-btn:active{transform:scale(.93)}
.pf-btn svg{width:60%;height:60%;display:block}
.pf-i-off{display:none}
.pf-sound.is-muted .pf-i-on{display:none}
.pf-sound.is-muted .pf-i-off{display:block}
/* the film is narrated, and muted autoplay gives no sign of that */
.pf-sound.is-muted::after{
  content:"";position:absolute;top:3px;right:3px;width:5px;height:5px;
  border-radius:50%;background:var(--gold);box-shadow:0 0 0 1.5px rgba(24,18,9,.5);
}
.pf-loop{color:rgba(255,255,255,.5)}
.pf-loop.is-on{color:var(--gold);background:rgba(255,255,255,.14)}

/* The narration, drawn from a cue list rather than burned into the file. The film
   autoplays muted, so for almost every reader this is the only voice it has: one
   line at a time, on its own plate, because the frame is cream watercolour and
   white type laid straight onto it would not be readable. Inset by the width of
   the control cluster on both sides, so it stays centred and still clears the
   buttons; the plate is only as wide as the line, so it covers almost nothing. */
.pf-sub{
  position:absolute;z-index:2;pointer-events:none;
  bottom:var(--pf-bot);
  left:calc(var(--pf-inset) + var(--pf-ctl-w) + 12px);
  right:calc(var(--pf-inset) + var(--pf-ctl-w) + 12px);
  display:flex;justify-content:center;
}
.pf-sub span{
  max-width:100%;padding:.34em .68em;border-radius:8px;
  font-family:var(--ff-b);font-weight:400;
  font-size:clamp(15px,1.35vw,21px);line-height:1.34;letter-spacing:-.005em;
  color:#fff;text-align:center;text-wrap:balance;
  background:rgba(20,15,7,.5);
  -webkit-backdrop-filter:blur(10px) saturate(1.25);backdrop-filter:blur(10px) saturate(1.25);
  border:1px solid rgba(255,255,255,.13);
  text-shadow:0 1px 2px rgba(20,15,7,.45);
  opacity:0;transform:translateY(5px);
  transition:opacity .24s ease,transform .3s cubic-bezier(.2,.8,.2,1);
}
.pf-sub.is-on span{opacity:1;transform:none}
/* on a phone the frame is small enough that inset-both-sides leaves no measure at
   all, so the line starts at the left edge and only the buttons are cleared */
@media (max-width:760px){
  .pf-sub{left:var(--pf-inset);justify-content:flex-start}
  /* the frame is ~200px tall here: a third line of caption would be a third of it */
  .pf-sub span{text-align:left;font-size:clamp(12.5px,3.5vw,15px)}
}

.pf-line{position:absolute;z-index:2;left:0;right:0;bottom:0;height:2px;background:rgba(255,255,255,.14)}
.pf-line i{display:block;height:100%;width:0;background:var(--gold)}

/* the claim, above the frame and on the frame's own measure — it is one sentence
   and there is no reason for it to break before the film's right edge does */
.pf-cap{
  margin:0 0 clamp(13px,1.6vw,22px);
  font-family:var(--ff-d);font-weight:300;
  font-size:clamp(1rem,1.3vw,1.28rem);line-height:1.45;letter-spacing:-.014em;
  color:var(--ink-2);text-wrap:pretty;
}
.pf-cap em{font-style:italic;color:var(--ment-dk)}

/* ══════════════════ THE FRAMEWORK — figure 1, in motion ══════════════════
   A 1:1 redraw of the teaser figure as a scrolling strip. Colours, radii and
   proportions are sampled off fig1-teaser.jpg rather than invented, so the page
   and the paper show the same picture. Panel internals are written in em and
   .mp's font-size is one thirtieth of a Target column, which makes the whole
   thing one drawing that scales from a single number set in mwm.js. */
.sec-loop{padding-bottom:clamp(70px,8vw,120px)}
/* pushed off the top edge: at 0% the radial's brightest point sat exactly on the
   join, which is the worst place for it even with the fade */
.sec-loop::before{background:radial-gradient(120% 62% at 50% 12%,var(--paper-2),transparent 62%)}

.mwm{
  /* sampled from the figure */
  --wm-ban:#0754B4;
  --wm-line:#8DACDF;
  --wm-fill:#EFF7FE;
  --wm-ink:#0B4FA8;
  --ob-line:#3B8D93;
  --ob-fill:#E8F1F2;
  --ob-ink:#1C6E71;
  --tg-ban:#D77A79;
  --tg-line:#DE8F8F;
  --tg-fill:#F9F0EF;
  --tg-ink:#C0504E;
  --fx-line:#6F309F;
  --ff-c:"Comic Sans MS","Chalkboard SE","Comic Neue","Trebuchet MS",sans-serif;

  /* the vertical rhythm of a panel, named once so the three operator arrows can
     be centred on exactly the band the cells occupy rather than on the panel */
  --bx-pt:3.35em;
  --bx-pb:1.05em;
  --bx-sub:3.2em;
  --bx-bd:.22em;

  /* the panel scales off its own width, so narrowing the block is also what
     shortens it — 75vw is the widest the strip reads as one screenful */
  width:min(var(--wrap-w),75%);
  margin:0 auto clamp(40px,5vw,70px);
}
.mwm-wrap{
  border-radius:var(--r-xl);padding:clamp(14px,1.5vw,24px);
  background:var(--paper-2);border:1px solid var(--line);box-shadow:var(--sh-3);
}
.mwm-flat{display:none;margin:0}
.mwm-flat img{width:100%;height:auto;display:block;border-radius:var(--r-l);
  border:1px solid var(--line);box-shadow:var(--sh-2)}
.mwm-flat figcaption{margin-top:10px;font-size:14px;color:var(--ink-3);text-align:center}
/* the strip is wider than this; the overflow is the point */
.mwm-view{overflow:hidden;padding:calc(var(--tg) / 30 * 3.2) 0 0}
.mwm-track{
  display:flex;align-items:stretch;will-change:transform;
  transition:transform .9s cubic-bezier(.66,0,.2,1);
}

/* ─── panels ─── */
.mp{
  flex:0 0 auto;position:relative;
  font-size:max(7px,calc(var(--tg) / 30));
  font-family:"Trebuchet MS","Segoe UI",system-ui,sans-serif;
}
.mp-wm{width:var(--wm)}
.mp-tg{width:var(--tg)}
.mp-box{
  height:100%;display:flex;flex-direction:column;
  border-radius:1.7em;border:var(--bx-bd) solid var(--wm-ban);background:#fff;
  padding:var(--bx-pt) .8em var(--bx-pb);
}
.mp-tg .mp-box{border-color:var(--tg-line)}

/* the badge and banner straddle the top edge of the box, as in the figure */
.mp-hd{
  position:absolute;left:1.4em;right:1.4em;top:0;transform:translateY(-52%);
  display:flex;align-items:center;z-index:3;pointer-events:none;
}
.mp-hex{flex:0 0 auto;width:5.4em;margin-right:-1.4em;z-index:2;
  filter:drop-shadow(.06em .16em .3em rgba(20,30,60,.3))}
/* the crop is the badge's own bounding box, measured off the figure (391x335),
   and the flat top runs 21.7%-78.3% of it — a regular hexagon's 25/75 cut the
   white inner outline off on the point side */
.mp-hex img{width:100%;aspect-ratio:389/335;object-fit:cover;
  clip-path:polygon(21.7% 0,78.3% 0,100% 50%,78.3% 100%,21.7% 100%,0 50%)}
.mp-ban{
  flex:0 0 auto;display:flex;align-items:center;gap:.7em;white-space:nowrap;
  background:var(--wm-ban);color:#fff;
  font-size:1.9em;font-weight:700;line-height:1.1;letter-spacing:.004em;
  padding:.3em .55em .3em 1.05em;
  clip-path:polygon(0 0,calc(100% - .78em) 0,100% 50%,calc(100% - .78em) 100%,0 100%,.4em 50%);
}
.mp-tg .mp-ban{background:var(--tg-ban)}
.mp-dots{
  flex:0 0 auto;width:2.6em;height:1.3em;
  background-image:radial-gradient(circle at 50% 50%,rgba(255,255,255,.8) 30%,transparent 33%);
  background-size:.52em .44em;
  -webkit-mask-image:linear-gradient(90deg,rgba(0,0,0,.2),#000);
  mask-image:linear-gradient(90deg,rgba(0,0,0,.2),#000);
}
/* not in the figure: the figure prints t and t+1 side by side, so it never has
   to say which tick you are looking at. A scrolling strip does. */
.mp-tick{
  margin-left:.9em;background:#fff;border:.11em solid var(--wm-line);color:var(--wm-ink);
  border-radius:2em;padding:.06em .75em;font-size:1.35em;line-height:1.5;
  box-shadow:0 .1em .3em rgba(20,30,60,.14);
}
.mp-tg .mp-tick{border-color:var(--tg-line);color:var(--tg-ink)}

/* fixed height in both boxes, so the cells below start at the same y and the
   three operator arrows can be lined up by arithmetic instead of by eye */
.mp-sub{
  /* --bx-sub is written in the panel's em; this element is 1.6× that, so the
     height has to be divided back down to stay the same physical band. The band
     is fixed but the line sits at the top of it: this is a subtitle to the
     banner above, not a heading for the cells below. */
  margin:-.5em 0 0;height:calc(var(--bx-sub) / 1.6 + .5em);
  display:flex;align-items:flex-start;justify-content:center;
  text-align:center;color:var(--wm-ban);
  font-size:1.6em;font-weight:700;line-height:1.2;
}
.mp-who{gap:.42em;color:var(--tg-ink);font-style:italic}
.mp-who img{width:1.75em;height:1.75em;border-radius:50%;object-fit:cover;
  border:.1em solid #fff;box-shadow:0 .06em .24em rgba(60,30,30,.3)}

/* ─── the 2×2 grid, with Ω living in its gutter ─── */
.mp-grid{
  flex:1 1 auto;display:grid;
  grid-template-columns:1fr var(--omg) 1fr;
  grid-template-rows:auto auto;
  row-gap:.45em;
}
.mp-grid [data-cell=ms]{grid-column:1;grid-row:1}
.mp-grid [data-cell=mo]{grid-column:3;grid-row:1}
.mp-grid [data-cell=ps]{grid-column:1;grid-row:2}
.mp-grid [data-cell=po]{grid-column:3;grid-row:2}
.mp-grid .conn-om{grid-column:2;grid-row:1 / 3;align-self:center}
.mp-stack{flex:1 1 auto;display:flex;flex-direction:column;gap:.5em}
.mp-stack .cell{flex:1 1 0}

.cell{
  display:flex;flex-direction:column;min-width:0;
  border-radius:1.15em;border:.19em solid var(--wm-line);background:var(--wm-fill);
  padding:.85em 1.05em .95em;
}
.c-ob{border-color:var(--ob-line);background:var(--ob-fill)}
.c-ac{border-color:var(--tg-line);background:var(--tg-fill)}

.cl-t{
  margin:0 0 .34em;display:flex;flex-direction:column;align-items:center;gap:.12em;
  text-align:center;font-size:1.44em;font-weight:700;line-height:1.14;color:var(--wm-ink);
}
.c-ob .cl-t{color:var(--ob-ink)}
.c-ac .cl-t{color:var(--tg-ink)}
.cl-t small{font-size:.74em;font-weight:600}
.mp-chip{
  display:inline-block;background:#fff;border:.075em solid var(--wm-line);
  border-radius:2em;padding:.02em .8em;font-size:.92em;color:var(--ink);
}
.c-ob .mp-chip{border-color:var(--ob-line)}
.c-ac .mp-chip{border-color:var(--tg-line)}

/* the frames are always drawn; only what fills them waits its turn */
.cl-b{
  flex:1 1 auto;display:flex;flex-direction:column;gap:.7em;justify-content:center;
  opacity:.12;filter:saturate(.12);transition:opacity .8s ease,filter .8s ease;
}
[data-cell=ms] .cl-b{gap:1.5em}
.cell.is-on .cl-b{opacity:1;filter:none}

/* ─── mental rows: a face and a thought ─── */
.cr{display:flex;align-items:center;gap:.55em;min-width:0}
/* the inferred-observation cell is one face against one thought, so the caption
   sits above the face and the bubble takes the whole right column — it is given
   the full height rather than its text height, or the single cloud reads as a
   leftover of the two it replaced */
.cr-obs{display:grid;grid-template-columns:auto 1fr;grid-template-rows:auto 1fr;
  column-gap:.55em;align-items:center;flex:1 1 auto}
.cr-lb{grid-area:1/1;justify-self:start;align-self:end;padding-bottom:.15em;
  font-size:1.24em;font-style:italic;font-weight:700;color:var(--ob-ink);white-space:nowrap}
.cr-obs .fc{grid-area:2/1;align-self:start}
/* stretched to the cell but the cloud stops short of filling it: at the cell's
   full height it comes out square, and a square cloud reads as a balloon */
.cr-bs{grid-area:1/2 / span 2;align-self:stretch;justify-content:center;
  display:flex;flex-direction:column;gap:.5em;min-width:0}
.cr-bs .cb{flex:0 0 auto;height:84%}
.fc{flex:0 0 auto;width:5.4em;text-align:center}
.fc img{width:5.4em;height:5.4em;border-radius:50%;object-fit:cover;display:block}
.fc b{display:block;margin-top:.2em;font-size:1.3em;font-style:italic;color:var(--wm-ink)}
.f-ob b{color:var(--ob-ink)}

/* the padding is the scallop's room, not a typographic choice: the bumps reach
   ~1.5em inside the box edge, so anything less and the wave cuts into the text */
.cb{position:relative;flex:1 1 auto;min-width:0;padding:1.75em 1.7em;display:flex;align-items:center}
.cb-bg{position:absolute;inset:0;width:100%;height:100%}
.cb-bg path{fill:#fff;stroke:#AEBFC4;stroke-width:1.3;vector-effect:non-scaling-stroke}
.cb-in{position:relative;margin:0;font-family:var(--ff-c);font-size:1.16em;line-height:1.3;color:#2B3036}
.cb-d1,.cb-d2{position:absolute;border-radius:50%;background:#fff;
  border:1px solid #AEBFC4;box-sizing:border-box}
.cb-d1{width:.8em;height:.8em;left:.2em;top:-.02em}
.cb-d2{width:.56em;height:.56em;left:-.42em;top:-.4em}
/* the merged cloud is tall enough that its top-left corner is above the head it
   belongs to, so the trail comes off the left flank instead, at face height */
.cr-bs .cb-d1{left:-.5em;top:36%}
.cr-bs .cb-d2{left:-1.3em;top:29%}

/* ─── physical rows: a clip and its caption ─── */
/* All four physical cells are shaped the same way — picture over caption — so
   reading one is the same motion as reading the next. 16/9 is the clips' own
   frame: anything else would crop a composition that was directed to fill it. */
.sh{display:flex;flex-direction:column;align-items:stretch;gap:.45em;
  flex:1 1 auto;min-width:0}
.sh-pic{
  position:relative;flex:0 0 auto;width:88%;margin:0 auto;aspect-ratio:16/9;
  border-radius:.4em;overflow:hidden;border:.16em solid #fff;background:#0D0F12;
  box-shadow:0 .12em .4em rgba(20,30,60,.24);
}
.sh-pic img,.sh-pic video{display:block;width:100%;height:100%;object-fit:cover}
.sh-pic video{cursor:pointer}
/* the only control these clips have: tapping the picture holds the step, and
   the glyph is what says the strip is waiting rather than broken */
.sh-pic.is-held::after{
  content:"";position:absolute;left:50%;top:50%;width:1.6em;height:2em;
  transform:translate(-50%,-50%);pointer-events:none;border-radius:.08em;
  background:
    linear-gradient(#fff,#fff) left  / 34% 100% no-repeat,
    linear-gradient(#fff,#fff) right / 34% 100% no-repeat;
  filter:drop-shadow(0 .06em .34em rgba(0,0,0,.55));
}
.sh-cap{margin:0;flex:0 0 auto;min-width:0;font-family:var(--ff-c);
  font-size:1.18em;line-height:1.3;color:#3A3E44;text-align:center}

/* ─── the three operators ─── */
.conn{
  flex:0 0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:.3em;opacity:.34;transition:opacity .5s ease;
}
/* Ω is centred on the grid inside the box; π and Γ sit outside it, so they are
   inset by the box's own head and foot to be centred on the same band. Without
   this the three arrows land at three different heights. */
.conn-pi,.conn-ga{
  width:var(--gap);font-size:max(7px,calc(var(--tg) / 30));
  padding:calc(var(--bx-pt) + var(--bx-sub) + var(--bx-bd)) 0 calc(var(--bx-pb) + var(--bx-bd));
}
.conn.is-hot{opacity:1}
.cn-step{font-size:1.3em;font-weight:700;line-height:1.1;color:#12437F}
.conn-pi .cn-step{color:#C2453F}
.conn-ga .cn-step{color:#1657B8}
/* fixed em width, not a share of the column: Ω's column is wider than π's and
   Γ's, so a percentage drew one arrow bigger and pushed its label off the band */
.cn-arw{display:block;width:4.7em;aspect-ratio:100/56;
  clip-path:polygon(0 26%,58% 26%,58% 0,100% 50%,58% 100%,58% 74%,0 74%)}
.conn-om .cn-arw{background:linear-gradient(90deg,#D9EDEA,#3E9A8F)}
.conn-pi .cn-arw{background:linear-gradient(90deg,#F9DEDD,#DF817D)}
.conn-ga .cn-arw{background:linear-gradient(90deg,#CFDFF7,#1F63C4)}
.cn-sym{font-size:1.95em;line-height:1;color:#2E8B84}
.conn-pi .cn-sym{color:#D9756F}
.conn-ga .cn-sym{color:#1F63C4}
.conn.is-hot .cn-arw{animation:cn-push 1.15s ease-in-out infinite}
@keyframes cn-push{0%,100%{transform:translateX(-.16em)}50%{transform:translateX(.2em)}}

/* ─── the transition being applied, with live subscripts ─── */
/* One card, three columns: the operator, its equations, and the one line of
   prose that used to be repeated in a legend under the strip. */
.mwm-fx{
  margin-top:clamp(12px,1.4vw,20px);
  display:grid;grid-template-columns:repeat(3,1fr);
  border-radius:16px;overflow:hidden;
}
.fxg{
  display:grid;grid-template-columns:auto 1fr;align-content:start;
  column-gap:clamp(7px,.8vw,12px);row-gap:5px;min-width:0;
  padding:clamp(10px,1.1vw,15px) clamp(11px,1.2vw,17px);
  opacity:.34;filter:saturate(.3);transition:opacity .5s ease,filter .5s ease;
}
.fxg.is-hot{opacity:1;filter:none;background:var(--white)}
.fxg-sym{
  grid-area:1 / 1;align-self:start;
  display:grid;place-items:center;width:2.3em;height:2.3em;border-radius:50%;
  font-size:clamp(12.5px,1.05vw,15.5px);color:#fff;background:#3E9A8F;box-shadow:var(--sh-1);
}
.fxg[data-op=pi] .fxg-sym{background:#E8827F}
.fxg[data-op=ga] .fxg-sym{background:#1F63C4}
.fxg-t{grid-area:1 / 2;min-width:0}
.fxg-step{display:block;font-family:var(--ff-m);font-size:clamp(9px,.72vw,10.5px);
  letter-spacing:.14em;text-transform:uppercase;font-weight:600;color:#2E8B84}
.fxg[data-op=pi] .fxg-step{color:#D9756F}
.fxg[data-op=ga] .fxg-step{color:#1F63C4}
.fxg-n{
  margin:1px 0 0;font-family:"Trebuchet MS","Segoe UI",system-ui,sans-serif;
  font-size:clamp(12px,1vw,15px);font-weight:700;line-height:1.2;color:var(--wm-ink);
}
.fxg[data-op=pi] .fxg-n{color:var(--tg-ink)}
.fxg-n small{font-size:.86em;font-weight:600;opacity:.8}
.fxg-r{grid-area:2 / 2;min-width:0}
.fxg-r .fx{margin:.16em 0;font-size:clamp(11px,.94vw,14.5px);color:var(--ink);white-space:nowrap}
.fxg-d{
  grid-area:3 / 1 / 4 / 3;margin:4px 0 0;
  font-size:clamp(11.5px,.86vw,13px);line-height:1.5;color:var(--ink-3);
}

/* transport, above the strip and out of the way of it */
.mwm-ctrl{display:flex;align-items:center;justify-content:flex-end;gap:8px;
  margin:0 0 clamp(8px,1vw,14px)}
.tc-btn{
  width:34px;height:34px;border-radius:50%;display:grid;place-items:center;border:1px solid var(--line);
  background:var(--white);box-shadow:var(--sh-1);transition:.3s;
}
.tc-btn:hover{border-color:var(--gold-2);transform:translateY(-1px)}
.tc-btn svg{width:17px;height:17px;fill:var(--ink-2)}
.tc-btn .i-play{display:none}
.tc-btn.is-paused .i-play{display:block}
.tc-btn.is-paused .i-pause{display:none}

/* ══════════════════════════ TAXONOMY ══════════════════════════ */
.sec-state::before{background:linear-gradient(180deg,var(--paper),var(--paper-2) 50%,var(--paper))}
.tax-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(16px,2.4vw,30px);align-items:start}
.tax-col{
  border-radius:var(--r-l);padding:clamp(16px,2vw,26px);background:var(--white);
  border:1px solid var(--line);box-shadow:var(--sh-1);
}
.tax-col[data-side=phy]{background:linear-gradient(170deg,var(--phy-lt2),var(--white) 55%)}
.tax-col[data-side=ment]{background:linear-gradient(170deg,var(--ment-lt2),var(--white) 55%)}
.tax-h{display:flex;align-items:center;gap:12px;padding-bottom:14px;margin-bottom:6px;border-bottom:1px solid var(--line)}
.tax-h .tx-sym{font-family:var(--ff-x);font-style:italic;font-size:23px}
.tax-col[data-side=phy] .tx-sym{color:var(--phy-dk)}
.tax-col[data-side=ment] .tx-sym{color:var(--ment-dk)}
.tax-h b{font-family:var(--ff-d);font-size:19px;font-weight:500;letter-spacing:-.02em}

.tnode{border-bottom:1px solid rgba(226,217,198,.6)}
.tnode:last-child{border-bottom:0}
.tnode-h{
  width:100%;display:flex;align-items:center;gap:10px;padding:12px 4px;text-align:left;
  font-size:14.8px;letter-spacing:-.01em;transition:.3s;
}
.tnode-h:hover{color:var(--gold-3)}
.tn-tw{
  flex:0 0 auto;width:16px;height:16px;border-radius:5px;display:grid;place-items:center;
  border:1px solid var(--line-2);font-size:11px;line-height:1;color:var(--ink-3);transition:.35s cubic-bezier(.2,.7,.2,1);
}
.tnode.open .tn-tw{transform:rotate(45deg);border-color:var(--gold-2);color:var(--gold-3)}
.tn-n{margin-left:auto;font-family:var(--ff-m);font-size:10px;color:var(--ink-4)}
.tnode-b{display:grid;grid-template-rows:0fr;transition:grid-template-rows .5s cubic-bezier(.2,.7,.2,1)}
.tnode.open .tnode-b{grid-template-rows:1fr}
.tnode-b>div{overflow:hidden}
.tleaf{padding:0 0 12px 26px;display:flex;flex-direction:column;gap:9px}
.tl{padding:9px 12px;border-radius:10px;background:rgba(255,255,255,.75);border:1px solid var(--line)}
.tl-k{font-size:12.8px;font-weight:600;color:var(--ink);letter-spacing:-.005em}
.tl-k span{display:inline-block;padding:0 2px}
.tl-v{margin-top:3px;font-family:var(--ff-m);font-size:11.4px;color:var(--ink-3);line-height:1.5}
.tl-v em{font-style:normal;color:var(--gold-3)}

.prop-box{
  margin-top:clamp(28px,3.4vw,44px);border-radius:var(--r-l);overflow:hidden;
  border:1px solid var(--line);background:var(--white);box-shadow:var(--sh-1);
}
.pb-tag{
  margin:0;padding:11px 22px;font-family:var(--ff-m);font-size:10.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--white);background:linear-gradient(90deg,var(--phy-dk),var(--ment-dk));
}
.pb-body{padding:20px 22px 22px}
.pb-body p{margin:0 0 10px;font-size:15px;color:var(--ink-2)}
.pb-body p:last-child{margin-bottom:0;margin-top:12px}
.pb-math{text-align:center;padding:8px 0 4px;font-size:17px;overflow-x:auto}

/* ══════════════════════════ MENTIS PIPELINE ══════════════════════════ */
.sec-mentis::before{background:
  radial-gradient(90% 52% at 12% 12%,var(--phy-lt2),transparent 62%),
  radial-gradient(80% 50% at 90% 34%,var(--ment-lt2),transparent 62%),var(--paper)}
.pipe{display:grid;grid-template-columns:300px 1fr;gap:clamp(18px,2.4vw,34px);align-items:start}
.pipe-rail{display:flex;flex-direction:column;gap:6px;position:sticky;top:86px}
.pstep{
  display:flex;gap:12px;align-items:flex-start;padding:13px 14px;border-radius:14px;text-align:left;
  border:1px solid transparent;transition:.4s cubic-bezier(.2,.7,.2,1);position:relative;
}
.pstep::before{
  content:"";position:absolute;left:26px;top:44px;bottom:-6px;width:1px;
  background:linear-gradient(180deg,var(--line-2),transparent);
}
.pstep:last-child::before{display:none}
.pstep:hover{background:rgba(255,255,255,.72)}
.pstep.is-on{background:var(--white);border-color:var(--line);box-shadow:var(--sh-1)}
.ps-n{
  flex:0 0 auto;width:29px;height:29px;border-radius:50%;display:grid;place-items:center;
  font-family:var(--ff-m);font-size:12px;font-weight:600;color:var(--ink-3);
  background:var(--paper-3);border:1px solid var(--line);transition:.4s;
}
.pstep.is-on .ps-n{background:linear-gradient(150deg,var(--gold-2),var(--gold));color:#fff;border-color:transparent;box-shadow:0 4px 12px -4px rgba(192,145,47,.7)}
.ps-tx{display:block;min-width:0}
.ps-t{font-size:14.6px;letter-spacing:-.012em;font-weight:500;display:block}
.ps-d{font-size:12.4px;color:var(--ink-3);display:block;margin-top:1px}
.ps-sym{font-family:var(--ff-x);font-style:italic;color:var(--ink-4);font-size:13px;margin-left:6px}

.pipe-panel{
  border-radius:var(--r-l);border:1px solid var(--line);background:var(--white);
  box-shadow:var(--sh-2);overflow:hidden;min-height:520px;
}
.pp-head{padding:18px 22px 14px;border-bottom:1px solid var(--line);background:linear-gradient(120deg,var(--paper-2),var(--white))}
.pp-k{font-family:var(--ff-m);font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--gold-3)}
.pp-h{margin:6px 0 0;font-family:var(--ff-d);font-size:22px;font-weight:500;letter-spacing:-.024em}
.pp-sub{margin:6px 0 0;font-size:14px;color:var(--ink-3);max-width:70ch}
.pp-body{padding:20px 22px 24px;animation:ppIn .55s cubic-bezier(.16,1,.3,1)}
@keyframes ppIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

.pp-note{
  margin:14px 0 0;padding:11px 14px;border-radius:12px;font-size:13.2px;line-height:1.58;
  color:var(--ink-2);background:var(--paper-2);border-left:2.5px solid var(--gold-2);
}
.pp-note b{font-weight:600;color:var(--ink)}
.pp-note em{font-style:italic}
.art{border-radius:14px;border:1px solid var(--line);overflow:hidden;margin-bottom:12px;background:var(--paper)}
.art:last-child{margin-bottom:0}
.art-h{
  display:flex;align-items:center;gap:9px;padding:9px 14px;background:var(--paper-2);
  border-bottom:1px solid var(--line);font-family:var(--ff-m);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3);
}
.art-h .dot{width:7px;height:7px;border-radius:50%}
.art-h .sym{font-family:var(--ff-x);font-style:italic;text-transform:none;letter-spacing:0;font-size:13px;color:var(--ink-2)}
.art-b{padding:12px 14px;font-size:13.4px;line-height:1.56;color:var(--ink-2)}
.art-b .kv{display:grid;grid-template-columns:118px 1fr;gap:4px 14px}
.art-b .kv dt{font-family:var(--ff-m);font-size:11px;color:var(--ink-4);letter-spacing:.03em;padding-top:2px}
.art-b .kv dd{margin:0}
.art.a-phy .art-h .dot{background:var(--phy)}
.art.a-ment .art-h .dot{background:var(--ment)}
.art.a-obs .art-h .dot{background:var(--teal)}
.art.a-act .art-h .dot{background:var(--coral)}

.opts{display:flex;flex-direction:column;gap:7px}
.opt{
  display:grid;grid-template-columns:26px 1fr auto;gap:11px;align-items:center;
  padding:10px 13px;border-radius:12px;border:1px solid var(--line);background:var(--paper);
  text-align:left;width:100%;transition:.35s cubic-bezier(.2,.7,.2,1);
}
.opt:hover{background:var(--white);border-color:var(--line-2);transform:translateX(2px)}
.opt.is-sel{background:linear-gradient(100deg,#EAF7EF,var(--white));border-color:rgba(46,139,87,.45);box-shadow:0 8px 22px -14px rgba(46,139,87,.8)}
.opt.is-open{background:var(--white);border-color:var(--gold-2)}
.o-l{
  width:26px;height:26px;border-radius:8px;display:grid;place-items:center;font-family:var(--ff-m);
  font-size:12px;font-weight:600;background:var(--paper-3);color:var(--ink-2);
}
.opt.is-sel .o-l{background:var(--green);color:#fff}
.o-t{font-size:13.2px;line-height:1.45}
.o-v{font-family:var(--ff-m);font-size:12px;color:var(--ink-3);display:flex;align-items:center;gap:8px}
.o-bar{width:54px;height:5px;border-radius:4px;background:var(--paper-3);overflow:hidden;position:relative}
.o-bar i{position:absolute;inset:0;transform-origin:0 50%;transform:scaleX(var(--v,0));background:linear-gradient(90deg,var(--gold-2),var(--gold));transition:transform .8s cubic-bezier(.16,1,.3,1)}
.opt.is-sel .o-bar i{background:linear-gradient(90deg,#6CC08B,var(--green))}
.opt.veto .o-v{color:var(--red)}
.o-detail{grid-column:1/-1;display:grid;grid-template-rows:0fr;transition:grid-template-rows .45s cubic-bezier(.2,.7,.2,1)}
.opt.is-open .o-detail{grid-template-rows:1fr}
.o-detail>div{overflow:hidden}
.o-in{padding-top:10px;display:grid;grid-template-columns:1fr 1fr;gap:10px}
.o-mini{border-radius:10px;border:1px solid var(--line);padding:8px 10px;background:var(--paper)}
.o-mini b{display:block;font-family:var(--ff-m);font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-4);margin-bottom:3px}
.o-mini p{margin:0;font-size:12.4px;line-height:1.45}
.o-scores{grid-column:1/-1;display:flex;gap:8px;flex-wrap:wrap;padding-top:2px}
.o-sc{display:flex;align-items:center;gap:7px;padding:5px 10px;border-radius:20px;background:var(--paper-2);font-family:var(--ff-m);font-size:11px}
.o-sc b{font-weight:600}
.o-sc.mc{color:var(--ment-dk)}.o-sc.pp{color:var(--phy-dk)}.o-sc.sa{color:var(--teal)}
.o-sc.vt{color:var(--red);background:#FCEDEA}

.fig-fold{margin-top:26px;border-radius:var(--r-m);border:1px solid var(--line);background:var(--white);overflow:hidden}
.fig-fold summary{padding:13px 18px;cursor:pointer;font-family:var(--ff-m);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);list-style:none}
.fig-fold summary::-webkit-details-marker{display:none}
.fig-fold summary::before{content:"+ ";color:var(--gold)}
.fig-fold[open] summary::before{content:"– "}
.fig-fold img{border-top:1px solid var(--line)}

/* ══════════════════════════ RESULTS ══════════════════════════ */
.sec-results::before{background:var(--paper)}
.findings{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-bottom:clamp(34px,4vw,56px)}
.fnd{
  padding:20px 20px 22px;border-radius:var(--r-l);border:1px solid var(--line);position:relative;overflow:hidden;
  background:linear-gradient(165deg,var(--white),var(--paper-2));box-shadow:var(--sh-1);
}
.fnd::after{content:"";position:absolute;left:0;top:0;height:3px;width:100%;background:linear-gradient(90deg,var(--gold),var(--gold-2),transparent)}
.fnd-n{font-family:var(--ff-m);font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-4)}
.fnd-big{
  display:block;margin:8px 0 6px;font-family:var(--ff-d);font-size:clamp(2rem,3.4vw,2.9rem);
  line-height:1;letter-spacing:-.04em;color:var(--gold-3);font-variation-settings:"SOFT" 20,"WONK" 1;
}
.fnd-big i{font-style:normal;font-size:.44em;color:var(--ink-3);letter-spacing:0;margin-left:5px}
.fnd p{margin:0;font-size:13.8px;color:var(--ink-2);line-height:1.52}
.fnd p b{font-weight:600;color:var(--ink)}

.chart-block,.table-block{
  margin-bottom:clamp(24px,3vw,40px);border-radius:var(--r-xl);border:1px solid var(--line);
  background:var(--white);box-shadow:var(--sh-1);overflow:hidden;
}
.cb-head{padding:22px clamp(18px,2.4vw,30px) 16px;border-bottom:1px solid var(--line);background:linear-gradient(120deg,var(--paper-2),var(--white))}
.cb-head h3{margin:0;font-family:var(--ff-d);font-size:clamp(1.25rem,2vw,1.6rem);font-weight:500;letter-spacing:-.028em;display:flex;align-items:center;gap:12px}
.cb-n{
  width:26px;height:26px;border-radius:8px;display:grid;place-items:center;font-family:var(--ff-m);
  font-size:12px;font-weight:600;color:#fff;background:linear-gradient(150deg,var(--ink-2),var(--ink));
}
.cb-head p{margin:8px 0 0;font-size:14px;color:var(--ink-3)}
.cb-head p b{color:var(--ink-2);font-weight:600}
.cb-body{display:grid;grid-template-columns:1fr 300px;gap:0}
.cb-body-2{grid-template-columns:1fr 1fr}
.cb-sub{display:grid;grid-template-columns:1fr 300px;border-top:1px solid var(--line)}
.cb-chart{padding:18px clamp(14px,2vw,24px) 20px;min-width:0}
.cb-body-2 .cb-chart+.cb-chart{border-left:1px solid var(--line)}
.cb-read{
  padding:20px;border-left:1px solid var(--line);background:linear-gradient(180deg,var(--paper-2),var(--white));
  font-size:13.6px;line-height:1.6;color:var(--ink-2);
}
.cb-read-wide{border-left:0;border-top:1px solid var(--line)}
.cr-k{font-family:var(--ff-m);font-size:9.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--gold-3);display:block;margin-bottom:7px}
.cb-read h4{margin:0 0 7px;font-size:15.5px;letter-spacing:-.015em;font-weight:600}
.cb-read p{margin:0 0 9px}
.cb-read p:last-child{margin-bottom:0}
.cb-read .num{font-family:var(--ff-m);font-weight:600;color:var(--ink)}
.cb-read .claim{
  display:block;margin-top:10px;padding:10px 12px;border-radius:10px;background:var(--white);
  border-left:2.5px solid var(--gold);font-size:13.4px;color:var(--ink);box-shadow:var(--sh-1);
}
/* the paper's headline result — that modeling the mind is what decides the task
   — is the one conclusion allowed to shout */
.cb-read .claim-key{border-left-color:var(--red);color:var(--red);font-weight:650}
/* .fnd p b is more specific than a bare .key, so it has to be named here */
.key,.fnd p b.key{color:var(--red);font-weight:650}
.cb-read.flash{animation:readFlash .6s cubic-bezier(.16,1,.3,1)}
@keyframes readFlash{from{opacity:.2;transform:translateY(6px)}to{opacity:1;transform:none}}

/* svg chart bits */
.chart svg{display:block;width:100%;height:auto;overflow:visible}
.ax-line{stroke:var(--line);stroke-width:1}
.ax-grid{stroke:var(--line);stroke-width:1;stroke-dasharray:2 5;opacity:.85}
.ax-t{font-family:var(--ff-m);font-size:10px;fill:var(--ink-4);letter-spacing:.02em}
.ax-lab{font-family:var(--ff-b);font-size:11px;fill:var(--ink-3)}
.hum-line{stroke:var(--gold);stroke-width:1.6;stroke-dasharray:7 5}
.hum-t{font-family:var(--ff-m);font-size:10px;fill:var(--gold-3)}
.ln{fill:none;stroke-width:1.7;opacity:.82;transition:opacity .3s,stroke-width .3s}
.ln.avg{stroke:var(--ink);stroke-width:3;opacity:1}
.ln.mute{opacity:.12}
.dotm{fill:var(--ink);r:3.4}
.vlab{font-family:var(--ff-m);font-size:11px;font-weight:600;fill:var(--ink)}
.hit{fill:transparent;cursor:pointer}
.rung-hl{fill:var(--gold-glow);opacity:0;transition:opacity .35s}
.rung.on .rung-hl{opacity:.42}
.rung-t{font-family:var(--ff-b);font-size:11.5px;fill:var(--ink-3);transition:fill .3s}
.rung.on .rung-t{fill:var(--ink);font-weight:600}
.bar{transition:opacity .3s,transform .3s;cursor:pointer}
.bar:hover{opacity:.88}
.bar-t{font-family:var(--ff-m);font-size:11px;font-weight:600;fill:var(--ink)}
.bar-l{font-family:var(--ff-b);font-size:11.5px;fill:var(--ink-2)}
.bar-g{font-family:var(--ff-m);font-size:10.5px;font-weight:600;fill:#fff}
.legend{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.lg{
  display:flex;align-items:center;gap:6px;padding:4px 10px;border-radius:20px;border:1px solid var(--line);
  font-family:var(--ff-m);font-size:10.5px;color:var(--ink-3);transition:.3s;background:var(--paper);
}
.lg:hover{border-color:var(--line-2)}
.lg.off{opacity:.4}
.lg i{width:14px;height:2.5px;border-radius:2px;display:block;flex:0 0 auto}
.lg.avg i{height:3.5px}
.lg.static{cursor:default;background:transparent;border-color:transparent;padding-left:2px}

/* readout extras */
.cb-read .big-n{font-family:var(--ff-m);font-size:1.9em;font-weight:600;color:var(--ink);letter-spacing:-.02em;line-height:1}
.chart-note{margin:11px 0 0;font-size:11.6px;line-height:1.5;color:var(--ink-4)}
.chart-note b{font-weight:600;color:var(--ink-3)}

/* ablation + oracle bars */
.bar.on rect:not(.hit):not(.otrack){filter:drop-shadow(0 4px 12px rgba(60,44,20,.34))}
.bar.on .bar-l{fill:var(--ink);font-weight:600}
.bar-d{font-family:var(--ff-m);font-size:11px;font-weight:600;fill:var(--ink-3)}
.pdot{fill:#fff;opacity:.9;stroke:rgba(60,44,20,.28);stroke-width:.6}
.otrack{fill:var(--paper-3)}
.bar.on .otrack{fill:var(--gold-glow);opacity:.55}
.sum-line{stroke:var(--ink-4);stroke-width:1;stroke-dasharray:2 4}
.sum-t{font-family:var(--ff-m);font-size:10px;fill:var(--ink-4)}

/* grouped slice charts */
.ch-top{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px}
.ch-lab{font-family:var(--ff-m);font-size:9.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-4)}
.ch-switch{display:inline-flex;gap:4px;padding:3px;border-radius:100px;background:var(--paper-3);border:1px solid var(--line);margin-left:auto}
.chs{
  padding:4px 12px;border-radius:100px;font-size:11.8px;color:var(--ink-3);
  transition:.3s cubic-bezier(.2,.7,.2,1);white-space:nowrap;
}
.chs.is-on{background:var(--white);color:var(--ink);box-shadow:var(--sh-1);font-weight:500}
.grp .rung-hl{fill:var(--gold-glow);opacity:0;transition:opacity .35s}
.grp.on .rung-hl{opacity:.38}
.grp-t{font-family:var(--ff-b);font-size:11.2px;fill:var(--ink-2);transition:fill .3s}
.grp.on .grp-t{fill:var(--ink);font-weight:600}
.gb{transition:opacity .3s}
.grp:hover .gb{opacity:.85}
.gain-t{font-family:var(--ff-m);font-size:10.5px;font-weight:600;fill:var(--gold-3)}

/* table */
.tbl-scroll{overflow-x:auto;padding:4px clamp(14px,2vw,24px) 8px}
.rtable{border-collapse:collapse;width:100%;font-family:var(--ff-m);font-size:12.4px;min-width:760px}
.rtable th,.rtable td{padding:8px 9px;text-align:right;border-bottom:1px solid var(--line);white-space:nowrap}
.rtable th{font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);font-weight:500;border-bottom:1px solid var(--line-2)}
.rtable th.grp{text-align:center;color:var(--ink-2);border-bottom:1px solid var(--line);font-weight:600}
.rtable td:first-child,.rtable th:first-child{text-align:left;width:34px;color:var(--ink-4)}
.rtable td:nth-child(2),.rtable th:nth-child(2){text-align:left;font-family:var(--ff-b);font-size:13px;white-space:normal;min-width:170px}
.rtable tbody tr{cursor:pointer;transition:background .25s}
.rtable tbody tr:hover{background:var(--paper-2)}
.rtable tbody tr.is-on{background:linear-gradient(90deg,var(--gold-glow),transparent 78%)}
.rtable tbody tr.best td{font-weight:600;color:var(--ink)}
.rtable tbody tr.hum td{color:var(--gold-3);font-style:italic}
.rtable tbody tr.abl{color:var(--ink-3)}
.rtable tbody tr.sep td{border-top:1px solid var(--line-2)}
.rtable td.avg{font-weight:600;color:var(--ink);background:rgba(240,232,216,.4)}
.rtable td .dl{font-size:10px;color:var(--green);margin-left:5px}

/* ══════════════════════════ APPS ══════════════════════════ */
/* apps hands over to cite at exactly the colour cite opens on, so fading that
   join would only put a pale band where there is nothing to hide */
.sec-apps::before{background:linear-gradient(180deg,var(--paper),var(--paper-2));--fb:0px}
.cond-row{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:clamp(24px,3vw,40px)}
.cond{
  padding:16px 18px;border-radius:var(--r-m);border:1px dashed var(--line-2);background:rgba(255,253,249,.7);
}
.cond b{display:block;font-size:14.5px;letter-spacing:-.012em;margin-bottom:3px;color:var(--gold-3)}
.cond span{font-size:13px;color:var(--ink-3);line-height:1.5}
.app-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
.app{
  padding:22px 24px 24px;border-radius:var(--r-l);border:1px solid var(--line);background:var(--white);
  box-shadow:var(--sh-1);position:relative;overflow:hidden;transition:transform .5s cubic-bezier(.16,1,.3,1),box-shadow .5s;
}
.app:hover{transform:translateY(-3px);box-shadow:var(--sh-2)}
.app-i{width:34px;height:34px;margin-bottom:12px;opacity:.9}
.app h3{margin:0 0 7px;font-family:var(--ff-d);font-size:19px;font-weight:500;letter-spacing:-.024em}
.app p{margin:0;font-size:14px;color:var(--ink-2);line-height:1.58}
.app p em{font-style:italic}
.app-tag{
  position:absolute;right:18px;top:18px;font-family:var(--ff-m);font-size:9.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-4);
}

/* ══════════════════════════ CITE ══════════════════════════ */
.sec-cite{padding-bottom:44px}
.sec-cite::before{background:linear-gradient(180deg,var(--paper-2),var(--paper-3));--ft:0px;--fb:0px}
.cite-top{display:grid;grid-template-columns:1fr 1fr;gap:clamp(22px,3vw,50px);align-items:start}
.cite-line{margin:16px 0 0;font-family:var(--ff-d);font-size:clamp(1rem,1.6vw,1.24rem);font-weight:300;line-height:1.5;color:var(--ink-2);max-width:40ch;letter-spacing:-.018em}
.cite-links{display:flex;gap:20px;margin-top:22px;font-size:14.5px}
.cite-links a{position:relative;padding-bottom:3px;border-bottom:1.5px solid var(--gold-2);transition:.3s}
.cite-links a:hover{color:var(--gold-3);border-color:var(--gold)}
.cite-box{border-radius:var(--r-m);overflow:hidden;border:1px solid var(--line-2);background:var(--white);box-shadow:var(--sh-1)}
.cite-box-h{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background:var(--paper-2);border-bottom:1px solid var(--line);font-family:var(--ff-m);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3)}
.copy-btn{font-family:var(--ff-m);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--gold-3);transition:.3s}
.copy-btn:hover{color:var(--ink)}
#bibtex{margin:0;padding:16px;font-family:var(--ff-m);font-size:12.2px;line-height:1.62;color:var(--ink-2);overflow-x:auto}

.authors{display:flex;flex-wrap:wrap;gap:clamp(24px,4vw,60px);align-items:center;margin-top:clamp(36px,4vw,60px);padding-top:26px;border-top:1px solid var(--line-2)}
.au b{display:block;font-size:16px;letter-spacing:-.015em}
.au b a{font-family:inherit;font-size:inherit;color:inherit;border-bottom:1px solid var(--line-2)}
.au b a:hover{color:var(--gold-3);border-color:var(--gold-2)}
.au span{display:block;font-size:13px;color:var(--ink-3);margin-top:1px}
.au-corr{font-style:italic;color:var(--gold-3)!important}
.au a{font-family:var(--ff-m);font-size:12px;color:var(--gold-3);border-bottom:1px solid transparent;transition:.3s}
.au a:hover{border-color:var(--gold-2)}
.au-logos{margin-left:auto;display:flex;align-items:center;gap:22px}
.au-logos img{height:44px;width:auto;mix-blend-mode:multiply;opacity:.9}

.foot{display:flex;align-items:center;gap:12px;margin-top:34px;padding-top:18px;border-top:1px solid var(--line);font-family:var(--ff-m);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-4)}
.foot-mark{width:22px;height:22px;mix-blend-mode:multiply;opacity:.8}
.foot-r{margin-left:auto;text-transform:none;letter-spacing:.02em;font-family:var(--ff-b);font-size:12px;color:var(--ink-3);font-style:italic}

/* ══════════════════════════ TOOLTIP ══════════════════════════ */
.tip{
  position:fixed;z-index:2000;pointer-events:none;max-width:280px;padding:9px 12px;border-radius:10px;
  background:rgba(25,28,36,.95);color:#F6F1E7;font-size:12.4px;line-height:1.45;box-shadow:var(--sh-2);
  opacity:0;transform:translateY(4px);transition:opacity .18s,transform .18s;
}
.tip.on{opacity:1;transform:none}
.tip b{color:#F3D68F;font-weight:600}
.tip .t-k{font-family:var(--ff-m);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:#A9B0C0;display:block;margin-bottom:3px}

/* ══════════════════════════ RESPONSIVE ══════════════════════════ */
@media (max-width:1080px){
  .cb-body,.cb-sub{grid-template-columns:1fr}
  .cb-read{border-left:0;border-top:1px solid var(--line)}
  .findings{grid-template-columns:1fr}
  .pipe{grid-template-columns:1fr}
  .pipe-rail{position:static;flex-direction:row;overflow-x:auto;padding-bottom:6px}
  .pstep{min-width:210px}
  .pstep::before{display:none}
  .cite-top{grid-template-columns:1fr}
  .tax-grid{grid-template-columns:1fr}
  .app-grid{grid-template-columns:1fr}
  .cond-row{grid-template-columns:1fr}
}
@media (max-width:900px){
  .mwm-wrap{display:none}
  .mwm-flat{display:block}
}
@media (max-width:860px){
  body{font-size:16px}
  .cb-body-2{grid-template-columns:1fr}
  .cb-body-2 .cb-chart+.cb-chart{border-left:0;border-top:1px solid var(--line)}
  .authors{gap:20px}
  .au-logos{margin-left:0}
}
@media (max-width:620px){
  .mwm-wrap{padding:12px}
  .fx{font-size:13px}
  .foot-r{display:none}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.06s!important}
  html{scroll-behavior:auto}
  .rv{opacity:1;transform:none}
  /* the observer fades in where it already stands instead of climbing out */
  .wm-god{--rise:0%}
  /* the pulse rings stop expanding; leave one legible static ring behind */
  .fp-ring-b{display:none}
  .fp-ring{opacity:.8}
}
