/**
 * site.css — the landing-page layer. Only applies under ?site=1.
 *
 * NOTE: this file deliberately does NOT touch html/body overflow or #root
 * positioning any more. The viewer is a fullscreen tool and its layout is
 * correct as shipped; an earlier attempt to force document scroll here made
 * the page slide off the canvas. Scroll is virtual now (scroll-spine.js),
 * so nothing in the DOM needs to move.
 */

/* ── fonts: Geist Sans + Geist Pixel (Vercel, SIL OFL 1.1, fonts/OFL.txt) ──
   Named everywhere in this file but never loaded before, so every heading
   and label fell back to system-ui / ui-monospace. Geist Pixel ships as five
   shapes rather than one variable axis; 'Geist Pixel' is Square, the others
   are available by their own names. */
@font-face { font-family: 'Geist Sans'; src: url('fonts/Geist-Variable.woff2') format('woff2');
             font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: 'Geist Pixel'; src: url('fonts/GeistPixel-Square.woff2') format('woff2');
             font-weight: 400; font-display: swap; }
@font-face { font-family: 'Geist Pixel Line'; src: url('fonts/GeistPixel-Line.woff2') format('woff2'); font-display: swap; }
@font-face { font-family: 'Geist Pixel Grid'; src: url('fonts/GeistPixel-Grid.woff2') format('woff2'); font-display: swap; }
@font-face { font-family: 'Geist Pixel Circle'; src: url('fonts/GeistPixel-Circle.woff2') format('woff2'); font-display: swap; }
@font-face { font-family: 'Geist Pixel Triangle'; src: url('fonts/GeistPixel-Triangle.woff2') format('woff2'); font-display: swap; }

/* fixed overlay layer. Children are placed each frame by world-anchor.js,
   projecting a real world coordinate in the room through the live camera. */
#site-anchors {
  position: fixed;
  inset: 0;
  z-index: 20;
  pointer-events: none;
  overflow: hidden;
}

/* An anchored object. transform is OWNED by world-anchor.js — never set it
   in CSS or the projection will fight you. */
.anchored {
  position: fixed;
  left: 0;
  top: 0;
  transform-origin: 50% 50%;
  pointer-events: auto;
  opacity: 0;
}
/* a card that is off (card-kit.js setVis): nothing in it shows or takes a tap, whatever its beat blocks say inline */
.anchored.is-dormant, .anchored.is-dormant * { visibility: hidden !important; pointer-events: none !important; }

/* ── the About card ───────────────────────────────────────────────────── */

.glass-card {
  position: relative;
  width: 620px;
  max-width: 78vw;
  border-radius: 6px;               /* rectangle, not a pill */
  overflow: hidden;
  isolation: isolate;
  /* fallback glass — the WebGL refraction layer draws over this when it
     initialises, so the card is never invisible if WebGL bails */
  /* 2026-10-01: Ricco found the 0.26 / 14 px glass too clear and liked it all the same, so this sits midway between it and the one
     before it (0.44 / 22 px): tint 0.35, blur 18 px; the refraction shader likewise (glass-rect.js alpha 0.43, card-kit.js frost 34) */
  background: linear-gradient(180deg, rgba(8, 11, 16, 0) 0%, rgba(8, 11, 16, 0.22) 100%), rgba(10, 14, 20, 0.35);
  backdrop-filter: blur(18px) saturate(1.5) brightness(0.9);
  -webkit-backdrop-filter: blur(18px) saturate(1.5) brightness(0.9);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    inset 0 -18px 36px -18px rgba(255, 255, 255, 0.14),
    0 30px 80px -20px rgba(0, 0, 0, 0.75);
  border: 1px solid rgba(255, 255, 255, 0.16);
}

/* the refraction canvas sits behind the content, inside the card */
.glass-card > .glass-gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
}

.glass-card > .card-content {
  position: relative;
  z-index: 1;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.55);
  padding: 30px 32px 26px;
  display: block;
}

/* ── type ─────────────────────────────────────────────────────────────── */

.eyebrow {
  font-family: 'Geist Pixel', ui-monospace, 'IBM Plex Mono', monospace;
  font-variation-settings: 'ELSH' 40;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  margin: 0 0 12px;
}

.card-heading {
  font-family: 'Geist Pixel', ui-monospace, 'IBM Plex Mono', monospace;
  font-variation-settings: 'ELSH' 1;
  font-size: 23px;
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.015em;
  text-wrap: balance;
  margin: 0 0 16px;
  color: #fff;
}

/* ── epigraph ─────────────────────────────────────────────────────────
 * Sits between the heading and the beats and stays put while they
 * cross-fade, so the travelling colour band has somewhere long enough to
 * actually be seen. A hairline on the left marks it as quoted without
 * spending a quotation mark on it.
 */
.card-epigraph {
  border-left: 1px solid rgba(127, 232, 208, 0.34);
  padding: 2px 0 2px 13px;
  margin: 0 0 18px;
  opacity: 0;
}

.epi-line {
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-size: 13px;
  line-height: 1.55;
  font-style: italic;
  letter-spacing: 0.005em;
  margin: 0;
  /* colourSweep writes per-character colour; nothing here may override it */
}

.epi-foot {
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-size: 10.5px;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.4);
  margin: 7px 0 0;
}

.glass-card.is-docked .card-epigraph { margin-bottom: 24px; padding-left: 17px; }
.glass-card.is-docked .epi-line { font-size: clamp(14px, 1.25vw, 17px); }
.glass-card.is-docked .epi-foot { font-size: clamp(11px, 0.85vw, 12.5px); }

.card-lead {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 1;
  font-size: 15px;
  letter-spacing: -0.005em;
  color: #fff;
  margin: 0 0 7px;
}

.card-body {
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-size: 13.5px;
  line-height: 1.6;
  margin: 0 0 11px;
}

.word { display: inline-block; color: rgba(255, 255, 255, 0.18); }

/* ── beat blocks ──────────────────────────────────────────────────────── */

/* one slot; every beat overlaps inside it and they cross-fade */
.beat-stack {
  position: relative;
  min-height: 190px;
}
/* the reading column is ~40% narrower than the card, so the same
   paragraph sets taller and needs more slot than it used to */
.glass-card.is-docked .beat-stack { min-height: clamp(260px, 40vh, 420px); }

/* top/left/right, not inset:0 — a block must be as tall as its content so
   card-kit can size the slot to the tallest beat */
.beat-block {
  position: absolute;
  top: 0; left: 0; right: 0;
  opacity: 0;
  visibility: hidden;
  will-change: opacity, transform;
}

/* ── project / stack beat furniture ───────────────────────────────────
 * A Projects beat is the same block as an About beat with three extra
 * parts: a ghosted index so you can see where you are in the ranking, a
 * role line that says what the thing was and what it placed, and the tech
 * as chips rather than prose. Chips because nobody reads a stack list as a
 * sentence, and a card that pretends otherwise wastes two lines.
 */
.beat-index {
  position: absolute;
  top: -0.12em;
  right: 0;
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 20;
  font-size: clamp(30px, 3.6vw, 56px);
  line-height: 1;
  letter-spacing: -0.04em;
  color: rgba(255, 255, 255, 0.07);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.beat-role {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 40;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #7FE8D0;
  margin: 0 0 12px;
  opacity: 0;
}
.glass-card.is-docked .beat-role { font-size: clamp(10px, 0.78vw, 12px); }

.beat-chips {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 14px 0 0;
  padding: 0;
}
.beat-chips > li {
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1;
  letter-spacing: 0.01em;
  padding: 5px 9px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
  opacity: 0;
  will-change: opacity, transform;
}
.glass-card.is-docked .beat-chips > li { font-size: clamp(10.5px, 0.8vw, 12.5px); padding: 6px 11px; }

.beat-links {
  margin: 13px 0 0;
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 40;
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}
.beat-links a {
  color: #fff;
  text-decoration: none;
  border-bottom: 1px solid rgba(127, 232, 208, 0.55);
  padding-bottom: 2px;
  margin-right: 16px;
  transition: border-color .2s ease, color .2s ease;
}
.beat-links a:hover { color: #7FE8D0; border-bottom-color: #7FE8D0; }

/* keep a long project title clear of the ghosted index behind it */
.card-projects .card-lead { padding-right: 2.6em; }

/* Stack chips are the content, not a footnote, so they get the room the
   body copy would otherwise take */
.card-stack .beat-chips { margin-top: 18px; }

/* ── docked state ─────────────────────────────────────────────────────── */

/* the card grows and flattens; content gets room to breathe and scroll
   internally if a beat overflows on short viewports */
/* Docked: the card RESIZES (layout) rather than being scaled up, so text
   is re-rendered crisp at the new size instead of being a blown-up bitmap. */
.glass-card.is-docked {
  width: min(1180px, 92vw);
  border-radius: 8px;
  transition: width .55s cubic-bezier(.22,.61,.36,1),
              border-radius .55s cubic-bezier(.22,.61,.36,1);
}
.glass-card.is-docked > .card-content {
  padding: clamp(28px, 4vh, 54px) clamp(26px, 4vw, 64px);
  min-height: 62vh;
  max-height: 86vh;
  overflow: hidden;
  /* Two columns. The reading measure stays where it is legible and the
     leftover third becomes a column instead of dead space. */
  display: grid;
  grid-template-columns: minmax(0, 1.32fr) minmax(0, 0.8fr);
  grid-template-areas:
    'main side'
    'rail rail'
    'hint hint';
  column-gap: clamp(28px, 4vw, 72px);
  align-content: center;
}
.glass-card.is-docked .card-main  { grid-area: main; min-width: 0; }
.glass-card.is-docked .card-side  { grid-area: side; min-width: 0; display: flex; }
.glass-card.is-docked .fact-rail  { grid-area: rail; }
.glass-card.is-docked .dock-hint  { grid-area: hint; }

/* ── side column ──────────────────────────────────────────────────────
 * Hidden entirely on the small in-room card: at that size there is no
 * spare width and no captured scroll to explain.
 */
.card-side { display: none; }
.glass-card.is-docked .card-side {
  flex-direction: column;
  justify-content: center;
  gap: clamp(16px, 2.2vh, 26px);
  border-left: 1px solid rgba(255, 255, 255, 0.09);
  padding-left: clamp(20px, 2.4vw, 40px);
}

.side-index {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 20;
  font-size: clamp(44px, 6vw, 86px);
  line-height: 0.9;
  letter-spacing: -0.04em;
  color: rgba(255, 255, 255, 0.14);
  font-variant-numeric: tabular-nums;
}

.side-chapters {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1vh, 11px);
}

.side-chapter {
  display: flex;
  align-items: baseline;
  gap: 11px;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(11.5px, 0.95vw, 14px);
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.3);
  transition: color .34s ease, opacity .34s ease;
}
.side-chapter .sc-n {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 40;
  font-size: 0.78em;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.22);
  transition: color .34s ease;
}
.side-chapter.is-done { color: rgba(255, 255, 255, 0.42); }
.side-chapter.is-on   { color: #fff; }
.side-chapter.is-on .sc-n { color: #7FE8D0; }

/* overall progress through the card — the scrollbar a captured section
   cannot otherwise show you */
.side-meter {
  height: 2px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
  overflow: hidden;
}
.side-meter > i {
  display: block;
  height: 100%;
  background: #7FE8D0;
  transform: scaleX(0);
  transform-origin: left center;
  will-change: transform;
}

/* one column again where there is no width to split */
@media (max-width: 860px) {
  .glass-card.is-docked > .card-content {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'main' 'rail' 'hint';
  }
  .glass-card.is-docked .card-side { display: none; }
}
.glass-card.is-docked .card-heading { font-size: clamp(26px, 3.4vw, 44px); margin-bottom: 26px; }
.glass-card.is-docked .card-lead    { font-size: clamp(18px, 1.75vw, 26px); margin-bottom: 12px; }
/* no max-width any more: the grid column IS the measure, and capping it
   again here is what left the right third of the card empty */
.glass-card.is-docked .card-body    { font-size: clamp(15.5px, 1.32vw, 20px); line-height: 1.62; }

/* smooth width change needs the base card to animate too */
.glass-card {
  transition: width .55s cubic-bezier(.22,.61,.36,1),
              border-radius .55s cubic-bezier(.22,.61,.36,1);
}

/* ── carousel stage ───────────────────────────────────────────────────
 * A sibling of the cards, not a child. It owns the entire viewport so a
 * 3D ring or tunnel has depth on all four sides; inside a 1180px card it
 * would get maybe 700 of them and read flat. z-index sits above the cards
 * and below nothing else, because while it is up it IS the page.
 */
.carousel-stage {
  position: fixed;
  inset: 0;
  /* z-index is set per layer in carousel-stage.js: back 16 (under the
     cards at 20, over the vignette at 15), front 40 (over everything) */
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
  /* no CSS transition: the opacity is written every frame from the beat's
     own eased progress, and a transition on top of that adds lag the
     scroll can outrun */
}
.carousel-stage > canvas {
  position: absolute;
  inset: 0;
  display: block;
}

.stage-caption {
  position: absolute;
  left: 0; right: 0;
  bottom: clamp(26px, 6vh, 70px);
  text-align: center;
  pointer-events: none;
  padding: 0 16px;
}
.stage-title {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 40;
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  margin: 0 0 8px;
}
.stage-sub {
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(13px, 1.1vw, 16px);
  letter-spacing: 0.01em;
  color: #fff;
  margin: 0;
}

/* ── bare section: type straight on the carousel ──────────────────────
 * No glass plate, no particle field, no rectangle between the reader and
 * the spiral. The type has to carry itself over a moving canvas, so it
 * gets a soft radial scrim of its own and a shadow — enough to stay
 * readable when a bright card passes behind a line, not so much that it
 * looks like a panel crept back in.
 */
.glass-card.is-bare { background: none !important; border: 0 !important;
                      box-shadow: none !important; backdrop-filter: none !important; }
.glass-card.is-bare > .glass-gl,
.glass-card.is-bare > .card-field,
.glass-card.is-bare > .glass-spec,
.glass-card.is-bare > .glass-rim { display: none !important; }

.glass-card.is-bare > .card-content {
  background:
    radial-gradient(120% 90% at 30% 50%,
      rgba(8, 8, 11, 0.82) 0%,
      rgba(8, 8, 11, 0.62) 42%,
      rgba(8, 8, 11, 0) 78%);
}
.glass-card.is-bare .card-heading,
.glass-card.is-bare .card-lead {
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.75), 0 1px 3px rgba(0, 0, 0, 0.9);
}
.glass-card.is-bare .card-body,
.glass-card.is-bare .beat-role,
.glass-card.is-bare .side-chapter {
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.85);
}
.glass-card.is-bare .beat-chips > li {
  background: rgba(8, 8, 11, 0.55);
  border-color: rgba(255, 255, 255, 0.22);
}
/* the side column's hairline reads as a panel edge when there is no panel */
.glass-card.is-bare.is-docked .card-side { border-left-color: rgba(255,255,255,0.14); }
/* the ghosted index competes with a moving carousel; lift it off */
.glass-card.is-bare .beat-index { color: rgba(255, 255, 255, 0.10); }

/* ── Projects · word behind the picture ───────────────────────────────
 * The composition is three layers deep and the z-order is the design:
 *
 *     z16  .card-words   the title, huge, centred
 *     z17  back carousel the picture slab, in front of the title
 *     z20  #site-anchors meta, description, tech, links
 *
 * You read the word from its ends because the picture covers its middle.
 * That only works if the word is genuinely behind — faking it with a
 * blend mode breaks the moment a light image passes over dark type.
 */
.card-words {
  position: fixed;
  inset: 0;
  z-index: 16;
  pointer-events: none;
}
.card-word-slot {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  padding: 0 4vw;
}
.card-word {
  margin: 0;
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-weight: 800;
  /*
   * Sized so a two-word title sets on one line and a three-word one on
   * two. 15.5vw put "INDIAN SPACE PROGRAM" on three lines that filled the
   * frame top to bottom and left nothing for the description.
   */
  font-size: clamp(38px, 9.4vw, 152px);
  line-height: 0.88;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  text-align: center;
  color: rgba(255, 255, 255, 0.95);
  /* text-wrap:balance was making the break decisions worse here, not
     better: it evens line lengths, which on a two-line title pulls a word
     up into the line the picture covers */
  text-wrap: normal;
  max-width: 92vw;
  text-shadow: 0 0 60px rgba(0, 0, 0, 0.55);
}
/* Image Flipper (image-flipper.js): the project's pages, bottom left */
.word-screens {
  position: absolute;
  left: clamp(16px, 2vw, 48px);
  bottom: clamp(18px, 5vh, 52px);
  /* the margin beside the ~1000px description column, never into it:
     at 1366 wide the paragraph starts 183px in */
  width: clamp(130px, calc((100vw - 1060px) / 2), 232px);
}
.word-screens-head {
  margin: 0 0 8px;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
.word-screens-head span { color: #7FE8D0; }
.word-screens-box {
  position: relative;
  aspect-ratio: 16 / 10;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(8, 10, 12, 0.6);
  overflow: hidden;
}
.word-screens-box > canvas.flip-canvas {
  position: absolute !important; inset: 0 !important;
  width: 100% !important; height: 100% !important; display: block;
}
@media (max-width: 1300px), (max-height: 620px) { .word-screens { display: none; } }

/* word-fill.js: the title in its project's colours. --vf is how far the
   fill has come in; the white letters step back under it so the
   anti-aliased glyph edges do not ring white round the colour. */
.card-word { color: rgba(255, 255, 255, calc(0.95 - 0.8 * var(--vf, 0))); }
.word-fill {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  isolation: isolate;
}
.word-fill-grad, .word-fill-grain { position: absolute; }
.word-fill-grain {
  background-size: 128px 128px;
  mix-blend-mode: overlay;
  opacity: 0.34;
}
/* the word sits ABOVE centre so the slab crosses it and the paragraph
   below stays completely clear */
.card-word-slot { align-items: flex-start; padding-top: 14vh; }

/* full bleed: the slab and the word are sized against the viewport, so a
   1180px card would crop the composition at the sides */
.glass-card.card-projects.is-docked {
  width: 100vw;
  /* the base card's 78vw cap made this 304px on a phone while its text
     column was 343px, so the card cut the description off at both sides */
  max-width: 100vw;
  border-radius: 0;
}

/* the content column sits under the slab, centred, no panel anywhere */
.card-projects.is-bare.is-docked > .card-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  /* foot room trimmed (was 44..104px) so the description sits lower and the
     slab, 25% bigger since 2026-09-29, has the band above it */
  padding: 0 clamp(20px, 5vw, 80px) clamp(28px, 4vh, 48px);
  min-height: 100vh;
  max-height: 100vh;
  background: none;              /* the bare-mode scrim would box it in */
}
/*
 * WIDTH, not max-width.
 *
 * .beat-stack's only children are .beat-block, which are position:absolute
 * and therefore contribute NO intrinsic width to their parent. In the old
 * grid layout .card-main was sized by its grid column so this never
 * mattered; in a flex column with align-items:center there is nothing left
 * to size it, so it collapsed to zero and every word in the meta line and
 * the description landed on its own line.
 */
/* px, not ch: 74ch here resolved in the column's ~9px font and came out
   ~650px, which set the long descriptions nine lines deep and pushed them
   up over the slab. */
.card-projects.is-bare .card-main {
  width: min(1040px, 88vw);
  max-width: 88vw;
  text-align: center;
}
.card-projects.is-bare .beat-stack { width: 100%; }
/* every beat ends just above the hint, whatever the tallest one is, so a
   short beat never starts up under the picture */
.card-projects.is-bare .beat-block { top: auto; bottom: 0; }
.card-projects.is-bare .beat-stack { min-height: clamp(230px, 34vh, 360px); }
.card-projects.is-bare .beat-block { text-align: center; }

/* everything that is furniture rather than content goes */
.card-projects.is-bare .eyebrow,
.card-projects.is-bare .card-heading,
.card-projects.is-bare .fact-rail,
.card-projects.is-bare.is-docked .card-side { display: none; }

/* the index, small and quiet, above the meta */
.card-projects.is-bare .beat-index {
  position: static;
  display: block;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: clamp(10px, 0.8vw, 12px);
  letter-spacing: 0.2em;
  color: var(--mint, #7FE8D0);
  margin: 18px 0 10px;                    /* clear of the slab's bottom edge */
}
.card-projects.is-bare .beat-role {
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  text-transform: uppercase;
  font-size: clamp(10px, 0.82vw, 12px);
  letter-spacing: 0.15em;
  color: rgba(255, 255, 255, 0.72);
  margin: 0 0 14px;
  opacity: 0;
  text-shadow: 0 1px 8px rgba(0,0,0,.9);
}

/*
 * THE DESCRIPTION. Deliberately larger than the rest of the site: this is
 * the only place the projects are explained, there is no panel competing
 * with it, and at the old 12.5px it read as a caption under a picture
 * rather than as the point of the section.
 */
.card-projects.is-bare .card-body {
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  text-transform: none;
  /* 24 at the top end, not 26: at 26 Malhar Passes set seven lines that
     reached 18px up over the slab on a 1080p screen. Still well above the
     21px that was called too small. */
  font-size: clamp(17px, 1.5vw, 24px);
  letter-spacing: 0.002em;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.94);
  max-width: 70ch;
  margin: 0 auto;
  text-wrap: pretty;
  /* it is read against a moving carousel, so it carries its own plate
     rather than relying on the picture being dark where the line is */
  text-shadow: 0 2px 10px rgba(0,0,0,.95), 0 0 34px rgba(0,0,0,.85);
}

/* tech as a plain line, not pills: a pill is a control and nothing here
   is clickable */
.card-projects.is-bare .beat-chips {
  justify-content: center;
  gap: 0;
  margin-top: 16px;
}
.card-projects.is-bare .beat-chips > li {
  border: 0;
  background: none;
  padding: 0;
  border-radius: 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  text-transform: uppercase;
  font-size: clamp(10px, 0.8vw, 12px);
  letter-spacing: 0.14em;
  color: rgba(255, 255, 255, 0.74);        /* was .42: unreadable over the chairs */
  text-shadow: 0 1px 8px rgba(0,0,0,.9);
}
.card-projects.is-bare .beat-chips > li:not(:last-child)::after {
  content: '·';
  margin: 0 9px;
  color: rgba(255, 255, 255, 0.24);
}

.card-projects.is-bare .beat-links {
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: clamp(10px, 0.82vw, 12px);
  letter-spacing: 0.15em;
  margin-top: 18px;
}
.card-projects.is-bare .beat-links a {
  display: inline-block;
  margin: 0 14px 6px;
  border-bottom-color: rgba(127, 232, 208, 0.55);
}

/* a captured section has no scrollbar, so the one affordance stays */
.card-projects.is-bare .dock-hint { margin-top: 20px; }

/*
 * The viewer's own chrome is authoring UI, not part of the site. Its
 * playback bar sits across the bottom of the screen, which is exactly
 * where a bottom-aligned description lands, so it was covering the text.
 * Hidden in site mode; &debug=1 puts it back for scrubbing the timeline.
 */
html.site-mode:not(.site-debug) #cinematic-hud,
html.site-mode:not(.site-debug) #top-brand-hud { display: none !important; }

/*
 * Aholo's table globe is a fixed canvas at z-index 200 — above the cards
 * (20), the back carousel (17) and even the takeover carousel (40). It drew
 * inside the Stack glass card and inside a tile of the Stack globe. In
 * site mode it is a thing in the room, so it goes back into the room.
 */
html.site-mode #orbit-globe-canvas { z-index: 12 !important; }

/*
 * DEBUG MODE puts the viewer's playback bar back, and at its native
 * position it sits across the bottom-centre of the screen: exactly where
 * the Projects description, tech line and links are. Shrunk into the
 * bottom-right corner it is still a working scrubber and stays out of the
 * content. The spine readout keeps the bottom-left.
 */
html.site-mode.site-debug #cinematic-hud {
  left: auto !important;
  right: 12px !important;
  bottom: 12px !important;
  transform: scale(0.72) !important;
  transform-origin: 100% 100% !important;
  opacity: 0.9;
}
html.site-mode.site-debug #top-brand-hud { opacity: 0.55; }

/* short screens (1366x768 and friends): same text, set wider and a touch
   tighter so a long description stays under the lifted slab */
@media (max-height: 860px) {
  .card-projects.is-bare .card-body {
    font-size: clamp(16px, 2.35vh, 19px);
    line-height: 1.5;
    max-width: 84ch;
  }
  .card-projects.is-bare.is-docked > .card-content { padding-bottom: 22px; }
  .card-projects.is-bare .beat-index { margin-top: 10px; }
  .card-projects.is-bare .beat-role { margin-bottom: 10px; }
}

@media (max-width: 860px) {
  .card-word { font-size: clamp(30px, 11vw, 74px); }
  .card-word-slot { padding-top: 9vh; }
  .card-projects.is-bare .card-body { font-size: 16px; max-width: none; }
  .card-projects.is-bare.is-docked > .card-content {
    justify-content: flex-end;
    padding-bottom: 8vh;
  }
}

/* a bare section (no glass) needs the centre of the room dimmed as well,
   because that is where its text is */
html.dock-bare .dock-vignette {
  background:
    radial-gradient(ellipse 78% 72% at 50% 50%,
      rgba(0,0,0,0.46) 0%,
      rgba(0,0,0,0.58) 52%,
      rgba(0,0,0,0.80) 82%,
      rgba(0,0,0,0.90) 100%);
}

/* ── hero ─────────────────────────────────────────────────────────────
 * Screen-space type over the intro flight. Lower-left like a cover, with
 * its own scrim: the classroom behind it is a cream wall under strip
 * lights, the brightest thing the site ever puts text on.
 */
#site-hero {
  position: fixed;
  inset: 0;
  z-index: 21;
  pointer-events: none;
}
#site-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 72% 70% at 12% 88%,
    rgba(4,4,7,0.72) 0%, rgba(4,4,7,0.42) 42%, rgba(4,4,7,0) 74%);
  opacity: 0;
  transition: opacity .6s ease;
}
html.hero-up #site-hero::before { opacity: 1; }

/* width in px/vw, never ch: `ch` resolves in the element's OWN font size,
   and on this block that is ~16px — min(21ch, …) came out ~185px wide and
   stacked the 78px headline one word per line, straight off the top of the
   screen. Each child now sets its own measure in its own units. */
.hero-block {
  position: absolute;
  left: clamp(20px, 6vw, 112px);
  bottom: clamp(96px, 17vh, 200px);
  width: min(900px, 86vw);
  opacity: 0;
  visibility: hidden;
  will-change: opacity, transform;
}
.hero-eyebrow {
  margin: 0 0 18px;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: clamp(11px, 0.95vw, 14px);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: #7FE8D0;
}
.hero-line {
  margin: 0;
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-weight: 700;
  font-size: clamp(32px, 4.3vw, 78px);
  line-height: 1.02;
  letter-spacing: -0.035em;
  color: #fff;
  max-width: 17ch;                 /* 17 of ITS characters: ~3 lines */
  text-wrap: balance;
  text-shadow: 0 2px 30px rgba(0,0,0,0.55);
}
.hero-sub {
  margin: 22px 0 0;
  max-width: 44ch;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(14px, 1.15vw, 18px);
  line-height: 1.6;
  color: rgba(255,255,255,0.86);
  text-shadow: 0 1px 12px rgba(0,0,0,0.8);
}
/* Text Carousel (extras.js textCarousel) */
.hero-rotate {
  margin: 26px 0 0;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-weight: 600;
  font-size: clamp(17px, 1.5vw, 25px);
  letter-spacing: -0.01em;
  line-height: 1.1;
  opacity: 0;
}
.tc { display: inline-flex; align-items: center; gap: 10px; }
.tc-prefix { color: #fff; text-shadow: 0 1px 12px rgba(0,0,0,0.8); }
.tc-badge {
  position: relative;
  display: inline-flex; align-items: center; justify-content: flex-start;
  overflow: hidden;
  padding-top: 5px; padding-bottom: 6px;
  border-radius: 9px;
  background: #7FE8D0;
  color: #06110f;
  box-sizing: border-box;
  white-space: nowrap;
}
.tc-content { display: inline-flex; white-space: nowrap; position: relative; }
.tc-char { display: inline-block; }   /* animated with element.animate(), which promotes a letter only while it moves */
.hero-note {
  position: absolute;
  right: clamp(20px, 3vw, 48px);
  bottom: clamp(22px, 4vh, 44px);
  max-width: 34ch;
  margin: 0;
  text-align: right;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px;
  line-height: 1.7;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.62);
  text-shadow: 0 1px 8px rgba(0,0,0,0.9);
  opacity: 0;
  visibility: hidden;
}

/* the scroll cue: a line that draws downward, over and over */
.hero-cue {
  position: absolute;
  left: 50%;
  bottom: clamp(20px, 4vh, 40px);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  opacity: 0;
  transition: opacity .5s ease;
}
.hero-cue.is-on { opacity: 1; }
.hero-cue-line {
  width: 1px;
  height: 46px;
  background: linear-gradient(to bottom, rgba(127,232,208,0), #7FE8D0);
  transform-origin: top;
  animation: hero-cue 1.9s cubic-bezier(.65,0,.35,1) infinite;
}
.hero-cue-label {
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.72);
}
@keyframes hero-cue {
  0%   { transform: scaleY(0); opacity: 1; }
  60%  { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-cue-line { animation: none; }
}
@media (max-width: 860px) {
  .hero-block { bottom: clamp(110px, 22vh, 190px); }
  .hero-note { display: none; }
}

/* a single-beat card has nothing to navigate: drop the chapter column and
   let the reading column take the card */
.glass-card.is-single.is-docked .card-side { display: none; }
.glass-card.is-single.is-docked > .card-content {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: 'main' 'rail' 'hint';
}
.glass-card.is-single.is-docked .card-main { max-width: 780px; }   /* px: ch resolves in this column's small inherited font */

/* ── contact ──────────────────────────────────────────────────────────
 * The email is the point of this card, so it is the biggest link on the
 * site rather than one underline among three.
 */
.card-contact .beat-links { margin-top: 26px; }
.card-contact .beat-links a:first-child {
  display: block;
  width: max-content;
  max-width: 100%;
  margin: 0 0 22px;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(22px, 2.6vw, 42px);
  font-weight: 600;
  letter-spacing: -0.02em;
  text-transform: none;
  color: #fff;
  border-bottom: 2px solid #7FE8D0;
  padding-bottom: 6px;
  overflow-wrap: anywhere;
}
.card-contact .beat-links a:first-child:hover { color: #7FE8D0; }
/* the address and its Copy button share a row */
.card-contact .ct-mailrow {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 14px 22px; margin: 0 0 22px;
}
.card-contact .ct-mailrow a:first-child { margin: 0; }
/* the scramble keeps the address on one line, so on a phone it has to fit
   the card: 24 characters in ~260px */
@media (max-width: 480px) {
  .card-contact .beat-links a:first-child { font-size: clamp(15px, 4.6vw, 22px); }
}

/* Cube Flip (extras.js cubeFlip): face 1 dark, face 2 accent */
.cf {
  display: inline-block; position: relative;
  perspective: 600px;
  background: transparent; border: 0; padding: 0; margin: 0;
  cursor: pointer; color: inherit;
  -webkit-tap-highlight-color: transparent;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase;
}
.cf:focus-visible { outline: 2px solid #7FE8D0; outline-offset: 5px; }
.cf-cube { position: relative; display: block; transform-style: preserve-3d; min-width: 80px; }
.cf-face {
  display: flex; align-items: center; justify-content: center;
  padding: 12px 20px; white-space: nowrap;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  border: 1px solid rgba(255, 255, 255, 0.22);
}
.cf-front { position: relative; background: rgba(12, 16, 19, 0.72); color: #fff; }
.cf-back  { position: absolute; inset: 0; background: #7FE8D0; color: #07100e; border-color: #7FE8D0; }

/* Text Ring (extras.js textRing) on the Contact card's corner */
.ct-badge {
  /* inside the corner: the card clips anything past its edge */
  position: absolute; top: 22px; right: 26px;
  width: 138px; height: 138px;
  pointer-events: auto;
  display: grid; place-items: center;
}
.tr-ring { position: absolute; inset: 0; }
.tr-spin { position: absolute; inset: 0; color: #7FE8D0; will-change: transform; }
.tr-spin > span { position: absolute; top: 50%; left: 50%; display: inline-block; line-height: 1; }
.ct-badge-core {
  position: relative; font-family: 'Geist Pixel', ui-monospace, monospace;
  font-size: 34px; line-height: 1; color: #fff; pointer-events: none;
}
.glass-card:not(.is-docked) .ct-badge { opacity: 0; transition: opacity 0.3s; }
.glass-card.is-docked .ct-badge { opacity: 1; transition: opacity 0.6s 0.2s; }
@media (max-width: 720px) {
  .ct-badge { display: none; }          /* no free corner on a phone card */
}

/* ── vignette ─────────────────────────────────────────────────────────── */

.dock-vignette {
  position: fixed;
  inset: 0;
  z-index: 15;                 /* under #site-anchors (20), over the canvas */
  pointer-events: none;
  opacity: 0;
  background:
    radial-gradient(ellipse 78% 72% at 50% 50%,
      rgba(0,0,0,0) 0%,
      rgba(0,0,0,0.30) 52%,
      rgba(0,0,0,0.72) 82%,
      rgba(0,0,0,0.88) 100%);
  will-change: opacity;
}

.dock-hint {
  margin: 18px 0 0;
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 80;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.75);
  opacity: 0;
  transition: opacity .35s;
}

/* ── photo rail (animos focus-slider / deck pattern) ─────────────────── */

/*
 * The deck. It was width:400% inside a flex parent, which flex-shrank it to
 * the column and squashed four photos into slivers. Frames are now a fixed
 * portrait size, the rail is as wide as its content, and card-kit centres
 * the active frame in pixels. Neighbours fade out at the column edge rather
 * than being cut off by it.
 */
.beat-photos {
  overflow: hidden;
  display: block;
  /* headroom for photo-motion.js (the centred shot lifts 1.12x and floats):
     the clip box grows 28px each way, the negative margin keeps the layout */
  padding: 28px 0; margin: -28px 0;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%);
}

.photo-rail {
  display: flex;
  gap: 16px;
  width: max-content;
  height: clamp(220px, 33vh, 380px);
  will-change: transform;
}

.photo-rail figure {
  position: relative;
  flex: 0 0 auto;
  aspect-ratio: 4 / 5;
  height: 100%;
  margin: 0;
  border-radius: 4px;
  overflow: hidden;
  background: #11161d;
  border: 1px solid rgba(255,255,255,0.14);
  box-shadow: 0 16px 34px -12px rgba(0,0,0,0.8);
  transform-origin: 50% 50%;
  /* --fs: the deck's own scale (card-kit); --pm: the centred shot's motion */
  transform: var(--pm, translateZ(0)) scale(var(--fs, 1));
  will-change: transform, opacity;
}

.photo-rail img { width: 100%; height: 100%; object-fit: cover; display: block; }

.photo-rail .ph-tag {
  position: absolute; left: 7px; bottom: 6px;
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 80;
  font-size: 8.5px; letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255,255,255,.72);
  background: rgba(0,0,0,.55); padding: 2px 6px; border-radius: 2px;
}

/* placeholder marker — must stay obvious until real assets land */
.is-placeholder::after {
  content: 'PLACEHOLDER';
  position: absolute; top: 8px; left: 8px;
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 80;
  font-size: 10px; font-weight: 600;
  letter-spacing: .16em;
  color: #FFCF8A;
  background: rgba(58,32,0,.92);
  border: 1px solid rgba(255,190,120,.75);
  padding: 3px 7px; border-radius: 3px;
  box-shadow: 0 2px 10px rgba(0,0,0,.6);
  z-index: 2;
}

/* ── responsive ───────────────────────────────────────────────────────── */

@media (max-width: 860px) {
  .glass-card { width: 92vw; }
  .glass-card.is-docked > .card-content { padding: 26px 20px 22px; }
  .glass-card.is-docked .card-heading { font-size: 20px; }
  .card-heading { font-size: 19px; }
  .card-epigraph { padding-left: 10px; margin-bottom: 14px; }
  .glass-card.is-docked .epi-line { font-size: 12.5px; }
  .glass-card.is-docked .epi-foot { font-size: 10px; }
  .card-body { font-size: 12.5px; }
}

@media (prefers-reduced-motion: reduce) {
  .word { color: rgba(255, 255, 255, 0.92) !important; }
}


/* ── world markers: waypoints standing in the room ────────────────────── */

.marker {
  position: fixed;
  left: 0; top: 0;
  transform-origin: 50% 100%;
  pointer-events: none;
  opacity: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  --stem: 0px;
}

/* a thin line growing out of whatever surface the marker stands on */
.marker-stem {
  width: 1px;
  height: var(--stem);
  background: linear-gradient(to top,
    rgba(127, 232, 208, 0) 0%,
    rgba(127, 232, 208, 0.85) 60%,
    rgba(127, 232, 208, 0.95) 100%);
  transition: none;
}

.marker-head {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 5px 10px 5px 8px;
  border-radius: 3px;
  background: rgba(6, 10, 14, 0.62);
  border: 1px solid rgba(127, 232, 208, 0.30);
  backdrop-filter: blur(9px);
  -webkit-backdrop-filter: blur(9px);
  box-shadow: 0 8px 26px -10px rgba(0, 0, 0, 0.9);
}

.marker-index {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 80;     /* Line — data */
  font-size: 10px;
  letter-spacing: 0.1em;
  color: #7FE8D0;
}

.marker-label {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 1;      /* Square — headings */
  font-size: 12.5px;
  letter-spacing: 0.15em;
  color: #fff;
}

.marker-preview {
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: 10.5px;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.72);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
  max-width: 30ch;
  white-space: normal;
  text-align: center;
}

@media (max-width: 860px) {
  .marker-preview { display: none; }
  .marker-label { font-size: 11px; }
}


/* ── curtain: loading -> scene (era-residence split reveal) ───────────── */

.curtain {
  position: fixed;
  inset: 0;
  z-index: 999998;              /* just under the signature intro */
  pointer-events: none;
  overflow: hidden;
}

.curtain-half {
  position: absolute;
  top: -2%;
  height: 104%;
  width: 50.6%;                 /* slight overlap so no seam line shows */
  background: #05070a;
  will-change: transform;
  transform: translateX(0);
  transition: transform 1.45s cubic-bezier(.76, 0, .24, 1);
}
.curtain-l { left: 0; }
.curtain-r { right: 0; }

/* a thin bright seam that flashes as the halves part */
.curtain-seam {
  position: absolute;
  left: 50%;
  top: 0;
  width: 1px;
  height: 100%;
  margin-left: -0.5px;
  background: linear-gradient(to bottom,
    rgba(127,232,208,0) 0%,
    rgba(127,232,208,0.9) 35%,
    rgba(255,255,255,1) 50%,
    rgba(127,232,208,0.9) 65%,
    rgba(127,232,208,0) 100%);
  opacity: 0;
  transform: scaleY(0.2);
  transition: opacity .5s ease, transform .9s cubic-bezier(.22,.61,.36,1);
}

.curtain.is-open .curtain-l { transform: translateX(-100.5%); }
.curtain.is-open .curtain-r { transform: translateX(100.5%); }
.curtain.is-open .curtain-seam {
  opacity: 0;
  transform: scaleY(1.6);
  transition: opacity .85s ease .12s, transform .85s ease;
}

/* seam lights up the instant before the halves move */
.curtain:not(.is-open) .curtain-seam {
  opacity: 0.85;
  transform: scaleY(1);
}

@media (prefers-reduced-motion: reduce) {
  .curtain-half { transition-duration: .35s; }
}

/* ── the field inside the glass, and how the glass answers a touch ────────
 * Stacking inside a card, low to high: the refraction canvas (.glass-gl), the field (card-fields.js), the light
 * (.glass-spec) and the rim (.glass-rim) (glass-touch.js), then the content. No blend modes: each is plain alpha, so the
 * card costs no render surface. */

.card-field {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
}
/* Reflect Shader is computed on a coarse grid and scaled up smoothly: soft lines, not pixels */
.card-field-reflect { image-rendering: auto; }

/* the light: a soft bloom where the pointer, the finger or the tilt puts it. --gx/--gy 0..1, --gh its strength (at rest 0) */
.glass-spec {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(ellipse 62% 78% at calc(var(--gx, 0.5) * 100%) calc(var(--gy, 0.4) * 100%),
    rgba(255, 255, 255, calc(0.04 + 0.17 * var(--gh, 0))) 0%,
    rgba(255, 255, 255, calc(0.015 + 0.05 * var(--gh, 0))) 38%,
    rgba(255, 255, 255, 0) 100%);
}
.glass-card.card-contact .glass-spec {
  background: radial-gradient(ellipse 62% 78% at calc(var(--gx, 0.5) * 100%) calc(var(--gy, 0.4) * 100%),
    rgba(255, 226, 184, calc(0.04 + 0.17 * var(--gh, 0))) 0%,
    rgba(255, 226, 184, calc(0.015 + 0.05 * var(--gh, 0))) 38%,
    rgba(255, 226, 184, 0) 100%);
}

/* the rim: a hairline ring lit on the side that faces the light (--la, degrees clockwise from up), with a fainter
   answering gleam on the far edge; --gr scales it (brighter while touched) */
.glass-rim {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
  padding: 1.5px;
  opacity: var(--gr, 0.5);
  background: conic-gradient(from calc(var(--la, -135) * 1deg - 60deg),
    rgba(255, 255, 255, 0) 0deg, rgba(255, 255, 255, 0.9) 60deg, rgba(255, 255, 255, 0) 120deg,
    rgba(255, 255, 255, 0) 200deg, rgba(255, 255, 255, 0.32) 240deg, rgba(255, 255, 255, 0) 280deg,
    rgba(255, 255, 255, 0) 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}

/* Apple's two accessibility settings for the material (Reduce Transparency, Increase Contrast): denser glass, stronger edge */
@media (prefers-reduced-transparency: reduce) {
  .glass-card { background: rgba(10, 14, 20, 0.78); backdrop-filter: none; -webkit-backdrop-filter: none; }
  .card-field { opacity: 0.35; }
}
@media (prefers-contrast: more) {
  .glass-card { background: rgba(10, 14, 20, 0.6); border-color: rgba(255, 255, 255, 0.5); }
  .card-field { opacity: 0.5; }
}


/* ── fact rail: fills the docked card's dead space with substance ─────── */

.fact-rail {
  display: none;
  margin-top: clamp(20px, 3.4vh, 40px);
  padding-top: 22px;
  border-top: 1px solid rgba(255,255,255,0.13);
  gap: clamp(14px, 2.4vw, 42px);
  flex-wrap: wrap;
}
.glass-card.is-docked .fact-rail { display: flex; }

.fact {
  display: flex;
  flex-direction: column;
  gap: 3px;
  opacity: 0;
  will-change: opacity, transform;
}

.fact-k {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 1;
  font-size: clamp(13px, 1.25vw, 18px);
  letter-spacing: 0.02em;
  color: #fff;
}

.fact-v {
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 80;
  font-size: 9.5px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.52);
}

/* ── photo caption: preview text under each placeholder ──────────────── */

.ph-caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 26px 10px 9px;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: 10.5px;
  line-height: 1.35;
  color: rgba(255,255,255,0.88);
  background: linear-gradient(to top, rgba(4,6,9,0.94) 40%, rgba(4,6,9,0) 100%);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .34s ease, transform .34s cubic-bezier(.22,.61,.36,1);
}

/* the centred photo shows its caption; hover forces it on any of them */
.photo-rail figure:hover .ph-caption { opacity: 1; transform: translateY(0); }

@media (max-width: 860px) {
  .fact-rail { gap: 12px; padding-top: 16px; }
  .fact-k { font-size: 12px; }
}

/* ── campus: stained glass down the nave ─────────────────────────────── */
/* panels and captions are placed by section-campus.js every frame; their
   transform is owned there, never here */
.sg-panel {
  position: fixed; left: 0; top: 0;
  opacity: 0;
  /* transform is a homography onto the pane's projected corners
     (section-campus.js), which needs the origin at the top-left */
  transform-origin: 0 0;
  pointer-events: auto;
  will-change: transform, opacity;
}
.sg-panel > .sg-glass { position: absolute; inset: 0; }
.sg-glass > canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
}
/* the lamp behind the glass: the lit bake shows through a soft pool that
   drifts and follows the pointer (--lx/--ly from section-campus.js) */
.sg-glass > .sg-lit {
  opacity: 0;
  -webkit-mask-image: radial-gradient(circle at var(--lx, 50%) var(--ly, 34%),
    #000 0%, rgba(0,0,0,0.85) 30%, rgba(0,0,0,0.45) 62%, rgba(0,0,0,0.2) 90%);
          mask-image: radial-gradient(circle at var(--lx, 50%) var(--ly, 34%),
    #000 0%, rgba(0,0,0,0.85) 30%, rgba(0,0,0,0.45) 62%, rgba(0,0,0,0.2) 90%);
}
/* unlit glass: solid and dark, the way a window reads with no light behind it.
   The grade (saturate .7, brightness .82) is baked into the canvas
   (glass-bake.js), so no pane carries a filter; only the no-WebGL flat
   fallback, which no shader graded, still gets it from here. */
.sg-glass > .sg-dim.sg-flat { filter: saturate(0.7) brightness(0.82); }
/* coloured light spilling off a lit pane into the air around it */
.sg-panel > .sg-halo {
  position: absolute; left: -85%; top: -45%; width: 270%; height: 190%;
  display: block;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: screen;
}
/* the pool of coloured light the lit pane throws on the floor */
.sg-floor {
  position: fixed; left: 0; top: 0;
  transform-origin: 0 0;
  opacity: 0;
  pointer-events: none;
  /* overlay tints the tiles the way coloured light does, instead of
     painting a disc on top of them */
  mix-blend-mode: overlay;
  will-change: transform, opacity;
}
/* the blur, the saturation and the rounded shape are baked into the canvas
   (glass-bake.js floorFrom); the page holds no filter for them */
.sg-floor > canvas { width: 100%; height: 100%; display: block; }
.sg-cords {
  position: fixed; inset: 0;
  width: 100vw; height: 100vh;
  overflow: visible;
  pointer-events: none;
  opacity: 0;
}
.sg-cords line { stroke: rgba(30, 22, 12, 0.85); stroke-width: 1.2; }

.sg-caption {
  position: fixed;
  bottom: clamp(40px, 9vh, 110px);
  width: min(430px, 80vw);
  opacity: 0; visibility: hidden;
  pointer-events: none;
  text-shadow: 0 1px 14px rgba(0,0,0,0.85), 0 0 2px rgba(0,0,0,0.6);
}
.sg-caption.is-left  { left: clamp(20px, 6vw, 112px); }
.sg-caption.is-right { right: clamp(20px, 6vw, 112px); text-align: right; }
/* a scrim with no edge: the gradient reaches transparent inside its box */
.sg-caption::before {
  content: '';
  position: absolute;
  inset: -70px -110px -60px -110px;
  z-index: -1;
  background: radial-gradient(closest-side, rgba(8,8,11,0.72), rgba(8,8,11,0.45) 55%, rgba(8,8,11,0) 100%);
}
.campus-tag {
  position: fixed;
  top: clamp(18px, 4vh, 40px);
  left: clamp(20px, 6vw, 112px);
  margin: 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase;
  color: #7FE8D0;
  opacity: 0;
  pointer-events: none;
  text-shadow: 0 1px 10px rgba(0,0,0,0.9);
}
.sg-leader {
  position: fixed; inset: 0;
  width: 100vw; height: 100vh;
  pointer-events: none;
  opacity: 0;
  overflow: visible;
}
.sg-leader path { fill: none; stroke: rgba(127,232,208,0.75); stroke-width: 1; }
.sg-leader circle { fill: #7FE8D0; }

.sg-num {
  margin: 0 0 10px;
  text-transform: uppercase;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.22em;
  color: #7FE8D0;
}
.sg-title {
  margin: 0;
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-weight: 700;
  font-size: clamp(22px, 2.1vw, 34px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: #fff;
  text-wrap: balance;
}
.sg-org {
  margin: 8px 0 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255,255,255,0.78);
}
.sg-note {
  margin: 14px 0 0;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(14px, 1.08vw, 17px);
  line-height: 1.55;
  color: rgba(255,255,255,0.94);
}

/* the section's own words, low on the left, in the hero's voice */
.campus-head {
  position: fixed;
  left: clamp(20px, 6vw, 112px);
  bottom: clamp(40px, 9vh, 110px);
  width: min(460px, 80vw);
  opacity: 0; visibility: hidden;
  pointer-events: none;
  text-shadow: 0 1px 14px rgba(0,0,0,0.8);
}
.campus-head .hero-eyebrow { margin-bottom: 12px; }
.campus-head::before {
  content: '';
  position: absolute;
  inset: -80px -120px -70px -120px;
  z-index: -1;
  background: radial-gradient(closest-side, rgba(8,8,11,0.7), rgba(8,8,11,0.42) 55%, rgba(8,8,11,0) 100%);
}
.campus-heading {
  margin: 0;
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-weight: 700;
  font-size: clamp(22px, 2.3vw, 38px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  color: #fff;
}
.campus-body {
  margin: 12px 0 0;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(13px, 1vw, 16px);
  line-height: 1.55;
  color: rgba(255,255,255,0.88);
}
@media (max-width: 720px) {
  .sg-caption { width: 86vw; }
  .sg-title { font-size: 20px; }
  .campus-head { bottom: 28px; }
}

/* ── how it was made: the pipeline rail (section-making.js) ─────────── */
.mk {
  position: fixed; inset: 0;
  z-index: 19;                       /* over the room, under the dot cursor */
  pointer-events: none;
  opacity: 0; visibility: hidden;
  --mk-gold: #FFE9A8;
  --mk-teal: #7FE8D0;
  --mk-side: clamp(20px, 6vw, 112px);
  --mk-rail-b: clamp(26px, 5.5vh, 54px);
}
.mk > * { position: absolute; }

/* title card: once, centred, then it clears */
.mk-title {
  left: 50%; top: 46%;
  width: min(980px, 86vw);
  transform: translate(-50%, -50%);
  text-align: center;
  text-shadow: 0 2px 30px rgba(0,0,0,0.6);
}
.mk-title::before {
  content: ''; position: absolute; inset: -120px -200px; z-index: -1;
  background: radial-gradient(closest-side, rgba(8,8,11,0.62), rgba(8,8,11,0.3) 55%, rgba(8,8,11,0) 100%);
}
.mk-title .hero-eyebrow { margin-bottom: 22px; }
.mk-title-h {
  margin: 0;
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-weight: 700;
  font-size: clamp(34px, 5.2vw, 88px);
  line-height: 1.02;
  letter-spacing: -0.035em;
  color: #fff;
  text-wrap: balance;
}
.mk-title-b {
  margin: 26px auto 0;
  max-width: 560px;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(14px, 1.2vw, 18px);
  line-height: 1.6;
  color: rgba(255,255,255,0.9);
}

/* the words for each station, above the rail on the left */
.mk-words {
  left: var(--mk-side);
  /* clear of the rail's tick labels when it becomes the playhead */
  bottom: calc(var(--mk-rail-b) + 86px);
  width: min(520px, 84vw);
  opacity: 0; visibility: hidden;
  text-shadow: 0 1px 14px rgba(0,0,0,0.85), 0 0 2px rgba(0,0,0,0.6);
}
.mk-words::before {
  content: ''; position: absolute; inset: -70px -120px -60px -130px; z-index: -1;
  background: radial-gradient(closest-side, rgba(8,8,11,0.7), rgba(8,8,11,0.42) 55%, rgba(8,8,11,0) 100%);
}
.mk-words-e {
  margin: 0 0 10px;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--mk-teal);
}
.mk-words-b {
  margin: 0;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(16px, 1.45vw, 22px);
  line-height: 1.5;
  color: #fff;
}
.mk-words-n {
  margin: 12px 0 0;
  opacity: 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 12px; line-height: 1.7;
  color: rgba(255,255,255,0.75);
}

/* the rail */
.mk-rail {
  left: var(--mk-side); right: var(--mk-side);
  bottom: var(--mk-rail-b);
  height: 44px;
  opacity: 0;
}
.mk-rail-track {
  position: absolute; left: 0; right: 0; top: 0;
  height: 2px;
  background: rgba(255,255,255,0.18);
  box-shadow: 0 1px 8px rgba(0,0,0,0.6);
}
.mk-rail-fill {
  position: absolute; inset: 0;
  background: var(--mk-teal);
  transform-origin: left center;
  transform: scaleX(0);
}
.mk-rail-bead {
  position: absolute; top: 1px;
  width: 12px; height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  background: var(--mk-teal);
  box-shadow: 0 0 0 4px rgba(127,232,208,0.18), 0 0 18px rgba(127,232,208,0.8);
}
.mk-rail-stop {
  position: absolute; top: 14px;
  transform: translateX(-50%);
  display: flex; gap: 8px; align-items: baseline;
  white-space: nowrap;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase;
  color: rgba(255,255,255,0.42);
  text-shadow: 0 1px 8px rgba(0,0,0,0.9);
  transition: color .3s ease, opacity .3s ease;
}
.mk-rail-stop:first-of-type { transform: none; }
.mk-rail-stop:last-of-type  { transform: translateX(-100%); }
.mk-rail-stop b { font-weight: 400; color: rgba(255,255,255,0.3); }
.mk-rail-stop em { font-style: normal; }
.mk-rail-stop.is-done { color: rgba(255,255,255,0.62); }
.mk-rail-stop.is-on { color: #fff; }
.mk-rail-stop.is-on b { color: var(--mk-teal); }
/* bind: the rail becomes the playhead scrubber */
.mk-rail-ticks { position: absolute; inset: 0; opacity: 0; }
.mk-rail-ticks span {
  position: absolute; top: -5px;
  width: 1px; height: 12px;
  background: rgba(255,255,255,0.45);
}
.mk-rail-ticks span::after {
  content: attr(data-t) 's';
  position: absolute; top: -20px; left: 0; transform: translateX(-50%);
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: 0.1em;
  color: rgba(255,255,255,0.88);
  text-shadow: 0 1px 6px rgba(0,0,0,0.95), 0 0 2px rgba(0,0,0,0.9);
}
.mk-rail.is-bind .mk-rail-stop { opacity: 0.35; }
.mk-led {
  right: var(--mk-side);
  bottom: calc(var(--mk-rail-b) + 74px);
  width: min(460px, 44vw);
  padding: 10px 0;
  opacity: 0;
  /* a dark board, as a real LED sign has: teal dots on the bright floor
     disappeared */
  background: rgba(6, 9, 11, 0.78);
  border-radius: 8px;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
}
.mk-led > .led-board { display: block; }

/* capture: the viewfinder of the camera that shot it */
.mk-vf {
  /* the frame closes just above the rail, so the words sit inside it like
     a subtitle instead of the corners cutting through them */
  inset: 5.5vh 4vw calc(var(--mk-rail-b) + 22px);
  opacity: 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: 0.16em;
  color: rgba(255,255,255,0.92);
  text-shadow: 0 1px 6px rgba(0,0,0,0.9);
}
.mk-vf > * { position: absolute; margin: 0; }
.mk-vf-c { width: 34px; height: 34px; border: 2px solid rgba(255,255,255,0.85); filter: drop-shadow(0 1px 4px rgba(0,0,0,0.7)); }
.mk-vf-c.is-tl { left: 0; top: 0; border-right: 0; border-bottom: 0; }
.mk-vf-c.is-tr { right: 0; top: 0; border-left: 0; border-bottom: 0; }
.mk-vf-c.is-bl { left: 0; bottom: 0; border-right: 0; border-top: 0; }
.mk-vf-c.is-br { right: 0; bottom: 0; border-left: 0; border-top: 0; }
.mk-vf-g { background: rgba(255,255,255,0.16); }
.mk-vf-g.is-v1, .mk-vf-g.is-v2 { top: 0; bottom: 0; width: 1px; }
.mk-vf-g.is-v1 { left: 33.33%; } .mk-vf-g.is-v2 { left: 66.66%; }
.mk-vf-g.is-h1, .mk-vf-g.is-h2 { left: 0; right: 0; height: 1px; }
.mk-vf-g.is-h1 { top: 33.33%; } .mk-vf-g.is-h2 { top: 66.66%; }
.mk-vf-focus {
  left: 50%; top: 50%; width: 76px; height: 56px;
  transform: translate(-50%, -50%);
  border: 1.5px solid rgba(255, 214, 90, 0.9);
  animation: mk-focus 2.4s ease-in-out infinite;
}
@keyframes mk-focus { 0%, 100% { transform: translate(-50%, -50%) scale(1); } 50% { transform: translate(-50%, -50%) scale(0.9); } }
.mk-vf-rec { left: 52px; top: 8px; display: flex; align-items: center; gap: 8px; }
.mk-vf-rec i { width: 10px; height: 10px; border-radius: 50%; background: #FF3B30; box-shadow: 0 0 10px #FF3B30; animation: mk-rec 1s steps(1) infinite; }
@keyframes mk-rec { 50% { opacity: 0.15; } }
.mk-vf-tc { left: 50%; top: 8px; transform: translateX(-50%); font-variant-numeric: tabular-nums; font-size: 14px; }
.mk-vf-clip { right: 52px; top: 8px; font-variant-numeric: tabular-nums; }
.mk-vf-dev { right: 52px; top: 30px; font-size: 10px; color: rgba(255,255,255,0.7); }

/* reconstruct: the scan line and its labels */
.mk-scan { inset: 0; width: 100vw; height: 100vh; overflow: visible; opacity: 0; }
.mk-scan-line { stroke: url(#mk-scan-grad); stroke-width: 2; }
.mk-scan-glow { stroke: rgba(255, 214, 120, 0.35); stroke-width: 14; filter: blur(6px); }
.mk-scan-tag {
  top: 16vh; left: 0; margin: 0;
  opacity: 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.26em; text-transform: uppercase;
  color: var(--mk-gold);
  white-space: nowrap;
  text-shadow: 0 1px 10px rgba(0,0,0,0.95);
}
.mk-scan-tag.is-l::before { content: '\2190  '; }
.mk-scan-tag.is-r::after  { content: '  \2192'; }
.mk-count, .mk-count-lab { left: 0; margin: 0; opacity: 0; white-space: nowrap; text-shadow: 0 2px 18px rgba(0,0,0,0.9); }
.mk-count {
  top: 20vh;
  font-family: 'Geist Pixel', ui-monospace, monospace;
  font-variation-settings: 'ELSH' 20;
  font-size: clamp(34px, 4.2vw, 64px);
  line-height: 1;
  color: var(--mk-gold);
  font-variant-numeric: tabular-nums;
}
.mk-count-lab {
  top: calc(20vh + clamp(42px, 5vw, 74px));
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase;
  color: rgba(255,255,255,0.75);
}

/* direct: the flight plan, large */
.mk-plan {
  left: 50%; top: 10vh;
  width: min(820px, 90vw);
  transform: translateX(-50%);
  opacity: 0; visibility: hidden;
  padding: 10px;
  border-radius: 12px;
  background: rgba(8, 10, 13, 0.62);
  border: 1px solid rgba(255,255,255,0.12);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 24px 60px -20px rgba(0,0,0,0.7);
}
.mk-plan-svg { display: block; width: 100%; height: auto; overflow: visible; }
.mk-plan .pl-room { fill: rgba(255,255,255,0.03); stroke: rgba(255,255,255,0.24); stroke-width: 1; }
.mk-plan .pl-path { fill: none; stroke: var(--mk-teal); stroke-width: 1.8; stroke-dasharray: 6 6; }
.mk-plan .pl-kf circle { fill: #0a0d10; stroke: var(--mk-teal); stroke-width: 1.8; }
.mk-plan .pl-kf text, .mk-plan .pl-end {
  font-family: ui-monospace, 'IBM Plex Mono', monospace; font-size: 11px; letter-spacing: 0.1em;
  fill: rgba(255,255,255,0.78);
}
.mk-plan .pl-end { font-size: 9px; letter-spacing: 0.24em; fill: rgba(255,255,255,0.45); }
.mk-plan .pl-label { font-family: ui-monospace, 'IBM Plex Mono', monospace; font-size: 11px; letter-spacing: 0.22em; fill: var(--mk-teal); }
.mk-plan .pl-cone { fill: url(#mk-cone-grad); }
.mk-plan .pl-cam { fill: var(--mk-gold); stroke: #0a0d10; stroke-width: 2; }

/* clean: the artefacts */
.mk-floaters { position: fixed; left: 0; top: 0; z-index: 18; pointer-events: none; opacity: 0; }

@media (max-width: 720px) {
  /* numbers only: the station's name is already in the words above */
  .mk-rail-stop em { display: none; }
  .mk-words { bottom: calc(var(--mk-rail-b) + 72px); }
  .mk-rail-ticks span:nth-child(even)::after { display: none; }
  .mk-vf-tc { left: 52px; top: 30px; transform: none; font-size: 12px; }
  .mk-vf { inset: 4vh 5vw calc(var(--mk-rail-b) + 220px); }
  .mk-vf-dev { display: none; }
  .mk-led { left: var(--mk-side); right: auto; width: 84vw; bottom: calc(var(--mk-rail-b) + 190px); }
}

/* ── extras (Originkit): encrypt, link preview ────────────── */
.enc-link { position: relative; overflow: hidden; isolation: isolate; }
.enc-sizer { visibility: hidden; }
.enc-live  { position: absolute; left: 0; top: 0; white-space: nowrap; }
/* the sweep: an accent band with a long trail, down then back up */
.enc-band {
  position: absolute; inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(to bottom, transparent 20%, rgba(127,232,208,0.34) 50%, transparent 51.5%);
  transform: translateY(-100%) scaleX(1.25) scaleY(1.25);
}
.enc-link.is-enc .enc-band { animation: enc-sweep 0.9s linear 1 forwards; }
@keyframes enc-sweep {
  0%   { opacity: 1; transform: translateY(-100%) scaleX(1.25) scaleY(1.25); }
  50%  { opacity: 1; transform: translateY(100%)  scaleX(1.25) scaleY(1.25); }
  50.1%{ opacity: 1; transform: translateY(100%)  scaleX(1.25) scaleY(-1.25); }
  100% { opacity: 0; transform: translateY(-100%) scaleX(1.25) scaleY(-1.25); }
}

.lp-wrap { position: relative; display: inline-block; }
.lp-card {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 14px);
  z-index: 30;
  overflow: hidden;
  border-radius: 12px;
  background: #0b0d10;
  border: 1px solid rgba(255,255,255,0.14);
  box-shadow: 0 18px 40px rgba(0,0,0,0.5), 0 2px 8px rgba(0,0,0,0.4);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform-origin: 50% 100%;
}
.lp-card > img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }

/* ── the pointer (cursor-kit.js): User Cursor, Click Effects ──────────────────────────────────────
 * Above the signature intro (z-index 999999): the Dot Cursor sat at 100000, under it, so on the signature screen the native
 * pointer was hidden and the dot was drawn out of sight: the visitor had no cursor at all until the intro lifted. */
.cur-root { --cur-size: 48px; --cur-pill-bg: #7FE8D0; --cur-pill-fg: #06110f; position: fixed; inset: 0; z-index: 1000000; pointer-events: none; overflow: hidden; }
.cur-arrow {
  position: absolute; left: 0; top: 0; width: var(--cur-size); height: var(--cur-size); transform-origin: 17.857% 10.714%;     /* the glyph's tip, 5/28 and 3/28 */
  opacity: 0; pointer-events: none; will-change: transform; transition: opacity .16s ease, filter .2s ease;
}
.cur-arrow svg { display: block; overflow: visible; width: 100%; height: 100%; }
.cur-ghost { transition: none; }
/* the pill: the component's own proportions, all from --cur-size (padding .18 / .36, type .43, a full round at one line). Not there until it has something to say. */
.cur-pill {
  position: absolute; left: 0; top: 0; box-sizing: border-box; width: max-content; max-width: min(78vw, 24em);
  padding: calc(var(--cur-size) * .18) calc(var(--cur-size) * .36); border-radius: calc(var(--cur-size) * .42);
  background: var(--cur-pill-bg); color: var(--cur-pill-fg); box-shadow: 0 4px 12px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .08);
  font-family: 'Geist Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; font-weight: 600; font-size: calc(var(--cur-size) * .43); line-height: 1.1; letter-spacing: .1px;
  opacity: 0; visibility: hidden; transform-origin: 0% 50%; will-change: transform, opacity; user-select: none; -webkit-user-select: none; pointer-events: none;
}
.cur-pill.cur-ghost { visibility: visible; }
.cur-pill.is-bubble { transition: opacity .22s ease, transform .32s cubic-bezier(.2, .9, .3, 1.25); }
/* the signature screen's centre takes no pointer (viewer.html .sig-center-wrap), so over the signature or the loading bar the target would be the screen itself and the
   pill could not tell them apart: they take it (a click on them still reaches the screen's own onclick, which lets the visitor in) */
#sig-svg, .sig-track, .sig-caption { pointer-events: auto; }
.cur-fx { position: absolute; width: 0; height: 0; pointer-events: none; }
.cur-tick, .cur-dot, .cur-ring { position: absolute; display: block; pointer-events: none; }
.cur-tick { left: 0; transform-origin: 0 50%; }
.cur-dot, .cur-ring { border-radius: 50%; }
.cur-ring { box-sizing: content-box; }
.cur-debug {
  position: fixed; left: 10px; top: 10px; z-index: 1000003; margin: 0; padding: 8px 10px; box-sizing: border-box; max-width: min(460px, calc(100vw - 20px));
  font: 11px/1.55 ui-monospace, 'IBM Plex Mono', monospace; color: #7FE8D0; background: rgba(8, 10, 12, .88); border: 1px solid rgba(127, 232, 208, .4);
  border-radius: 6px; white-space: pre-wrap; pointer-events: none;
}
html.cur-on, html.cur-on * { cursor: none !important; }
html.cur-on.cur-text, html.cur-on.cur-text * { cursor: text !important; }

/* ── keycap (Originkit IsometricButton) on the contact links ─────────── */
.kc {
  --kc-accent: #7FE8D0;
  --kc-fill: #10161a;
  --kc-edge: #0a0e11;
  position: relative;
  display: inline-block;
  margin: 14px 26px 18px 0;
  vertical-align: middle;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase;
}
.kc-sizer { display: block; visibility: hidden; padding: 12px 24px; white-space: nowrap; }
/* the isometric camera: no perspective, so parallel edges stay parallel.
   The component's own 49 / -37 is a hero-button angle; on a level card it
   read as two keys knocked askew. 42 / -8 keeps the prism, sits straight. */
.kc-scene {
  position: absolute; inset: 0;
  transform: translateY(3px) rotateX(42deg) rotateZ(-8deg);
  transform-style: preserve-3d;
  pointer-events: none;
}
.kc-refl, .kc-glow, .kc-side, .kc-top { position: absolute; inset: 0; border-radius: 11px; }
/* NEVER a blend mode in here: a blended element leaves the preserve-3d
   scene and the key reads as permanently pressed (the component's warning) */
.kc-refl {
  background: linear-gradient(120deg, #5FD0E8 0%, var(--kc-accent) 50%, #B4F5C8 110%);
  transform: translateZ(calc(-1 * var(--kc-float))) scaleZ(-1);
  filter: blur(var(--kc-refl-blur)) brightness(calc(var(--kc-bright) * 0.95));
  opacity: var(--kc-refl);
}
.kc-stand {
  position: absolute; inset: 0;
  transform-origin: center bottom;
  transform: rotateX(var(--kc-stand));
  transform-style: preserve-3d;
}
.kc-glow {
  background: linear-gradient(120deg, #5FD0E8 0%, var(--kc-accent) 50%, #B4F5C8 110%);
  transform: translateZ(var(--kc-float));
  box-shadow: 0 0 calc(16px * var(--kc-spread)) calc(2px * var(--kc-spread)) rgba(127,232,208,0.8),
              0 0 calc(44px * var(--kc-spread)) calc(6px * var(--kc-spread)) rgba(127,232,208,0.4);
  filter: brightness(var(--kc-bright));
}
.kc-side { background: var(--kc-edge); box-shadow: 0 0 0 1px var(--kc-edge); }
.kc-top {
  display: flex; align-items: center; justify-content: center;
  white-space: nowrap;
  background: linear-gradient(135deg, #1b2226 0%, #10161a 75%);
  border: 1px solid rgba(255,255,255,0.1);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.08);
  color: color-mix(in srgb, #FFFFFF var(--kc-text-mix), var(--kc-accent));
  text-shadow: 0 0 var(--kc-text-glow) var(--kc-accent), 0 0 calc(var(--kc-text-glow) * 2) var(--kc-accent);
}
/* the real link: unrotated, invisible, pinned at the rest height */
.card-contact .beat-links a.kc-hit {
  position: absolute; inset: -6px;
  margin: 0; padding: 0;
  border: 0; border-radius: 11px;
  color: transparent !important;
  background: transparent;
  font-size: 0;
}
.card-contact .beat-links a.kc-hit:focus-visible { outline: 2px solid var(--kc-accent); outline-offset: 6px; }

/* ── Aholo's orbit controls stay out of site mode ─────────────────────────
   The viewer's own camera controller listens on this layer for wheel, drag,
   pinch and double-click. Under ?site=1 the scroll spine drives the camera,
   but the controller still heard every wheel tick as a ZOOM: each tick
   dollied the camera ~30 cm in, and the next frame the spine put it back on
   the path — the "abrupt in-and-out, then smooth" on every notch, worst when
   scrolling slowly. Touch did the same through its pinch/drag handlers.
   Scroll is read on window, so it still arrives; nothing else needs this
   layer. */
html.site-mode [class*="MultiScene3D-event-layer"] { pointer-events: none !important; }

/* ── goodbye: leave an email on the Originkit Mech Keyboard ───────────── */
.gb {
  position: fixed; inset: 0;
  z-index: 19;
  opacity: 0; visibility: hidden;
  pointer-events: none;
  display: flex; flex-direction: column; align-items: center;
}
html.gb-on .gb { pointer-events: auto; z-index: 22; }      /* docked: over the cards' layer (20), so a stray card can never sit over the form */
.gb::before {                    /* the vault is bright: a soft scrim, no edges */
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(90% 70% at 50% 40%, rgba(8,8,11,0.72), rgba(8,8,11,0.45) 60%, rgba(8,8,11,0.25) 100%);
}
.gb-box {
  margin-top: clamp(40px, 9vh, 110px);
  width: min(640px, 88vw);
  text-align: center;
  text-shadow: 0 1px 14px rgba(0,0,0,0.85);
}
.gb-box .hero-eyebrow { margin-bottom: 14px; }
/* the name (vector-wordmark.js): the first thing on the screen, the box follows it closely */
.gb-wordmark {
  position: relative; flex: 0 0 auto;
  width: min(880px, 94vw);
  height: clamp(92px, 17vh, 168px);
  margin-top: clamp(24px, 5vh, 64px);
  touch-action: none;
}
.gb-wordmark > .wm-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.wm-label {
  position: absolute; left: 0; top: 0; pointer-events: none; white-space: nowrap;
  font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.08em;
  color: #7fe8d0; opacity: calc(0.6 * var(--wm-in, 1));
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
}
.gb-wordmark + .gb-box { margin-top: clamp(2px, 1vh, 12px); }
@media (max-width: 720px) {
  .gb-wordmark { width: min(640px, calc(100vw - 104px)); height: clamp(50px, 10vh, 85px); margin-top: clamp(8px, 1.8vh, 16px); }
  .gb-wordmark + .gb-box { margin-top: clamp(2px, 0.8vh, 8px); }
}
@media (max-height: 720px) {
  .gb-wordmark { height: clamp(50px, 10vh, 80px); margin-top: clamp(6px, 1.5vh, 14px); }
}
@media (max-height: 520px) {
  .gb-wordmark { display: none; }
  .gb-wordmark + .gb-box, .gb-box { margin-top: clamp(14px, 3.5vh, 30px); }
}
.gb-heading {
  margin: 0;
  font-family: 'Geist Sans', system-ui, -apple-system, sans-serif;
  font-weight: 700;
  font-size: clamp(30px, 4vw, 60px);
  line-height: 1.02; letter-spacing: -0.035em;
  color: #fff;
}
.gb-body {
  margin: 14px auto 0; max-width: 520px;
  font-family: 'Geist Sans', system-ui, sans-serif;
  font-size: clamp(14px, 1.15vw, 17px); line-height: 1.55;
  color: rgba(255,255,255,0.88);
}
.gb-form { margin: 26px auto 0; display: flex; gap: 12px; align-items: stretch; justify-content: center; }
.gb-input {
  flex: 1 1 auto; min-width: 0; max-width: 460px;
  padding: 14px 18px;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: max(16px, clamp(15px, 1.3vw, 19px)); letter-spacing: 0.02em;      /* 16 px at least: iOS zooms the page in on a field under 16 px, and this page cannot be panned back */
  color: #fff;
  background: rgba(8, 10, 13, 0.62);
  border: 1px solid rgba(127,232,208,0.45);
  border-radius: 10px;
  outline: none;
  caret-color: #7FE8D0;
  /* the page sets user-select: none on html and body (viewer.html), and WebKit has been known to take that into a text field */
  -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; touch-action: manipulation;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.gb-input::placeholder { color: rgba(255,255,255,0.38); }
.gb-input:focus { border-color: #7FE8D0; box-shadow: 0 0 0 3px rgba(127,232,208,0.18); }
.gb-send {
  flex: 0 0 auto;
  padding: 0 20px;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 13px; letter-spacing: 0.16em; text-transform: uppercase;
  color: #0a0d10; background: #7FE8D0;
  border: 0; border-radius: 10px; cursor: pointer;
}
.gb-send:disabled { opacity: 0.6; cursor: default; }
.gb-send:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.gb-status {
  min-height: 1.4em; margin: 14px 0 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 12px; letter-spacing: 0.12em;
  color: #7FE8D0;
}
.gb-status.is-quiet { color: rgba(255,255,255,0.6); }
.gb-sr, .gb-hp {
  position: absolute !important; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}
.gb-kb {
  position: relative;
  width: min(1020px, 96vw);
  flex: 1 1 auto;
  min-height: 200px; max-height: 52vh;
  margin-top: clamp(4px, 2vh, 24px);
}
.gb-foot {
  position: absolute; left: 0; right: 0; bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  pointer-events: none;
}
.gb-hint {
  margin: 0; text-align: center; opacity: 0;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase;
  color: #fff; text-shadow: 0 1px 8px rgba(0,0,0,0.9);
  pointer-events: none;
}
@media (max-width: 720px) {
  /* narrower by the section rail on both sides, so its dots never sit over the field or button */
  .gb-box { width: min(640px, calc(100vw - 104px)); }
  .gb-box .hero-eyebrow { margin-bottom: 8px; }
  .gb-heading { font-size: clamp(24px, 6vw, 32px); }
  .gb-body { margin-top: 8px; font-size: clamp(13px, 3.5vw, 15px); line-height: 1.45; }
  .gb-form { flex-direction: column; margin-top: 14px; gap: 8px; }
  .gb-input { padding: 11px 14px; }
  .gb-send { padding: 11px 20px; }
  .gb-status { margin-top: 6px; min-height: 1.2em; }
  .gb-kb { max-height: 48vh; margin-top: clamp(4px, 1.5vh, 16px); }
  .gb-foot { gap: 6px; padding: 6px 0 calc(8px + env(safe-area-inset-bottom, 0px)); }
  .gb-privacy { font-size: 9px; line-height: 1.35; }
}
@media (max-width: 720px) and (max-height: 560px) {
  .gb-wordmark + .gb-box, .gb-box { margin-top: clamp(6px, 1.5vh, 14px); }
  .gb-box .hero-eyebrow { margin-bottom: 4px; }
  .gb-heading { font-size: 24px; }
  .gb-body { margin-top: 4px; font-size: 12px; line-height: 1.35; }
  .gb-form { margin-top: 8px; gap: 6px; }
  .gb-input { padding: 9px 12px; }
  .gb-send { padding: 9px 16px; }
  .gb-status { margin-top: 4px; min-height: 1.1em; }
  .gb-foot { gap: 4px; padding: 4px 0 calc(6px + env(safe-area-inset-bottom, 0px)); }
}
@media (max-width: 720px), (max-height: 520px) {
  .gb-kb { min-height: 0; }
  .gb-kb.is-cramped { visibility: hidden; }
  .gb-foot { position: static; flex: 0 0 auto; margin-top: auto; width: 100%; }
}
/* the viewer sizes every canvas to the viewport; this one belongs to its box */
/* fireworks.js: over the scrim, under the form */
.gb > .fw-canvas { position: absolute !important; inset: 0 !important; width: 100% !important; height: 100% !important;
                   z-index: -1; pointer-events: none; transition: opacity 0.6s; }
.gb-kb > canvas { position: absolute !important; inset: 0 !important; width: 100% !important; height: 100% !important; }
/* the scramble effect positions its text absolutely: centre it on the button */
.gb-send.enc-link { position: relative; display: inline-flex; align-items: center; justify-content: center; }
.gb-send .enc-live { inset: 0; display: flex; align-items: center; justify-content: center; }

/* ── Axis Cursor (extras.js axisCursor), Making › Direct ─────────────── */
.ax { position: fixed; inset: 0; z-index: 40; pointer-events: none; opacity: 0; transition: opacity 0.2s ease-in-out; }
.ax.is-on { opacity: 1; }
.ax-v, .ax-h, .ax-dot, .ax-label { position: absolute; left: 0; top: 0; will-change: transform; }
.ax-v { width: 1px; height: 100vh; margin-left: -0.5px; background: rgba(127, 232, 208, 0.55); }
.ax-h { height: 1px; width: 100vw; margin-top: -0.5px; background: rgba(127, 232, 208, 0.55); }
.ax-dot { width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%; background: #7FE8D0; }
.ax-label {
  margin: 12px 0 0 12px; padding: 5px 9px; border-radius: 2px; white-space: nowrap;
  font-family: ui-monospace, 'IBM Plex Mono', monospace; font-size: 11px; letter-spacing: 0.08em; line-height: 1;
  background: #7FE8D0; color: #06110f;
}
html.ax-on .cur-root { opacity: 0 !important; }

/* ── site-nav.js: the section rail, Skip and Tour ─────────────────────── */
.nav-rail {
  position: fixed; right: clamp(10px, 1.6vw, 26px); top: 50%;
  transform: translate(6px, -50%);
  z-index: 30;
  display: flex; flex-direction: column; align-items: flex-end; gap: 18px;
  opacity: 0; visibility: hidden;
  transition: opacity .5s ease, transform .5s cubic-bezier(.22,.61,.36,1), visibility .5s;
}
.nav-rail.is-on { opacity: 1; visibility: visible; transform: translate(0, -50%); }
.nav-rail ol {
  list-style: none; margin: 0; padding: 7px 5px; display: flex; flex-direction: column; gap: 4px;
  /* a quiet backing so the dots read over a bright room */
  background: rgba(8,10,12,.42); border-radius: 999px;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.nav-mark {
  --m: 0;
  --base: 1;                                   /* the dot's resting size; the current section's is bigger (below) */
  /* The button fills the capsule and centres what it holds, so the dots and the Tour / Quality / Sound rings (the widest thing in
     the capsule) share ONE vertical axis. It used to shrink to fit its 6 px dot and sit at the capsule's left edge: the dots ran
     8 px left of the rings (6 px on a phone). */
  display: flex; align-items: center; justify-content: center; gap: 10px; width: 100%; box-sizing: border-box;
  background: none; border: 0; cursor: pointer; color: #fff;
  /* the dock magnify opens the spacing round the marker you are near */
  padding: calc(5px + var(--m) * 5px) 2px;
  -webkit-tap-highlight-color: transparent;
}
.nav-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: rgba(255,255,255,.38);
  box-shadow: 0 0 0 1px rgba(0,0,0,.35);
  transform: scale(calc(var(--base) + var(--m) * 1.35));
  transition: background .3s ease, box-shadow .3s ease;
}
.nav-mark.is-current .nav-dot {
  background: #7FE8D0;
  box-shadow: 0 0 0 1px rgba(0,0,0,.35), 0 0 10px rgba(127,232,208,.8);
}
.nav-mark.is-current { --base: 1.45; }
.nav-mark { position: relative; }
.nav-label {
  /* one column: every label ends 6 px clear of the capsule (the button starts 5 px inside it) */
  position: absolute; right: calc(100% + 11px); top: 50%; margin-top: -9px;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase; white-space: nowrap;
  color: rgba(255,255,255,.92);
  background: rgba(8,10,12,.72); padding: 4px 8px; border-radius: 3px;
  /* slides out with the magnify: nearest label fully, neighbours partly */
  opacity: var(--m); transform: translateX(calc((1 - var(--m)) * 8px)); pointer-events: none;
  line-height: 10px;
}
.nav-mark:hover .nav-label, .nav-mark:focus-visible .nav-label { opacity: 1; transform: none; }
.nav-mark.is-current .nav-label { color: #7FE8D0; }
.nav-mark:focus-visible { outline: none; }
.nav-mark:focus-visible .nav-dot { box-shadow: 0 0 0 2px #7FE8D0; }
/* the rail shows the current label on its own while you are on a card */
html.is-docked .nav-mark.is-current .nav-label { opacity: .85; transform: none; }

.nav-tour, .nav-skip {
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255,255,255,.88);
  background: rgba(8,10,12,.6);
  border: 1px solid rgba(255,255,255,.18); border-radius: 999px;
  padding: 7px 12px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: border-color .25s ease, color .25s ease, background .25s ease;
}
.nav-tour:hover, .nav-skip:hover { border-color: #7FE8D0; color: #7FE8D0; }
.nav-tour:focus-visible, .nav-skip:focus-visible { outline: 2px solid #7FE8D0; outline-offset: 3px; }
.nav-tour { display: inline-flex; align-items: center; gap: 8px; }
.nav-tour-icon {
  width: 0; height: 0;
  border-left: 6px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent;
}
.nav-tour.is-on { color: #07100e; background: #7FE8D0; border-color: #7FE8D0; }
.nav-tour.is-on .nav-tour-icon { width: 7px; height: 7px; border: 0; background: currentColor; }

.nav-skip {
  position: fixed; right: clamp(14px, 2.2vw, 34px); bottom: clamp(16px, 3.5vh, 34px);
  z-index: 31;
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .45s ease, transform .45s cubic-bezier(.22,.61,.36,1), visibility .45s,
              border-color .25s ease, color .25s ease;
}
.nav-skip.is-on { opacity: 1; visibility: visible; transform: none; }

@media (max-width: 720px) {
  .nav-rail { right: 6px; gap: 12px; }
  .nav-rail ol { gap: 0; }
  .nav-mark { padding: 6px 4px; }
  .nav-label { display: none; }
  .nav-tour { padding: 6px 9px; }
  .nav-tour-label { display: none; }
  .nav-skip { right: 12px; bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }
}
@media (prefers-reduced-motion: reduce) {
  .nav-rail, .nav-skip, .nav-dot, .nav-label { transition: none; }
}

/* ── phones: fit every card to the screen ─────────────────────────────── */
@media (max-width: 720px) {
  /* a docked card never taller than the screen it is on */
  .glass-card.is-docked { max-height: calc(100svh - 28px); }
  .glass-card.is-docked > .card-content { padding: 20px 18px 16px; }
  .glass-card.is-docked .card-heading { font-size: 21px; margin-bottom: 14px; }
  .glass-card.is-docked .card-lead { font-size: 17px; margin-bottom: 8px; }
  .glass-card.is-docked .card-body { font-size: 14.5px; line-height: 1.55; }
  /* the Ghalib couplet stays, its gloss goes: two lines instead of five */
  .glass-card.is-docked .card-epigraph { margin-bottom: 14px; }
  .glass-card.is-docked .epi-line { font-size: 12px; line-height: 1.5; }
  .glass-card.is-docked .epi-foot { display: none; }
  /* facts: a tight two-column grid instead of a row that ran off the card */
  .glass-card.is-docked .fact-rail {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px; padding-top: 12px;
  }
  .fact-rail .fact-k { font-size: 12px; }
  .fact-rail .fact-v { font-size: 9px; letter-spacing: .1em; }
  .photo-rail { height: clamp(180px, 30svh, 260px); }

  /* Projects: the text column is the screen minus a gutter */
  .card-projects.is-bare .card-main { width: calc(100vw - 36px); max-width: none; }
  .card-projects.is-bare .card-body { font-size: 15px; line-height: 1.5; }
  .card-projects.is-bare .beat-chips { flex-wrap: wrap; row-gap: 4px; }
  .card-projects.is-bare.is-docked > .card-content { padding: 0 18px calc(46px + env(safe-area-inset-bottom, 0px)); }
  /* phone, once measured (section-projects.js fitFlow): the description hangs
     from just under the slab instead of standing on the foot of the screen */
  .card-projects.is-bare.proj-top.is-docked > .card-content { justify-content: flex-start; padding-top: var(--proj-text-top, 50vh); }
  .card-projects.is-bare.proj-top .beat-block { top: 0; bottom: auto; }
  /* ...and the titles stand on one foot just above the slab */
  html.proj-top-on .card-word-slot { align-items: flex-end; padding-top: 0; bottom: auto; height: var(--proj-word-foot, 20vh); }

  /* Making: the words sit on the bright chapel floor; give them a real backing */
  .mk::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 58%;
    background: linear-gradient(to top, rgba(6,7,9,.86), rgba(6,7,9,.55) 55%, rgba(6,7,9,0));
    z-index: -1; pointer-events: none;
  }
  .mk-words { width: calc(100vw - 40px); }
  .mk-words-b { font-size: 15.5px; }

  /* the rail would sit on the hero headline; it comes in once that goes */
  html.hero-up .nav-rail { opacity: 0; visibility: hidden; }
}
@media (max-width: 720px) and (max-height: 700px) {
  .glass-card.is-docked .fact-rail { display: none; }
}

/* ── Arrow Reveal (extras.js arrowReveal): the Skip button ────────────── */
.ar {
  --ar-ease: cubic-bezier(.44, 0, .56, 1);
  overflow: hidden;
  display: inline-flex; align-items: center; gap: 14px;
  padding: 3px 3px 3px 16px !important;
  border-radius: 999px !important;
}
.ar:not(.nav-skip) { position: relative; }   /* the Skip button stays fixed */
.nav-skip.ar {
  transition: opacity .45s ease, transform .46s var(--ar-ease), visibility .45s, border-color .25s ease;
}
.ar.is-on:active { transform: scale(.97); }
.ar-label { position: relative; z-index: 1; transition: transform .46s var(--ar-ease); }
.ar-slot { flex: none; width: 26px; height: 26px; }
.ar-badge {
  position: absolute; z-index: 2; left: var(--ar-cx, 0); top: var(--ar-top, 3px);
  width: 26px; height: 26px; border-radius: 50%;
  background: #7FE8D0; transform: scale(1); transform-origin: center;
  transition: transform .46s var(--ar-ease); pointer-events: none;
}
.ar-icon {
  position: absolute; z-index: 3; left: var(--ar-cx, 0); top: var(--ar-top, 3px);
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  color: #07100e; pointer-events: none;
  transform: translateX(0) rotate(var(--ar-rest, 90deg));
  transition: transform .46s var(--ar-ease);
}
.ar-icon svg { width: 58%; height: 58%; }
.ar:hover .ar-badge, .ar:focus-visible .ar-badge { transform: scale(var(--ar-scale, 4)); }
.ar:hover .ar-icon, .ar:focus-visible .ar-icon { transform: translateX(var(--ar-x, 0)) rotate(var(--ar-hover, 90deg)); }
.ar:hover .ar-label, .ar:focus-visible .ar-label { transform: translateX(-8px); }
/* the same look, worn while cursor-kit.js's ghost clicks the button (enter-nudge.js, the cursor kit's ghostTo) */
.ar.is-ghost .ar-badge { transform: scale(var(--ar-scale, 4)); }
.ar.is-ghost .ar-icon { transform: translateX(var(--ar-x, 0)) rotate(var(--ar-hover, 90deg)); }
.ar.is-ghost .ar-label { transform: translateX(-8px); }
.nav-skip.ar { color: rgba(255,255,255,.92); }
.nav-skip.ar:hover { color: rgba(255,255,255,.92); border-color: #7FE8D0; }

/* ── Orbit Border (extras.js orbitBorder): the Tour button ────────────── */
@property --ob-arc { syntax: '<angle>'; inherits: true; initial-value: 72deg; }
.ob {
  --ob-arc: 72deg;                    /* 20% of the ring at rest */
  --ob-color: #7FE8D0;
  position: relative; isolation: isolate;
  padding: 1px !important;             /* the band the comet rides */
  background: rgba(255,255,255,.16) !important;
  border: 0 !important;
  transition: --ob-arc .45s cubic-bezier(.22,.61,.36,1), transform .6s cubic-bezier(.44,0,.56,1) !important;
}
.ob:hover { --ob-arc: 288deg; transform: scale(1.04); }
.ob:active { transform: scale(.97); }
.ob.is-live { --ob-arc: 288deg; }
.ob-band {
  position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none; overflow: hidden;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
.ob-comet {
  position: absolute; left: 50%; top: 50%;
  width: var(--ob-side, 120px); height: var(--ob-side, 120px);
  margin: calc(var(--ob-side, 120px) / -2) 0 0 calc(var(--ob-side, 120px) / -2);
  animation: obSpin 3.33s linear infinite;
}
.ob.is-live .ob-comet { animation-duration: 1.66s; }
.ob-comet > i {
  position: absolute; inset: 0;
  background: conic-gradient(from 0deg, var(--ob-color) 0deg,
              var(--ob-color) calc(var(--ob-arc) * var(--ob-arc) / 360deg),
              rgba(0,0,0,0) var(--ob-arc), rgba(0,0,0,0) 360deg);
}
.ob-glow { filter: blur(13px); }
.ob-core { opacity: .45; }
@keyframes obSpin { to { transform: rotate(-360deg); } }
.ob-face {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 12px; border-radius: inherit;
  background: rgba(8,10,12,.82);
}
.nav-tour.ob { padding: 1px !important; }
.nav-tour.ob.is-on .ob-face { background: rgba(8,10,12,.82); color: #7FE8D0; }
.nav-tour.ob.is-on { color: #7FE8D0; background: rgba(127,232,208,.25) !important; }
/* the Sound pill (site-nav.js, sound.js): a speaker, waves when on, a cross when off */
.nav-sound-icon { width: 13px; height: 13px; flex: none; }
.nav-sound .snd-on { display: none; }
.nav-sound.is-on .snd-on { display: inline; }
.nav-sound.is-on .snd-off { display: none; }
.nav-sound.is-on .nav-sound-icon { width: 13px; height: 13px; background: none; }
@media (prefers-reduced-motion: reduce) { .ob-comet { animation: none; } }
@media (max-width: 720px) { .ob-face { padding: 6px 9px; } }

/* ── during the orb transition, nothing of the room you are leaving ─────
   The site's layers are painted from the camera loop, and the orb runs its
   own; a jump from the rail or the tour starts it while a card is still up,
   so that card froze on screen through the whole transition. A scroll
   never showed this because every card has sunk by the end of a room. */
html.site-transit #site-anchors,
html.site-transit .card-words,
html.site-transit .carousel-stage,
html.site-transit .dock-vignette,
html.site-transit #site-hero,
html.site-transit .mk,
html.site-transit .gb,
html.site-transit .ax,
html.site-transit .nav-skip {
  opacity: 0 !important;
  visibility: hidden !important;
  transition: opacity .25s ease, visibility 0s linear .25s !important;
}

.gb-privacy {
  width: min(560px, 90vw); margin: 0; text-align: center;
  font-family: ui-monospace, 'IBM Plex Mono', monospace; font-size: 9.5px; letter-spacing: .06em; line-height: 1.5;
  color: rgba(255,255,255,.45);
}

/* ── Projects video card under the pointer (project-hover.js) ──────────
   z 18: over the cover flow's canvas (17), under the cards' text (20) */
.ph-wrap { position: fixed; z-index: 18; perspective: 1100px; pointer-events: none; visibility: hidden; }
.ph-wrap.is-on { visibility: visible; }
.ph-card { position: absolute; inset: 0; overflow: hidden; transform-style: preserve-3d; will-change: transform; background: #0b0e11; }
.ph-media { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.ph-glare, .ph-glow { position: absolute; inset: 0; pointer-events: none; opacity: 0; }
.ph-glare { mix-blend-mode: soft-light; }
body.ph-point, body.ph-point * { cursor: pointer !important; }
/* the Visit pill: Arrow Reveal, riding the cursor */
.ph-visit, .ph-visit.ar {
  position: fixed; left: 0; top: 0; z-index: 45; pointer-events: none;
  font-family: ui-monospace, 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255,255,255,.92); background: rgba(8,10,12,.72);
  border: 1px solid rgba(127,232,208,.55);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .25s ease;
}
.ph-visit.is-on { opacity: 1; }
.ph-visit.is-hot .ar-badge { transform: scale(var(--ar-scale, 4)); }
.ph-visit.is-hot .ar-icon { transform: translateX(var(--ar-x, 0)) rotate(var(--ar-hover, -45deg)); }
.ph-visit.is-hot .ar-label { transform: translateX(-8px); }
html.site-transit .ph-wrap, html.site-transit .ph-visit { visibility: hidden !important; }

/* ── About photos, alive (photo-motion.js) ───────────────────────────── */
.photo-rail img { will-change: transform; }
.pm-glare, .pm-glow { position: absolute; inset: 0; pointer-events: none; opacity: 0; z-index: 1; }
.pm-glare { mix-blend-mode: soft-light; }
.photo-rail .ph-tag { z-index: 2; }

/* ── the dock's tools: Tour and Sound inside the rail capsule (site-nav.js) ── */
.nav-rail .nav-sep { height: 1px; margin: 5px 6px 3px; background: rgba(255,255,255,.16); }
.nav-tool { padding: calc(4px + var(--m) * 4px) 1px; }
.nav-ringwrap { display: inline-flex; transform: scale(calc(1 + var(--m) * .32)); transform-origin: 50% 50%; }
.nav-ring {
  width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; color: rgba(255,255,255,.86);
}
.nav-ring .ob-face { padding: 0 !important; width: 100%; height: 100%; justify-content: center; align-items: center; border-radius: 50%; }
.nav-ico { width: 12px; height: 12px; display: block; }
.nav-tool:hover .nav-ring, .nav-tool:focus-visible .nav-ring { color: #7FE8D0; }
.nav-tool:focus-visible .nav-ring { box-shadow: 0 0 0 2px #7FE8D0; }
.nav-tool-tour .ico-stop { display: none; }
.nav-tool-tour.is-on .ico-play { display: none; }
.nav-tool-tour.is-on .ico-stop { display: inline; }
.nav-tool-sound .snd-on { display: none; }
.nav-tool-sound.is-on .snd-on { display: inline; }
.nav-tool-sound.is-on .snd-off { display: none; }
.nav-tool.is-on .nav-ring { color: #7FE8D0; }
/* Quality (site-nav.js, quality.js): three bars, as many lit as the tier, and a teal dot while it is on Auto */
.nav-tool-quality .q-b { fill: currentColor; opacity: .28; transition: opacity .25s ease; }
.nav-tool-quality.q-low .q-b1,
.nav-tool-quality.q-balanced .q-b1, .nav-tool-quality.q-balanced .q-b2,
.nav-tool-quality.q-high .q-b { opacity: 1; }
.nav-tool-quality .q-auto { fill: #7FE8D0; opacity: 0; transition: opacity .25s ease; }
.nav-tool-quality.is-auto .q-auto { opacity: 1; }
/* and what the dock's tools say, over the room, bottom left (Skip is bottom right) */
.q-toast {
  position: fixed; left: clamp(14px, 2.2vw, 34px); bottom: calc(clamp(16px, 3.5vh, 34px) + env(safe-area-inset-bottom, 0px));
  z-index: 32; max-width: min(340px, calc(100vw - 150px)); box-sizing: border-box;
  padding: 9px 13px 10px; border-radius: 6px; color: #fff;
  background: rgba(8, 10, 12, .74); border: 1px solid rgba(255, 255, 255, .16);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2), 0 18px 50px -18px rgba(0, 0, 0, .7);
  opacity: 0; visibility: hidden; transform: translateY(8px); pointer-events: none;
  transition: opacity .35s ease, transform .35s cubic-bezier(.22, .61, .36, 1), visibility .35s;
}
.q-toast.is-on { opacity: 1; visibility: visible; transform: none; }
.q-toast-h { margin: 0; font: 10px/1.4 ui-monospace, 'IBM Plex Mono', monospace; letter-spacing: .18em; text-transform: uppercase; color: #7FE8D0; }
.q-toast-b { margin: 4px 0 0; font: 12.5px/1.45 'Geist Sans', system-ui, sans-serif; color: rgba(255, 255, 255, .88); }
@media (prefers-reduced-motion: reduce) { .q-toast { transition: opacity .2s ease, visibility .2s; transform: none; } }
.nav-tool.is-on .ob-face { background: rgba(8,10,12,.82) !important; }
.nav-tool .nav-label { margin-top: -9px; }
@media (max-width: 720px) {
  .nav-ring { width: 22px; height: 22px; }
  .nav-tool { padding: 4px 2px; }
}

/* ── "Leave the tour?" (leave-confirm.js): asked before any link that leaves this page ────────────────────────────────
 * The glass recipe of the site's cards (a plain backdrop blur: no WebGL context for a question), the card type for the
 * heading and body, and the Arrow Reveal of Skip / Visit site for Open. "Stay here" is the filled one. */
.leave-root {
  position: fixed; inset: 0; z-index: 1000001;
  display: none; align-items: center; justify-content: center;
  padding: max(16px, env(safe-area-inset-top, 0px)) max(16px, env(safe-area-inset-right, 0px))
           max(16px, env(safe-area-inset-bottom, 0px)) max(16px, env(safe-area-inset-left, 0px));
  touch-action: none; overscroll-behavior: contain; -webkit-tap-highlight-color: transparent;
}
.leave-root.is-open { display: flex; }
.leave-scrim { position: absolute; inset: 0; background: rgba(3, 5, 8, 0.6); opacity: 0; transition: opacity .24s ease; }
.leave-card {
  position: relative; box-sizing: border-box; width: min(100%, 432px); max-height: 100%; overflow-y: auto;
  padding: 22px 22px 20px; border-radius: 6px; isolation: isolate; color: #fff;
  background: linear-gradient(180deg, rgba(8, 11, 16, 0.12) 0%, rgba(8, 11, 16, 0.34) 100%), rgba(10, 14, 20, 0.6);
  -webkit-backdrop-filter: blur(16px) saturate(1.5); backdrop-filter: blur(16px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), inset 0 -18px 36px -18px rgba(255, 255, 255, 0.12),
              0 30px 80px -20px rgba(0, 0, 0, 0.8);
  opacity: 0; transform: translateY(10px) scale(.985);
  transition: opacity .24s ease, transform .3s cubic-bezier(.22, .61, .36, 1);
}
.leave-root.is-in .leave-scrim { opacity: 1; }
.leave-root.is-in .leave-card { opacity: 1; transform: none; }
.leave-eyebrow { margin-bottom: 10px; }
.leave-heading { margin-bottom: 14px; }
.leave-dest {
  margin: 0 0 12px; overflow-wrap: anywhere;
  font-family: ui-monospace, 'IBM Plex Mono', monospace; font-size: 12px; letter-spacing: .02em; color: #fff;
}
.leave-host { border-bottom: 1px solid rgba(127, 232, 208, 0.55); padding-bottom: 2px; }
.leave-note { margin: 0 0 20px; color: rgba(255, 255, 255, 0.72); }
.leave-actions { display: flex; gap: 10px; }
.leave-stay, .leave-go {
  min-height: 44px; cursor: pointer; -webkit-tap-highlight-color: transparent;
  font-family: ui-monospace, 'IBM Plex Mono', monospace; font-size: 11px; letter-spacing: .18em; text-transform: uppercase;
}
.leave-stay {
  padding: 0 24px; color: #07100e; background: #7FE8D0; border: 1px solid #7FE8D0; border-radius: 999px;
  transition: transform .2s ease, box-shadow .25s ease;
}
.leave-stay:hover { box-shadow: 0 0 0 4px rgba(127, 232, 208, 0.22); }
.leave-stay:active { transform: scale(.97); }
.leave-go.ar {
  color: rgba(255, 255, 255, 0.92); background: rgba(8, 10, 12, 0.6); border: 1px solid rgba(255, 255, 255, 0.22);
  padding: 8px 9px 8px 22px !important;
}
.leave-go.ar:hover { border-color: #7FE8D0; }
.leave-stay:focus-visible, .leave-go:focus-visible { outline: 2px solid #7FE8D0; outline-offset: 3px; }
/* on a desktop the site's own cursor IS the pointer (the native one is hidden), so it rides above the question too */
html.leave-open .cur-root { z-index: 1000002; }
@media (max-width: 420px) {
  .leave-actions { flex-direction: column; }
  .leave-stay { width: 100%; }
  .leave-go.ar { justify-content: center; }
}
@media (max-height: 460px) {
  .leave-card { padding: 16px 20px 16px; }
  .leave-note { margin-bottom: 14px; }
}
@media (prefers-reduced-motion: reduce) { .leave-scrim, .leave-card { transition: none; } }
