/* ==========================================================================
   John Luna — Command Deck
   Brand: Deep Forest #0C211C · Ground #12302A · Forest #1F4A3B
          Linen #F6F3EC · Sage #9DB4A8 · Amber #C88A2B
   Type:  Petrona (display) · Source Sans 3 (body) · JetBrains Mono (data)

   Two rules do most of the work here.

   Amber appears once per surface. On the deck that is the three hero figures;
   on a project it is NEXT RUN. Everything else that used to be Amber — the
   italic in the display heading, the list numerals, the stat figures — is
   Linen or Sage. Amber otherwise means one thing only: focus.

   Nothing labels itself twice. There is a single status vocabulary, defined
   in app.js and rendered by .status wherever it appears.
   ========================================================================== */

:root {
  --deep:    #0C211C;
  --ground:  #12302A;
  --forest:  #1F4A3B;
  --ink:     #F6F3EC;
  --muted:   #9DB4A8;   /* Sage. The working colour for every small label. */
  --faint:   #6E8B7D;   /* Slate. Fails 4.5:1 on panels, so decoration only. */
  --amber:   #C88A2B;
  --amber-hi:#E0A651;
  --line:    rgba(246, 243, 236, 0.14);
  --line-hi: rgba(246, 243, 236, 0.30);
  --sage-fill: rgba(157, 180, 168, 0.13);

  /* Type. Nothing in the label or mono ranges is allowed below 12px, which is
     what 0.75rem is against the 16px root. */
  --fs-mono:  0.75rem;
  --fs-label: 0.75rem;

  /* Spacing scale. --s3 is the gutter, --s7 the section gap. */
  --s1: 8px;  --s2: 16px; --s3: 24px; --s4: 32px;
  --s5: 48px; --s6: 64px; --s7: 96px; --s8: 128px;

  --pad: var(--s3);
  --top: clamp(4.5rem, 9vh, 6.5rem);
  --maxw: 1200px;

  /* The content box every view is laid out in, and the space either side of it.
     The carousel canvas is positioned against these rather than against the
     viewport: on an ultrawide the two are hundreds of pixels apart, and a ring
     pinned to the screen edge floats away from the column it belongs to. */
  --content: min(100vw, calc(var(--maxw) + var(--pad) * 2));
  --gutter: max(0px, calc((100vw - var(--content)) / 2));

  /* The carousel's box. Copy belongs in the measure, because copy has a
     comfortable line length; a carousel does not. So the canvas starts where
     the text column ends and runs to the edge of the screen rather than to the
     edge of the measure. Confined to the measure it came out as a 696px square
     on a 2000px display, which is most of the screen left empty. */
  --textcol:  calc((var(--content) - var(--pad) * 2) * 0.42);
  --scene-x:  calc(var(--gutter) + var(--pad) + var(--textcol));
  --scene-w:  calc(100vw - var(--scene-x) - var(--s5));

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--deep);
  color: var(--ink);
  font-family: "Source Sans 3", ui-sans-serif, system-ui, -apple-system, sans-serif;
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

h1, h2, h3 {
  font-family: Petrona, "Iowan Old Style", Georgia, serif;
  font-weight: 400;
  margin: 0;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

a { color: inherit; text-decoration: none; }

/* Amber means focus, and only focus. */
:focus-visible {
  outline: 2px solid var(--amber-hi);
  outline-offset: 2px;
  border-radius: 2px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: fixed;
  z-index: 100;
  top: var(--s2); left: var(--s2);
  transform: translateY(-200%);
  background: var(--ground);
  border: 1px solid var(--line-hi);
  color: var(--ink);
  padding: var(--s2) var(--s3);
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.skip-link:focus { transform: none; }

.eyebrow {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}

/* ============================ STATUS ============================
   The one label vocabulary. Live, Queued, Framework, and nothing else.
   Always rendered as words: colour never carries the meaning alone. */

.status {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 400;
}
/* Live reads brighter, not greener. #6FA189 measured 3.70:1 over the brightest
   frame of the hero clip, and colour was never carrying the meaning anyway:
   the word does. Linen for running, Sage for everything else. */
.status[data-status="live"] { color: var(--ink); }
/* Cut is struck through rather than dimmed. Dimming it far enough to read as
   cut would take it under the contrast floor, and the rule says it anyway. */
.status[data-status="cut"] { text-decoration: line-through; }

/* ============================ BACKGROUND LAYERS ============================ */

#gl, .hero-video, #scrim, #grain {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* The still fallback, and what shows through when no context is created at
   all: below 768px, on a metered connection, or on a device short of memory. */
#gl {
  display: block;
  z-index: 0;
  background: linear-gradient(168deg, #0C211C 0%, #16362C 46%, #0C211C 100%);
}
body.flat-bg .hero-video { display: none; }

.hero-video {
  z-index: 0;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.4s var(--ease);
  /* The clip is the room, not the subject. */
  /* 0.55 is not a taste setting. It is the number at which Sage body copy
     still clears 4.5:1 over the single brightest pixel of the clip once the
     scrim is on top; see the contrast note in the README. */
  filter: brightness(0.55) saturate(0.85);
}
body.video-mode .hero-video.ready { opacity: 1; }
body.video-mode #gl { opacity: 0.25; transition: opacity 1.4s var(--ease); }

/* The contrast floor. Deep Forest at 50% over whichever moving layer is on,
   so body copy clears 4.5:1 and the display heading clears 7:1 against the
   brightest frame either of them can produce. Tuned with the shader ceiling
   in app.js: raise one and the other has to come with it. */
#scrim {
  z-index: 1;
  background: rgba(12, 33, 28, 0.5);
}
body.video-mode #scrim { background: rgba(12, 33, 28, 0.5); }

/* Film grain, rendered once as a repeating data texture. */
#grain {
  z-index: 2;
  opacity: 0.045;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Vignette and floor gradient, so text always has ground under it. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    radial-gradient(130% 90% at 50% 0%, transparent 30%, rgba(12, 33, 28, 0.5) 100%),
    linear-gradient(180deg, rgba(12, 33, 28, 0.7) 0%, rgba(12, 33, 28, 0.05) 32%, rgba(12, 33, 28, 0.08) 58%, rgba(12, 33, 28, 0.82) 100%);
}

/* ============================ SCROLLBARS ============================ */

* {
  scrollbar-width: thin;
  scrollbar-color: rgba(246, 243, 236, 0.18) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(246, 243, 236, 0.16);
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover { background: rgba(246, 243, 236, 0.3); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* ============================ CHROME ============================ */

/* Both views scroll internally, and the chrome is fixed over them. Without a
   ground under it, a scrolled heading runs straight through the brand lockup. */
.chrome::before {
  content: "";
  position: absolute;
  /* Taller than the header, so the fade starts below the lockup rather than
     through it. */
  inset: -1px 0 -28px;
  z-index: -1;
  background: linear-gradient(180deg, var(--deep) 0%, var(--deep) 62%, rgba(12, 33, 28, 0) 100%);
  opacity: 0;
  transition: opacity 0.25s var(--ease);
}
body.scrolled .chrome::before { opacity: 1; }

body[data-view="landing"] .chrome { display: none; }

.chrome {
  position: fixed;
  z-index: 40;
  top: 0; left: 0; right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: clamp(1rem, 2.5vh, 1.75rem) var(--pad);
  max-width: calc(var(--maxw) + var(--pad) * 2);
  margin-inline: auto;
}

.brand { display: flex; align-items: center; gap: var(--s2); }

.monogram {
  width: 42px; height: 42px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--amber);
  color: var(--amber);
  font-family: Petrona, Georgia, serif;
  font-size: 1.05rem;
  line-height: 1;
  transition: background 0.4s var(--ease), color 0.4s var(--ease);
}
.brand:hover .monogram { background: var(--amber); color: var(--deep); }

.brand-text {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1.65;
}
.brand-text b { display: block; color: var(--ink); font-weight: 500; }
.brand-text i { display: block; font-style: normal; color: var(--muted); }

@media (max-width: 768px) { .brand-text i { display: none; } }

.chrome-right { display: flex; align-items: center; gap: var(--s1); }

.ghost-btn {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  background: rgba(12, 33, 28, 0.5);
  border: 1px solid var(--line);
  padding: 0.6rem 0.9rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  backdrop-filter: blur(8px);
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
}
.ghost-btn:hover { border-color: var(--line-hi); color: var(--ink); }
.ghost-btn b { color: var(--ink); font-weight: 500; }
.ghost-btn[hidden] { display: none; }

/* ============================ VIEW SYSTEM ============================ */

#stage {
  position: relative;
  z-index: 10;
  min-height: 100%;
}

.view {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: var(--top) var(--pad) var(--s3);
  max-width: calc(var(--maxw) + var(--pad) * 2);
  margin-inline: auto;
  min-height: 100dvh;
}
.view[hidden] { display: none; }

/* ============================ ENTRY ============================

   The landing. One viewport, pinned, and five scenes that hand over to each
   other as the scroll moves through. The scene is a contour field that zooms
   and floods with the scroll; the copy sits on it in the design system's own
   dark-surface tokens, which is why this block carries its own variables
   rather than the deck's. Amber appears exactly once on the whole surface: the
   square on the one link that leaves it. */

.view-landing {
  --e-bg:    #12302A;                       /* Deep Forest, the DS dark surface */
  --e-fg:    #F6F3EC;                       /* Linen */
  --e-sage:  #C9D8CC;                       /* Sage, the DS secondary on dark */
  --e-amber: #C88A2B;
  --e-dim:   rgba(246, 243, 236, 0.8);
  --e-line:  rgba(246, 243, 236, 0.12);
  --e-ease:  cubic-bezier(0.16, 1, 0.3, 1);
  --e-scrim: 0.12;                          /* Forest over the field */

  /* The design's three responsive values. */
  --e-side:   24px;                         /* copy inset from the left edge */
  --e-edge:   24px;                         /* rail inset from the right edge */
  --e-navpad: 32px 24px 24px;

  max-width: none;
  padding: 0;
  overflow-y: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
  background: var(--e-bg);
  color: var(--e-fg);
}
.view-landing::-webkit-scrollbar { width: 0; }
.view-landing.menu-open { overflow: hidden; }

@media (min-width: 640px)  { .view-landing { --e-side: 32px;  --e-edge: 32px; --e-navpad: 48px 32px 24px; } }
@media (min-width: 768px)  { .view-landing { --e-side: 80px;  --e-edge: 48px; --e-navpad: 48px 48px 24px; } }
@media (min-width: 1024px) { .view-landing { --e-side: 128px; } }

@media (prefers-reduced-motion: reduce) {
  .view-landing { scroll-behavior: auto; }
}

.view-landing ::selection { background: var(--e-amber); color: var(--e-bg); }

/* The track is what scrolls; the frame is what you see. app.js sizes both
   from the container, so a browser toolbar coming and going never leaves the
   frame taller than the viewport it sits in. */
/* The view is a flex column, and a flex item shrinks to fit by default: without
   flex: none the track collapses to the viewport and nothing scrolls. */
.entry-track { position: relative; flex: none; background: var(--e-bg); }
.entry-frame {
  position: sticky;
  top: 0;
  width: 100%;
  overflow: hidden;
  background: var(--e-bg);
}

/* --- the field --- */

.entry-field {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, #12302A, #1F4A3B);
}
.entry-canvas {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 1.2s ease-out;
}
.entry-canvas.is-live { opacity: 1; }

.entry-scrim {
  position: absolute;
  inset: 0;
  background: rgba(31, 74, 59, var(--e-scrim));
  pointer-events: none;
}
/* The vignette's centre follows the section that is up, so the light sits
   behind the copy rather than in the middle of the screen. */
.entry-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at var(--vig-at, 35% 50%), transparent 40%, rgba(18, 48, 42, 0.55) 100%);
}

/* --- shared type --- */

.e-label {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--e-sage);
}
.e-mono {
  font-family: "JetBrains Mono", Consolas, ui-monospace, monospace;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* --- the header --- */

.entry-nav {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--e-navpad);
}

.entry-brand { display: flex; align-items: center; gap: 12px; }
.entry-mark { width: 32px; height: 32px; display: block; flex: none; }
.entry-brand-text { display: flex; flex-direction: column; line-height: 1.2; }
.entry-brand-text b {
  font-family: Petrona, Georgia, serif;
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -0.01em;
  color: var(--e-fg);
}
.entry-brand-text i {
  font-style: normal;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--e-sage);
}

.entry-links { display: none; align-items: center; gap: 40px; }
.entry-links a {
  position: relative;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--e-fg);
  transition: color 0.15s ease-out;
}
.entry-links a:hover { color: var(--e-sage); }
.entry-links a.is-here::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -12px;
  height: 2px;
  background: var(--e-fg);
}

/* The switch between the two sites, in the entry's own vocabulary. Below 640px
   it gives way to a line in the menu. */
.entry-switch {
  display: none;
  gap: 3px;
  padding: 3px;
  border: 1px solid rgba(246, 243, 236, 0.3);
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--e-fg);
}
.entry-switch a,
.entry-switch span { padding: 8px 14px; border-radius: 4px; white-space: nowrap; }
.entry-switch a { color: var(--e-fg); opacity: 0.75; transition: opacity 0.15s ease-out; }
.entry-switch a:hover { opacity: 1; color: var(--e-fg); }
.entry-switch [aria-current] { background: var(--e-fg); color: var(--e-bg); }
@media (min-width: 640px) { .entry-switch { display: flex; } }

.entry-burger {
  --d: 500ms;
  display: flex;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: 0;
  padding: 8px;
  cursor: pointer;
}
.entry-burger span { display: block; width: 24px; height: 2px; background: var(--e-fg); }
.entry-burger span:last-child { width: 16px; }

@media (min-width: 1024px) {
  .entry-links { display: flex; }
  .entry-burger { display: none; }
}

/* The header and the rail arrive a beat after the page, in order. */
.entry-brand, .entry-links a, .entry-switch, .entry-burger, .entry-rail {
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 0.6s var(--e-ease) var(--d, 100ms), transform 0.6s var(--e-ease) var(--d, 100ms);
}
.entry-rail { --d: 600ms; transform: none; }
.view-landing.nav-in :is(.entry-brand, .entry-links a, .entry-switch, .entry-burger, .entry-rail) { opacity: 1; transform: none; }

/* --- the rail --- */

.entry-rail {
  position: absolute;
  right: var(--e-edge);
  top: 50%;
  translate: 0 -50%;
  z-index: 40;
  display: none;
  flex-direction: column;
  align-items: flex-end;
  gap: 24px;
}
@media (min-width: 768px) {
  .entry-rail { display: flex; }
  /* Room on the right for the rail, so a row's cadence never runs under it. */
  .entry-s { padding-right: max(var(--e-side), calc(var(--e-edge) + 128px)); }
}

.entry-stop {
  display: flex;
  align-items: center;
  gap: 12px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: rgba(246, 243, 236, 0.4);
  transition: color 0.3s ease-out;
}
.entry-stop.is-on { color: var(--e-fg); }
.entry-stop-label {
  color: inherit;
  font-size: 11px;
  opacity: 0;
  transform: translateX(8px);
  transition: opacity 0.4s var(--e-ease), transform 0.4s var(--e-ease);
}
.entry-stop.is-on .entry-stop-label,
.entry-stop:hover .entry-stop-label,
.entry-stop:focus-visible .entry-stop-label { opacity: 1; transform: none; }
.entry-stop-bar {
  width: 12px;
  height: 1px;
  background: currentColor;
  transition: width 0.4s var(--e-ease);
}
.entry-stop.is-on .entry-stop-bar { width: 32px; }

.entry-rail-track {
  position: relative;
  width: 1px;
  height: 96px;
  background: rgba(246, 243, 236, 0.15);
}
.entry-rail-fill {
  position: absolute;
  left: 0; top: 0;
  width: 1px;
  height: 0;
  background: var(--e-fg);
}

/* --- the scenes --- */

/* Each scene is the whole frame. Its opacity is the continuous hand-over,
   set every frame from the scroll; .is-on is the threshold at which its
   contents get up, and the only time it answers the pointer. */
.entry-s {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  padding: 0 var(--e-side);
  opacity: 0;
  transition: opacity 0.1s ease-out;
  pointer-events: none;
}
.entry-s.is-on .entry-s-inner,
.entry-s.is-on .entry-cue { pointer-events: auto; }
.entry-s-inner { position: relative; }
.entry-centre { justify-content: center; }
.entry-end { justify-content: flex-end; }

.fx {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.8s var(--e-ease) var(--d, 0ms), transform 0.8s var(--e-ease) var(--d, 0ms);
}
.entry-s.is-on .fx { opacity: 1; transform: none; }

/* 01 Open */

.entry-s-open { max-width: 1000px; }
.entry-s-open .e-label { margin-bottom: 24px; }

.entry-h1 {
  margin: 0;
  font-family: Petrona, Georgia, serif;
  font-weight: 500;
  font-size: clamp(2.25rem, 5.8vw, 6rem);
  line-height: 1.06;
  letter-spacing: -0.01em;
  color: var(--e-fg);
  text-wrap: balance;
}
.entry-h1 .w {
  display: inline-block;
  margin-right: 0.24em;
  opacity: 0;
  transform: translateY(40px);
  filter: blur(10px);
  transition: opacity 0.9s var(--e-ease) var(--d), transform 0.9s var(--e-ease) var(--d), filter 0.9s var(--e-ease) var(--d);
}
.entry-s.is-on .entry-h1 .w { opacity: 1; transform: none; filter: blur(0); }

.entry-lede {
  margin: 32px 0 0;
  font-size: clamp(1rem, 1.3vw, 1.25rem);
  line-height: 1.55;
  max-width: 560px;
  color: var(--e-dim);
}

.entry-cue {
  position: absolute;
  bottom: 48px;
  left: var(--e-side);
  display: flex;
  align-items: center;
  gap: 16px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--e-sage);
}
.entry-cue-line {
  display: block;
  width: 1px;
  height: 48px;
  background: var(--e-sage);
  animation: e-cue 2.4s var(--e-ease) infinite;
}
@keyframes e-cue {
  0%   { transform: scaleY(0); transform-origin: top; }
  45%  { transform: scaleY(1); transform-origin: top; }
  55%  { transform: scaleY(1); transform-origin: bottom; }
  100% { transform: scaleY(0); transform-origin: bottom; }
}
@media (prefers-reduced-motion: reduce) {
  .entry-cue-line { animation: none; }
}

/* 02 Tools */

.entry-s-tools { width: 100%; max-width: 1100px; }
.entry-s-tools .e-label { margin-bottom: 24px; }

.entry-h2 {
  margin: 0 0 48px;
  font-family: Petrona, Georgia, serif;
  font-weight: 500;
  font-size: clamp(1.75rem, 3.6vw, 3.75rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--e-fg);
  text-wrap: balance;
}

.entry-tools {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--e-line);
}
/* Each row lights as the scroll reaches it, in order. Dim is a colour rather
   than an opacity, so the rule under it stays where it is. */
.entry-tool {
  display: grid;
  grid-template-columns: 32px 1fr auto;
  align-items: baseline;
  gap: 24px;
  padding: 18px 0;
  border-bottom: 1px solid var(--e-line);
  color: rgba(246, 243, 236, 0.38);
  transition: color 0.5s ease-out, opacity 0.8s var(--e-ease) var(--d), transform 0.8s var(--e-ease) var(--d);
}
.entry-tool.is-lit { color: var(--e-fg); }
.entry-tool-num { opacity: 0.7; }
.entry-tool-name { font-size: clamp(1.05rem, 1.6vw, 1.5rem); font-weight: 600; line-height: 1.3; }
.entry-tool-lede { display: none; font-size: clamp(0.95rem, 1.1vw, 1.05rem); line-height: 1.4; opacity: 0.8; }
.entry-tool-when { text-align: right; white-space: nowrap; opacity: 0.7; }

@media (min-width: 1024px) {
  .entry-tool { grid-template-columns: 40px 1.1fr 1.6fr auto; }
  .entry-tool-lede { display: block; }
}

/* 03 Numbers */

.entry-s-numbers { width: 100%; max-width: 1200px; text-align: center; }
.entry-s-numbers .e-label { margin-bottom: 56px; }

.entry-figures {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 32px 24px;
}
@media (min-width: 768px) { .entry-figures { grid-template-columns: repeat(4, 1fr); } }

.entry-figure { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.entry-fig-n {
  font-family: "JetBrains Mono", Consolas, ui-monospace, monospace;
  font-weight: 400;
  font-size: clamp(3rem, 7.5vw, 7.5rem);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--e-fg);
  font-variant-numeric: tabular-nums;
}
.entry-fig-l { font-size: clamp(0.95rem, 1.15vw, 1.125rem); font-weight: 600; color: var(--e-fg); }
.entry-fig-note { font-size: 14px; line-height: 1.4; color: var(--e-sage); max-width: 200px; }

/* 04 Next */

.entry-s-next { max-width: 1000px; }
.entry-s-next .e-label { margin-bottom: 24px; }
.entry-h2-next { margin-bottom: 40px; font-size: clamp(1.75rem, 4vw, 4.25rem); }

.entry-body {
  margin: 0 0 40px;
  font-size: clamp(1rem, 1.25vw, 1.2rem);
  line-height: 1.55;
  max-width: 600px;
  color: var(--e-dim);
}

.entry-chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.entry-chip {
  padding: 10px 16px;
  border-radius: 6px;
  border: 1px solid rgba(246, 243, 236, 0.25);
  font-size: 14px;
  font-weight: 600;
  color: rgba(246, 243, 236, 0.4);
  transform: translateY(16px);
  transition: color 0.5s ease-out, opacity 0.8s var(--e-ease) var(--d), transform 0.8s var(--e-ease) var(--d);
}
.entry-chip.is-lit { color: var(--e-fg); }

/* 05 Deck */

/* The count to twenty, at the size of the frame, behind the copy. */
.entry-counter {
  position: absolute;
  left: var(--e-side);
  bottom: -0.12em;
  font-family: "JetBrains Mono", Consolas, ui-monospace, monospace;
  font-weight: 400;
  font-size: clamp(10rem, 32vw, 34rem);
  line-height: 1;
  letter-spacing: -0.06em;
  color: rgba(246, 243, 236, 0.07);
  font-variant-numeric: tabular-nums;
  user-select: none;
}

.entry-s-deck { max-width: 680px; text-align: left; }
.entry-s-deck .e-label { margin-bottom: 16px; }
.entry-h2-deck { margin-bottom: 24px; font-size: clamp(2rem, 4.4vw, 4.5rem); line-height: 1.08; }
.entry-line { display: block; overflow: hidden; }
.entry-line > span {
  display: block;
  transform: translateY(110%);
  transition: transform 0.9s var(--e-ease) var(--d);
}
.entry-s.is-on .entry-line > span { transform: none; }
.entry-s-deck .entry-body { max-width: 480px; font-size: clamp(1rem, 1.2vw, 1.125rem); }

.entry-cta { display: inline-flex; align-items: center; gap: 16px; color: var(--e-fg); }
.entry-cta-label {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.entry-cta-box {
  width: 48px; height: 48px;
  border-radius: 8px;
  background: var(--e-amber);
  color: var(--e-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.3s var(--e-ease);
}
.entry-cta:hover .entry-cta-box { transform: scale(1.1); }

/* --- the menu --- */

.entry-menu {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--e-bg);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1), visibility 0.5s;
}
.entry-menu.is-open { opacity: 1; visibility: visible; }

.entry-menu-panel {
  height: 100%;
  display: flex;
  flex-direction: column;
  transform: translateY(-32px);
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.entry-menu.is-open .entry-menu-panel { transform: none; }

.entry-menu-top { display: flex; justify-content: flex-end; padding: var(--e-navpad); }
.entry-menu-close {
  width: 40px; height: 40px;
  border-radius: 8px;
  border: 1px solid rgba(246, 243, 236, 0.3);
  background: none;
  color: var(--e-fg);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color 0.15s ease-out;
}
.entry-menu-close:hover { border-color: var(--e-fg); }

.entry-menu-links {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 32px;
}
.entry-menu-links a {
  display: block;
  padding: 12px 0;
  font-family: Petrona, Georgia, serif;
  font-weight: 500;
  font-size: clamp(1.75rem, 6vw, 2.5rem);
  letter-spacing: -0.01em;
  color: rgba(246, 243, 236, 0.6);
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s var(--e-ease) var(--d), transform 0.5s var(--e-ease) var(--d), color 0.15s ease-out;
}
.entry-menu-links a.is-here,
.entry-menu-links a:hover { color: var(--e-fg); }
.entry-menu.is-open .entry-menu-links a { opacity: 1; transform: none; }
.entry-menu-switch { margin-top: 24px; font-size: clamp(1.25rem, 4.5vw, 1.5rem) !important; }
@media (min-width: 640px) { .entry-menu-switch { display: none !important; } }

.entry-menu-foot { display: flex; gap: 32px; padding: 0 32px 40px; }
.entry-menu-foot a { color: var(--e-sage); }

/* ============================ DECK ============================

   With activity data the middle band carries the page. Without it the hero
   centres in the space it has and the index strip docks to the bottom edge a
   section gap away, so the quiet reads as composition rather than as a hole.
   Spacing is never stretched to fill. */

/* The hero, the activity band and the index strip are one group with a 96px
   section gap between them, centred in the page. Docking the strip to the very
   bottom instead would put the whole surplus into a single 217px hole between
   the hero and the strip, which is the gap the brief is about. Centred, no gap
   between two elements exceeds 128px at any height the deck is used at. */
.view-deck {
  overflow-y: auto;
  justify-content: center;
  justify-content: safe center;
  gap: var(--s7);
}

.home-top {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.home-title {
  /* One Petrona treatment. One weight, one size, one colour. */
  font-size: clamp(3rem, 10vw, 7.5rem);
  line-height: 0.9;
  font-weight: 300;
  letter-spacing: -0.035em;
  color: var(--ink);
}

.home-lede {
  font-size: clamp(1.05rem, 1.8vw, 1.35rem);
  color: var(--muted);
  max-width: 34ch;
  margin: 0;
  line-height: 1.45;
}

/* The one Amber group on this surface. */
.home-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s4);
  margin: var(--s1) 0 0;
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
/* The one Amber instance on this surface, in the lighter tone: #C88A2B is
   3.70:1 over the brightest frame of the clip, #E0A651 is 5.06:1. */
.home-stats b { color: var(--amber-hi); font-weight: 500; }

/* --- The middle band --- */

.activity[hidden] { display: none; }

.activity-heading {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--s2);
}

.activity-list { list-style: none; margin: 0; padding: 0; }

.run {
  display: grid;
  grid-template-columns: minmax(0, 13rem) 11rem minmax(0, 1fr);
  gap: var(--s3);
  align-items: baseline;
  border-top: 1px solid var(--line);
  padding: 0.6rem 0;
}
.run-tool, .run-at {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-mono);
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
}
.run-tool { color: var(--ink); }
.run-at { color: var(--muted); }
.run-out { color: var(--muted); min-width: 0; }

@media (max-width: 768px) {
  .run { grid-template-columns: 1fr; gap: var(--s1); }
}

/* --- The index strip --- */

.strip-caption {
  margin: 0 0 var(--s1);
  font-family: Petrona, Georgia, serif;
  font-size: 1.05rem;
  line-height: 1.3;
  color: var(--ink);
  min-height: 1.3em;
}
.strip-caption .sep { color: var(--muted); }
body:not(.fan) .strip-caption { display: none; }

.launcher {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  list-style: none;
  margin: 0;
  padding: 0;
  /* Closed on all four sides. The first cell used to be the only one without
     a rule beside it, which read as a mistake rather than as a grid. */
  border-top: 1px solid var(--line);
  border-right: 1px solid var(--line);
}

.door {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  align-items: flex-start;
  height: 100%;
  text-align: left;
  padding: 0.85rem 0.75rem 0.9rem;
  border-left: 1px solid var(--line);
  color: inherit;
  cursor: pointer;
  transition: background 0.35s var(--ease);
}
.door:hover { background: var(--sage-fill); }
.door:focus-visible { outline-offset: -2px; }

.door-num {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.door-name {
  font-family: Petrona, Georgia, serif;
  font-size: clamp(0.98rem, 1.15vw, 1.16rem);
  line-height: 1.2;
  color: var(--ink);
  /* Two lines are reserved in every cell whether the title needs them or not,
     which is what puts the status labels on one baseline across the strip. */
  min-height: calc(2 * 1.2em);
}

.door-status { margin-top: auto; }

/* An unbuilt slot. In the list, in the row, and obviously not a thing you can
   open: no pointer, no focus, no hover, and a number rather than a name. */
.door.is-inert {
  cursor: default;
  color: var(--muted);
}
.door.is-inert:hover { background: none; }
.door.is-inert .door-num { color: var(--muted); }
.door.is-inert .door-name { min-height: 0; }

/* Current item, for when the strip is on screen beside a project. */
.door.is-current { background: var(--sage-fill); }
.door.is-current::after {
  content: "";
  position: absolute;
  left: -1px; right: 0; bottom: 0;
  height: 2px;
  background: var(--muted);
}

/* ============================ PROJECT VIEW ============================ */

.view-project { overflow-y: auto; }

/* Deep Forest under the reading column specifically. The global scrim sets the
   floor; this is what keeps a 62ch measure comfortable rather than merely
   legible. */
.view-project::before {
  content: "";
  position: fixed;
  inset: 0 40% 0 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(12, 33, 28, 0.55) 0%, rgba(12, 33, 28, 0.55) 70%, rgba(12, 33, 28, 0) 100%);
}

.project-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: var(--s5);
  align-items: start;
  margin-top: var(--s4);
  flex: 1;
}

.project-main { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }

.project-index {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
  display: flex;
  gap: var(--s2);
  align-items: baseline;
}

.project-title {
  font-size: clamp(2.4rem, 5.5vw, 4.2rem);
  line-height: 0.98;
  font-weight: 300;
  color: var(--ink);
}

.project-lede {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  color: var(--muted);
  max-width: 44ch;
  margin: 0;
  line-height: 1.5;
}

.project-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  margin: var(--s1) 0 0;
}
.project-meta > div {
  background: rgba(12, 33, 28, 0.62);
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.project-meta dt {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.project-meta dd {
  margin: 0;
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: 0.86rem;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* The one Amber instance on this surface. */
.project-meta dd.hot { color: var(--amber); }

.project-body { display: flex; flex-direction: column; gap: var(--s2); }
.project-body h3 {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 400;
}
.project-body p { margin: 0; color: var(--muted); max-width: 62ch; }
.project-body ol {
  margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 0.55rem;
  counter-reset: step;
}
.project-body ol li {
  display: flex;
  gap: var(--s2);
  color: var(--muted);
  border-top: 1px solid var(--line);
  padding-top: 0.55rem;
  max-width: 62ch;
}
.project-body ol li::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  color: var(--muted);
  flex: none;
  padding-top: 0.2rem;
}
.project-body b { color: var(--ink); font-weight: 600; }

/* Category, demoted out of the label system and into a sentence. */
.project-cat {
  margin: var(--s1) 0 0;
  color: var(--muted);
  font-size: 0.95rem;
}
.project-cat b { color: var(--ink); font-weight: 400; }

.project-links {
  display: flex; flex-wrap: wrap; gap: var(--s1);
  margin-top: auto;
  padding-top: var(--s2);
}

.link-btn {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  border: 1px solid var(--line-hi);
  padding: 0.65rem 1rem;
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease), color 0.3s var(--ease);
}
.link-btn:hover { background: var(--sage-fill); border-color: var(--muted); }
.link-btn::after { content: "\2197"; }
.link-btn.dead {
  color: var(--muted);
  border-style: dashed;
  border-color: var(--line);
  cursor: default;
}
.link-btn.dead::after { content: ""; }
.link-btn.dead:hover { background: none; border-color: var(--line); }

/* --- Side rail --- */

.project-side {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  min-width: 0;
  /* The rail is short and the article is long. Sticky, so the numbers stay
     beside the prose instead of leaving a rail of empty page. */
  position: sticky;
  top: var(--top);
}

.media {
  position: relative;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--line);
  background: rgba(12, 33, 28, 0.5);
  overflow: hidden;
  /* Starts collapsed and only opens once a clip has actually decoded, so a
     project without one shows no empty box and no instruction to fill it. */
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}
.media[hidden] { display: none; }
.media.is-ready { opacity: 1; transform: none; }

.media-video {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1s var(--ease);
}
.media-video.ready { opacity: 1; }

/* Development only. app.js never builds this outside ?dev=1. */
.media-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s1);
  text-align: center;
  padding: var(--s3);
}
.media-code {
  font-family: Petrona, Georgia, serif;
  font-size: clamp(3rem, 7vw, 5.5rem);
  line-height: 1;
  color: var(--forest);
}
.media-note {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  max-width: 26ch;
  line-height: 1.7;
}

.project-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.project-stats > div {
  background: rgba(12, 33, 28, 0.62);
  padding: 0.8rem 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.project-stats .n {
  font-family: Petrona, Georgia, serif;
  font-size: 1.7rem;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.project-stats .l {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  line-height: 1.45;
}

.stats-note {
  margin: 0;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.5;
}
.stats-note:empty { display: none; }

/* --- Pager --- */

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  border-top: 1px solid var(--line);
  padding-top: var(--s2);
  margin-top: var(--s5);
}
.pager-btn {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 0;
  padding: 0.4rem 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  transition: color 0.3s var(--ease);
  text-align: left;
}
.pager-btn:hover { color: var(--ink); }
.pager-btn.next { text-align: right; }

.pager-keys {
  margin: 0;
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: var(--fs-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
}

/* ============================ MORPH LAYER ============================ */

#morph {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
}
#morph .flyer {
  position: fixed;
  margin: 0;
  font-family: Petrona, Georgia, serif;
  font-weight: 300;
  line-height: 0.98;
  color: var(--ink);
  white-space: nowrap;
  transform-origin: left top;
  will-change: transform, opacity;
}

/* ============================ MISC ============================ */

.noscript {
  position: relative;
  z-index: 50;
  max-width: 40ch;
  margin: 30vh auto;
  padding: var(--s3);
  border: 1px solid var(--line);
  color: var(--muted);
  text-align: center;
}

/* ============================ FAN TREATMENT ============================

   The card fan is the picker on a wide screen. The strip stops repeating the
   same seven titles beside it and becomes a position indicator: numbers, a
   rule, and the title revealed for whichever card is current. */

#scene {
  position: fixed;
  top: 0;
  /* Starts where the text column ends, finishes near the edge of the screen. */
  left: var(--scene-x);
  right: auto;
  width: var(--scene-w);
  /* A canvas is a replaced element: top and bottom will not stretch it, so the
     height has to be stated or it keeps its 2:1 intrinsic ratio.

     Capped square. A ring has to fit across the frame, and the horizontal field
     of view is the vertical one times the aspect: let the canvas get much
     taller than it is wide and the camera has to retreat a long way to fit the
     ring in, leaving the cards tiny. On a tall window the ring takes a square
     and the rest is air. */
  /* Capped square. A ring has to fit across the frame, and the horizontal field
     of view is the vertical one times the aspect: let the canvas get much
     taller than it is wide and the camera has to retreat a long way to fit the
     ring in, leaving the cards tiny. */
  height: min(calc(100dvh - 150px), var(--scene-w));
  z-index: 5;
  display: none;
  outline: none;
}
body.monoliths #scene { display: block; }

/* The carousel belongs to the deck. The landing is opaque and sits above it,
   so the scene is simply switched off there rather than dimmed. */
body[data-view="landing"] #scene { display: none; }

/* With the carousel running out to the edge of the screen, the deck view goes
   with it: the copy column stays at the measure's left edge and keeps its own
   width, and the index strip below stretches to meet the canvas, so the two
   share an edge instead of ending in different places. */
body.fan .view-deck {
  max-width: none;
  margin-inline: 0;
  padding-left: calc(var(--gutter) + var(--pad));
  padding-right: var(--s5);
}
body.fan .home-top { max-width: var(--textcol); }
body.fan .home-lede { max-width: 24ch; }
body.fan .home-title { font-size: clamp(3rem, 7vw, 6rem); }

/* In the fan treatment the cards carry the seven titles, so the strip stops
   repeating them and becomes the position readout for the fan: a track of
   seven segments, and one shared caption naming whichever card is current.
   One caption rather than seven means no reserved space and no layout shift. */
/* One row, whatever the count. Twenty segments across the measure is 60px
   each, which a two digit number sits in comfortably. */
body.fan .launcher {
  display: flex;
  grid-template-columns: none;
}
body.fan .launcher li { flex: 1 1 0; min-width: 0; }

body.fan .door {
  display: block;
  padding: 0.5rem 0.4rem 0.55rem;
}
body.fan .door-name,
body.fan .door-status { display: none; }
/* Built segments are Linen, unbuilt ones Sage. Both clear the contrast floor;
   dimming far enough to read as "not yet" would not. */
body.fan .door-num { color: var(--ink); }
body.fan .door.is-inert .door-num { color: var(--muted); }
body.fan .door.is-inert::after { background: rgba(246, 243, 236, 0.07); }
body.fan .door:hover .door-num,
body.fan .door:focus-visible .door-num { color: var(--ink); }
body.fan .door.is-current .door-num,
body.fan .door.is-active .door-num { color: var(--ink); }

body.fan .door::after {
  content: "";
  position: absolute;
  left: -1px; right: 0; bottom: 0;
  height: 2px;
  background: var(--line);
  transition: background 0.3s var(--ease);
}
body.fan .door:hover::after,
body.fan .door:focus-visible::after { background: var(--muted); }
body.fan .door.is-current::after,
body.fan .door.is-active::after { background: var(--ink); }

body.fan .view-project::before { inset: 0 45% 0 0; }

@media (max-width: 1199.98px) { #scene { display: none !important; } }

/* The stage stacks above the scene canvas, so with the fan active it has to
   let pointer events through everywhere except on its own furniture.
   Without this, #stage silently swallows every hover and click meant for a card. */
body.monoliths #stage { pointer-events: none; }
body.monoliths .home-top,
body.monoliths .launcher-wrap,
body.monoliths .activity,
body.monoliths .view-project,
/* The landing is a scroll container. Left in the none, every wheel event fell
   through it to the canvas and the page would not scroll at all. */
body.monoliths .view-landing { pointer-events: auto; }

/* ============================ BREAKPOINTS ============================ */

@media (max-width: 1200px) {
  .project-grid { gap: var(--s4); }
  /* Seven columns need about 150px each to hold a wrapped title. Below the
     1200 breakpoint they get four and wrap to a second row. */
  .launcher { grid-template-columns: repeat(4, 1fr); }
  .door { border-bottom: 1px solid var(--line); }
}

@media (max-width: 768px) {
  .launcher { grid-template-columns: repeat(2, 1fr); }
  /* Seven into two leaves the last one alone on its row. Let it span, rather
     than trail a half-drawn cell beside it. */
  .launcher li:last-child:nth-child(odd) { grid-column: 1 / -1; }

  .project-grid { grid-template-columns: 1fr; }
  .project-side { position: static; order: -1; }
  .media { aspect-ratio: 16 / 9; }
  .project-links { margin-top: var(--s3); }

  .view-project::before { inset: 0; background: rgba(12, 33, 28, 0.45); }

  .pager { flex-wrap: wrap; }
  .pager-keys { order: 1; width: 100%; text-align: left; }
}

/* Short viewports. A 1366x768 laptop leaves roughly 600px of page once the
   browser has taken its share. Tighten the rhythm rather than clip it. */
@media (min-width: 769px) and (max-height: 720px) {
  .home-title { font-size: clamp(2.6rem, 6vw, 4.6rem); }
  .home-top { gap: var(--s1); }
  .view-deck { gap: var(--s5); }
  .door { padding-top: 0.6rem; padding-bottom: 0.65rem; }
  .door-name { min-height: 0; }
}

/* ============================ REDUCED MOTION ============================ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  .hero-video, .media { display: none; }
}
