html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* Adam Laloge — portfolio
   Theme: Mission Control in a Nebula (as rendered in Adam's Canva draft)
   Base: near-black navy, never pure black. Nebula glow frames content, never sits behind body text.
   Accents: teal = primary (labels, links). Gold = status and proof markers only. */

:root {
  --base: #070b16;
  --panel: rgba(255, 255, 255, 0.03);
  --panel-border: rgba(255, 255, 255, 0.09);
  --heading: #f4f7fb;
  --body: #a7b3c7;
  --dim: #77839a;
  --teal: #5eead4;
  --gold: #d9b36a;
  --hairline: rgba(255, 255, 255, 0.08);
  --sans: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, "Liberation Mono", monospace;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--base);
  color: var(--body);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.65;
  position: relative;
}

/* Nebula glow layer */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(640px 420px at 12% -4%, rgba(45, 212, 191, 0.13), transparent 62%),
    radial-gradient(760px 520px at 92% 2%, rgba(124, 93, 250, 0.12), transparent 62%),
    radial-gradient(820px 620px at 88% 86%, rgba(190, 80, 140, 0.06), transparent 62%),
    radial-gradient(600px 500px at 4% 70%, rgba(45, 212, 191, 0.05), transparent 60%);
  pointer-events: none;
}

/* Animated sky canvas (JS): a backdrop for the hero only, not the whole page. */
#sky {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  /* The sky melts into the page at both ends instead of slicing at the hero box */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 30px, #000 calc(100% - 110px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 30px, #000 calc(100% - 110px), transparent 100%);
}
/* When the canvas is live, retire the hero's static dot layer so stars don't double up */
html.sky-on .hero::before { display: none; }

.wrap {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 28px;
}

/* Header */
.site-header {
  background: rgba(255, 255, 255, 0.025);
  border-bottom: 1px solid var(--hairline);
}
.header-inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 16px;
  padding-bottom: 16px;
}
.brand {
  color: var(--heading);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.01em;
  text-decoration: none;
}
.site-header nav a {
  color: var(--body);
  text-decoration: none;
  margin-left: 26px;
  font-size: 15px;
}
.site-header nav a:hover { color: var(--teal); }
.site-header nav a.active { color: var(--teal); }
.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  color: var(--heading);
  font-size: 19px;
  line-height: 1;
  width: 44px;
  height: 44px;
  cursor: pointer;
}
img { max-width: 100%; }
.card-link { font-size: 15px; }
.teaser-img {
  width: 100%;
  display: block;
  border-radius: 14px;
  border: 1px solid var(--panel-border);
}

/* Type */
h1, h2, h3 { color: var(--heading); font-weight: 600; line-height: 1.25; }
h1 { font-size: 40px; margin: 10px 0 18px; }
h2 { font-size: 28px; margin: 6px 0 12px; }
h3 { font-size: 20px; margin: 30px 0 8px; }

.label {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--teal);
  margin: 0;
}

a { color: var(--teal); }
a:hover { text-decoration: underline; }

section {
  padding: 56px 0 8px;
  border-top: 1px solid var(--hairline);
  margin-top: 56px;
}
section:first-of-type { border-top: none; margin-top: 0; }

/* Hero: the starfield lives here, as a backdrop. The rest of the page is plain dark. */
html, body { overflow-x: clip; }
.hero {
  padding-top: 72px;
  padding-bottom: 88px;
  position: relative;
  overflow: hidden;
  /* Full-bleed: the sky runs edge to edge, breaking out of the content column */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  padding-left: 28px;
  padding-right: 28px;
  box-sizing: border-box;
}
@media (min-width: 1101px) {
  .hero { padding-left: calc(50vw - 522px); padding-right: calc(50vw - 522px); }
}
.hero > *:not(#sky) { position: relative; z-index: 1; }
/* Static fallback sky (no-JS), scoped to the hero like the canvas */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image:
    radial-gradient(1px 1px at 24px 36px, rgba(255, 255, 255, 0.55), transparent 100%),
    radial-gradient(1px 1px at 120px 140px, rgba(255, 255, 255, 0.35), transparent 100%),
    radial-gradient(1.5px 1.5px at 210px 70px, rgba(255, 255, 255, 0.4), transparent 100%),
    radial-gradient(1px 1px at 60px 200px, rgba(255, 255, 255, 0.3), transparent 100%),
    radial-gradient(1px 1px at 300px 240px, rgba(255, 255, 255, 0.45), transparent 100%);
  background-size: 340px 280px;
  opacity: 0.5;
  pointer-events: none;
}
.hero .intro { max-width: 720px; font-size: 18px; }
.hero-links { margin-top: 26px; display: flex; gap: 14px; flex-wrap: wrap; }

.pill {
  display: inline-block;
  padding: 9px 20px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: var(--heading);
  text-decoration: none;
  font-size: 15px;
  background: rgba(255, 255, 255, 0.04);
}
.pill:hover { text-decoration: none; border-color: var(--teal); }
.pill-teal { background: rgba(45, 212, 191, 0.10); }
.pill-violet { background: rgba(124, 93, 250, 0.12); }

/* Work cards */
.section-note { color: var(--dim); max-width: 640px; }
.cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-top: 24px;
}
.card {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  padding: 24px 26px;
}
.card:hover { border-color: rgba(94, 234, 212, 0.45); }
.card p { margin: 8px 0; }
.card-label { font-size: 13px; }
.status {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
}

/* Photography gallery */
.gal-chips { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0 16px; }
.chip {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--body);
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 999px;
  padding: 10px 16px;
  cursor: pointer;
}
.chip:hover { color: var(--heading); border-color: var(--teal); }
.chip.active { color: var(--base); background: var(--teal); border-color: var(--teal); font-weight: 700; }
.pflow-scene {
  background: radial-gradient(120% 90% at 50% 0%, rgba(94, 234, 212, 0.05), transparent 60%), var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  padding: 10px 0 16px;
  margin: 6px 0 18px;
}
.pflow-track {
  display: flex;
  gap: 26px;
  overflow-x: auto;
  padding: 26px calc(50% - 240px) 22px;
  perspective: 1300px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.pflow-track::-webkit-scrollbar { display: none; }
.pflow-track:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; border-radius: 10px; }
.pcov {
  flex: 0 0 auto;
  height: 440px;
  aspect-ratio: var(--ar);
  padding: 0;
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  overflow: hidden;
  background: #0a0f1e;
  cursor: pointer;
  transform-style: preserve-3d;
  box-shadow: 0 22px 30px rgba(0, 0, 0, 0.5);
  -webkit-tap-highlight-color: transparent;
}
.pcov img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.pcov.selected { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold), 0 22px 30px rgba(0, 0, 0, 0.5); }
.pcov:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; }
.photo-detail {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  padding: 18px 20px;
}
.pd-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; }
.photo-detail h3 { margin: 6px 0 0; font-size: 21px; }
.pd-side { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; flex: 0 0 auto; }
#pd-count { font-family: var(--mono); font-size: 12px; letter-spacing: 0.14em; color: var(--gold); }
.pd-nav { display: flex; gap: 8px; }
.pd-arrow {
  -webkit-appearance: none;
  appearance: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--panel-border);
  background: rgba(255, 255, 255, 0.03);
  color: var(--heading);
  font-size: 20px;
  line-height: 1;
  padding: 0;
  cursor: pointer;
}
.pd-arrow:hover { border-color: var(--teal); color: var(--teal); }
.pd-desc { color: var(--dim); margin: 10px 0 0; }
@media (max-width: 760px) {
  .pcov { height: 232px; }
  .pflow-track { gap: 18px; padding: 20px calc(50% - 150px) 16px; }
}

/* Full viewing pane: the tapped photo flies out of the flow, tap again to return */
.pane {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  background: rgba(4, 6, 12, 0.84);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.3s ease;
}
.pane.open { opacity: 1; }
.pane-img {
  position: fixed;
  border-radius: 10px;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.6);
  object-fit: cover;
  transition: left 0.34s cubic-bezier(0.22, 0.8, 0.3, 1), top 0.34s cubic-bezier(0.22, 0.8, 0.3, 1), width 0.34s cubic-bezier(0.22, 0.8, 0.3, 1), height 0.34s cubic-bezier(0.22, 0.8, 0.3, 1);
}
.pane-cap {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 26px;
  text-align: center;
  pointer-events: none;
}
.pane-title { color: var(--heading); font-weight: 600; font-size: 17px; }
.pane-count { display: block; margin-top: 4px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; color: var(--gold); }
.pane-close {
  -webkit-appearance: none;
  appearance: none;
  position: fixed;
  top: 16px;
  right: 16px;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 1px solid var(--panel-border);
  background: rgba(10, 15, 30, 0.8);
  color: var(--heading);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}
.pane-close:hover { border-color: var(--teal); color: var(--teal); }

/* Frosted glass is the button standard (Adam, 2026-10-26): a translucent
   tint in the button's own color, a backdrop blur, and a lit top edge.
   Applies to UI chrome only — book covers, photos, and artwork slices
   are content, not buttons, and stay unglassed. */
.pill, .chip, .nav-toggle, .pd-arrow, .pane-close {
  backdrop-filter: blur(10px) saturate(1.4);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 0 0 1px rgba(255, 255, 255, 0.05),
    0 6px 18px rgba(0, 0, 0, 0.25);
}
.pill-teal { background: rgba(45, 212, 191, 0.13); border-color: rgba(45, 212, 191, 0.45); }
.pill-violet { background: rgba(124, 93, 250, 0.15); border-color: rgba(124, 93, 250, 0.5); }
.chip.active { background: rgba(45, 212, 191, 0.85); }
.nav-toggle { background: rgba(255, 255, 255, 0.05); }
.pd-arrow { background: rgba(255, 255, 255, 0.05); }
.pane-close { background: rgba(10, 15, 30, 0.45); }

/* Bookshelf — Cover Flow */
.flow-scene {
  background: radial-gradient(120% 90% at 50% 0%, rgba(94, 234, 212, 0.05), transparent 60%), var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  padding: 10px 0 18px;
  margin: 18px 0 22px;
}
.flow-block { margin-top: 24px; }
.flow-block .flow-scene { margin: 8px 0 14px; }
.flow-label { margin: 0 0 2px; }
.flow-track {
  display: flex;
  gap: 30px;
  overflow-x: auto;
  padding: 34px calc(50% - 84px) 26px;
  perspective: 1200px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.flow-track::-webkit-scrollbar { display: none; }
.cov {
  position: relative;
  flex: 0 0 auto;
  width: 168px;
  height: 252px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  transform-style: preserve-3d;
  -webkit-tap-highlight-color: transparent;
}
.cov:focus-visible { outline: 2px solid var(--teal); outline-offset: 6px; }
.cov-front {
  position: absolute;
  inset: 0;
  transform: translateZ(calc(var(--bd) / 2));
  backface-visibility: hidden;
  border-radius: 3px 8px 8px 3px;
  background: linear-gradient(105deg, rgba(0,0,0,0.5), rgba(255,255,255,0.06) 22%, rgba(0,0,0,0.15) 60%, rgba(0,0,0,0.45)), var(--cov);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.10), inset 7px 0 12px rgba(0, 0, 0, 0.35), 0 18px 22px rgba(0, 0, 0, 0.45);
  display: flex;
  flex-direction: column;
  padding: 18px 15px 18px 31px;
  text-align: left;
}
.cov-front::before {
  /* Bound spine band down the left of the cover */
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 17px;
  background: linear-gradient(90deg, rgba(0,0,0,0.6), rgba(0,0,0,0.28) 55%, rgba(255,255,255,0.10) 85%, rgba(0,0,0,0.35));
  border-right: 1px solid rgba(0, 0, 0, 0.45);
  border-radius: 3px 0 0 3px;
}
.cov-front::after {
  content: "";
  position: absolute;
  inset: 9px;
  border: 1px solid rgba(217, 179, 106, 0.35);
  border-radius: 4px;
  pointer-events: none;
}
.cov.selected .cov-front { box-shadow: inset 0 0 0 2px var(--gold), inset 7px 0 12px rgba(0, 0, 0, 0.35), 0 18px 22px rgba(0, 0, 0, 0.45); }
.cov-status {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
}
.cov-title {
  margin-top: 14px;
  font-size: 19px;
  font-weight: 700;
  line-height: 1.28;
  color: #f0ead8;
}
.cov-author {
  margin-top: auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
}
.cov-edge {
  position: absolute;
  top: 2px;
  height: calc(100% - 4px);
  width: var(--bd);
  backface-visibility: hidden;
}
.cov-edge-right {
  right: 0;
  transform: rotateY(90deg) translateZ(calc(var(--bd) / 2));
  background: repeating-linear-gradient(90deg, #ddd3ba 0 2px, #c9bd9f 2px 4px);
  border-radius: 1px;
}
.cov-edge-left {
  left: 0;
  transform: rotateY(-90deg) translateZ(calc(var(--bd) / 2));
  background: linear-gradient(180deg, rgba(0,0,0,0.4), rgba(0,0,0,0.1)), var(--cov);
}
.flow-hint {
  text-align: center;
  color: var(--dim);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 2px 0 4px;
}
.book-detail { margin-top: 4px; }
.book-byline { color: var(--dim); }
@media (max-width: 760px) {
  .cov { width: 132px; height: 198px; }
  .cov-title { font-size: 16px; }
  .flow-track { padding: 26px calc(50% - 66px) 20px; gap: 22px; }
}

/* Resume */
.resume-meta { color: var(--body); }

/* ---------- Resume ---------- */
#resume { max-width: 840px; }
#resume h3 {
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 8px;
  margin: 36px 0 14px;
}
#resume .skills-grid { gap: 18px; }
.job-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 18px;
  margin: 20px 0 6px;
}
.job-company { margin: 0; }
.job-loc { color: var(--dim); }
.job-role { margin: 1px 0 0; font-style: italic; color: var(--body); }
.job-dates {
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: 0.08em;
  color: var(--dim);
  white-space: nowrap;
  margin: 0;
}
.gold-note { color: var(--gold); }
.skill-panel {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  padding: 18px 20px;
}
.skill-panel li { margin: 5px 0; }
@media (max-width: 560px) {
  .job-head { flex-direction: column; gap: 2px; }
}

/* Print: the web resume should survive a recruiter's printer —
   white page, dark text, site chrome and the download button hidden. */
@media print {
  body { background: #fff !important; }
  body::before { display: none !important; }
  .site-header, .site-footer { display: none !important; }
  #resume { max-width: none; }
  #resume .pill { display: none !important; }
  body, p, li, .job-role, .resume-meta { color: #1c1c1c !important; }
  h1, h2, h3, strong, .job-company strong { color: #000 !important; }
  .label { color: #555 !important; }
  .job-loc, .job-dates { color: #444 !important; }
  #resume h3 { border-bottom-color: #bbb; }
  .skill-panel { background: #fff !important; border-color: #bbb; }
  .gold-note { color: #7a5c14 !important; }
  a { color: #000 !important; text-decoration: none !important; }
}
.job-head strong { color: var(--heading); }
ul { margin: 6px 0 10px; padding-left: 22px; }
li { margin: 4px 0; }
ul.plain { list-style: none; padding-left: 0; }
.skills-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 36px;
}

/* Footer */
.site-footer {
  border-top: 1px solid var(--hairline);
  margin-top: 72px;
  padding: 26px 0 34px;
}
.footer-inner {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  color: var(--dim);
  font-size: 15px;
}
.footer-inner strong { color: var(--heading); }
.footer-links a { color: var(--body); text-decoration: none; }
.footer-links a:hover { color: var(--teal); }

@media (max-width: 760px) {
  h1 { font-size: 30px; }
  h2 { font-size: 24px; }
  .wrap { padding: 0 20px; }
  .cards, .skills-grid { grid-template-columns: 1fr; }
  section { padding-top: 40px; margin-top: 40px; }
  .hero { padding-top: 48px; }
  .hero .intro { font-size: 17px; }
  /* Hamburger nav, on one 8px rhythm (researched 2026-10-26 against
     Material 3 app-bar/list specs, Apple HIG and WCAG target sizes):
     every gap has exactly one owner. Bar body is 64px = 8 + the 48px
     toggle + 8. Menu rows are exactly 48px (12 + 24 line + 12);
     dividers are inset shadows BETWEEN rows only, so rows stay 48 --
     a border would silently make them 49. One separator line above
     the panel (the nav's own border) and the header's bottom border
     below it; text centres sit 32px from each separator. */
  .header-inner { flex-wrap: wrap; padding-top: 8px; padding-bottom: 8px; }
  .header-inner:has(nav.open) { padding-bottom: 0; }
  .brand { line-height: 24px; }
  .nav-toggle { display: block; width: 48px; height: 48px; font-size: 21px; }
  .site-header nav { display: none; width: 100%; flex-direction: column; padding: 8px 0; }
  .site-header nav.open { display: flex; border-top: 1px solid var(--hairline); }
  .site-header nav a {
    margin-left: 0;
    padding: 12px 0;
    font-size: 16px;
    line-height: 24px;
    border-top: none;
  }
  .site-header nav a + a { box-shadow: inset 0 1px 0 var(--hairline); }
  .footer-inner { flex-direction: column; gap: 6px; }
}

/* Space Dreams — the page sliced into worlds */
.latent-band {
  display: flex;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  height: 62vh;
  min-height: 380px;
  overflow: hidden;
  background: #000;
  transition: height 0.8s cubic-bezier(0.22, 0.8, 0.3, 1);
}
.latent-band.open {
  /* Starts below the top bar (its height arrives as --bar-h, measured by
     the page script) so the opened image is completely in view and never
     slides under the bar's glass. */
  position: fixed;
  inset: var(--bar-h, 57px) 0 0 0;
  height: auto;
  min-height: 0;
  z-index: 40;
}
.slice {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  padding: 0;
  border: none;
  background: #000;
  cursor: pointer;
  overflow: hidden;
  transition: flex-grow 0.8s cubic-bezier(0.22, 0.8, 0.3, 1), opacity 0.6s ease;
  -webkit-tap-highlight-color: transparent;
}
.slice + .slice { margin-left: -16px; }
.slice img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.15);
  animation: sliceDrift 16s ease-in-out infinite alternate;
  pointer-events: none;
}
.slice:nth-child(2) img { animation-duration: 21s; animation-delay: -6s; }
.slice:nth-child(3) img { animation-duration: 18s; animation-delay: -3s; }
@keyframes sliceDrift {
  from { transform: scale(1.15) translateX(-2.4%); }
  to { transform: scale(1.15) translateX(2.4%); }
}
/* Dreamy wobbly edges between worlds: each slice's leading edge is the
   wave, so the world beneath shows through the troughs. Alternating phase. */
.slice:not(:first-child) {
  clip-path: polygon(7px 0, 100% 0, 100% 100%, 13px 100%, 4px 87%, 15px 73%, 5px 59%, 14px 45%, 4px 31%, 15px 17%, 6px 5%);
}
.slice:nth-child(even):not(:first-child) {
  clip-path: polygon(13px 0, 100% 0, 100% 100%, 6px 100%, 15px 85%, 4px 71%, 14px 56%, 5px 42%, 15px 27%, 4px 13%, 10px 3%);
}
/* The seam membrane: a narrow blurred strip riding the boundary, swaying
   slowly (transform only) so the melt between worlds stays alive. */
.seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -19px;
  width: 38px;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent);
  backdrop-filter: blur(7px) saturate(1.25);
  -webkit-backdrop-filter: blur(7px) saturate(1.25);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
  animation: seamSway 9s ease-in-out infinite alternate;
}
@keyframes seamSway {
  from { transform: translateX(-5px); }
  to { transform: translateX(5px); }
}
.latent-band.open .seam { opacity: 0; }
/* Liquid-dimensional seam (WebGL): the canvas paints the two worlds
   and the liquid boundary; the DOM slices stay as the tap layer and
   the fallback. The band needs a positioning context for the canvas;
   .latent-band.open (higher specificity) still wins when open. */
.latent-band { position: relative; }
.dream-gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s ease;
  z-index: 0;
}
.latent-band.gl-live .slice { background: transparent; clip-path: none; z-index: 1; }
.latent-band.gl-live .seam { display: none; }
.slice img { transition: opacity 0.5s ease; }
.latent-band.gl-live .slice img { opacity: 0; animation: none; }
/* Under GL the band's own height transition must go: it exists only
   on the close path (opening snaps, since CSS cannot animate to
   auto), and while it runs the geometry is mid-flight, so the
   shader's targets get re-read in steps -- a staircase the eye sees
   as stutter. With it off, both directions snap the box and let the
   eased seam + reframing carry the visible motion, symmetrically.
   The no-GL fallback keeps its transition (rule is gl-live
   scoped, per band). */
.latent-band.gl-live { transition: none; }
.slice-title { z-index: 3; }
/* Room for the words to breathe */
.latent-head { padding-bottom: 20px; }
.latent-head .section-note { max-width: 62ch; }
.latent-band { margin-bottom: 10px; }
.latent-band.open .slice { flex-grow: 0.0001; opacity: 0; }
.latent-band.open .slice.active { flex-grow: 30; opacity: 1; clip-path: none; margin-left: 0; }
.latent-band.open .slice-title { opacity: 0; }
.slice-title {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  writing-mode: vertical-rl;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.85);
  transition: opacity 0.3s ease;
}
.slice-cap {
  position: absolute;
  left: 26px;
  bottom: 24px;
  right: 26px;
  text-align: left;
  color: #fff;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.5s ease 0.35s, transform 0.5s ease 0.35s;
  pointer-events: none;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.85);
}
.slice-cap strong { display: block; font-size: 26px; margin-bottom: 6px; }
.slice-cap > span { display: block; color: rgba(255, 255, 255, 0.88); max-width: 520px; }
.slice-cap .slice-hint {
  margin-top: 10px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.65);
}
.latent-band.open .slice.active .slice-cap { opacity: 1; transform: none; }
.slice:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }

/* While a world is open the page does not move: scroll is locked at
   whatever size the display is, and the image fills it */
html.world-lock, html.world-lock body { overflow: hidden; }

/* Over an opened world: header and title block float above the image */
html.latent-live .site-header { position: relative; z-index: 50; }
html.latent-live .latent-head { position: relative; z-index: 45; }
html.latent-live[data-world] .site-header {
  /* Pinned at the top while a world is open. The image itself does NOT
     run under the bar (the opened band starts below it, so the artwork
     stays completely in view); the glass is faked by the ::before layer,
     a duplicated copy of the image's top strip, mirrored and blurred. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  overflow: hidden;
  background: #060607;
}
html.latent-live[data-world] .site-header::before {
  content: "";
  position: absolute;
  inset: -20px;
  background-size: cover;
  background-position: 50% 32%;
  transform: scaleY(-1);
  filter: blur(16px) saturate(1.35);
  pointer-events: none;
}
html.latent-live[data-world] .site-header::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
html.latent-live[data-world] .header-inner { position: relative; z-index: 1; }
html.latent-live[data-world="golden"] .site-header::before { background-image: url("art/golden-city.jpg"); }
html.latent-live[data-world="ember"] .site-header::before { background-image: url("art/obsidian-dragon.jpg"); }
html.latent-live[data-world="abyss"] .site-header::before { background-image: url("art/drowned-bells.jpg"); }
html.latent-live[data-world="shore"] .site-header::before { background-image: url("art/far-shore.jpg"); }
html.latent-live[data-world="golden"] .site-header::after { background: rgba(26, 16, 5, 0.38); }
html.latent-live[data-world="ember"] .site-header::after { background: rgba(22, 9, 5, 0.38); }
html.latent-live[data-world="abyss"] .site-header::after { background: rgba(3, 16, 18, 0.42); }
html.latent-live[data-world="shore"] .site-header::after { background: rgba(6, 18, 10, 0.42); }

/* When a world opens, the text above it fades away entirely */
html.latent-live .latent-head { transition: opacity 0.7s ease; }
html.latent-live[data-world] .latent-head { opacity: 0; pointer-events: none; }

/* Opening a world re-skins the whole page in its colors */
html.latent-live body,
html.latent-live .site-header,
html.latent-live .site-footer,
html.latent-live h1, html.latent-live h2, html.latent-live h3,
html.latent-live .label, html.latent-live .brand, html.latent-live .section-note {
  transition: background-color 0.9s ease, color 0.9s ease, border-color 0.9s ease;
}
html.latent-live[data-world="golden"] {
  --base: #140c05;
  --heading: #ffe9c4;
  --body: #d8c3a5;
  --dim: #a68d6d;
  --teal: #ffb347;
  --gold: #f5c86e;
  --panel: rgba(255, 179, 71, 0.06);
  --panel-border: rgba(255, 179, 71, 0.22);
}
html.latent-live[data-world="ember"] {
  --base: #0e0605;
  --heading: #ffdcc8;
  --body: #d3aca0;
  --dim: #a87a6f;
  --teal: #ff7847;
  --gold: #ff9a5c;
  --panel: rgba(255, 110, 50, 0.06);
  --panel-border: rgba(255, 110, 50, 0.22);
}
html.latent-live[data-world="abyss"] {
  --base: #04120f;
  --heading: #dcf5ec;
  --body: #aacfc4;
  --dim: #71948a;
  --teal: #46e8c0;
  --gold: #ffb36b;
  --panel: rgba(70, 232, 192, 0.06);
  --panel-border: rgba(70, 232, 192, 0.22);
}
html.latent-live[data-world="shore"] {
  --base: #07130a;
  --heading: #eaf7dc;
  --body: #bfd6b2;
  --dim: #8aa87f;
  --teal: #4fe3ae;
  --gold: #ffd968;
  --panel: rgba(110, 230, 160, 0.06);
  --panel-border: rgba(110, 230, 160, 0.22);
}

/* The world's ambient light: fixed glow layers in the image's colors,
   crossfaded in over the page's stock nebula when a world opens */
.world-ambient {
  position: fixed;
  inset: 0;
  z-index: -1;
  opacity: 0;
  transition: opacity 1s ease;
  pointer-events: none;
}
html.latent-live[data-world="golden"] .world-ambient {
  opacity: 1;
  background:
    radial-gradient(720px 480px at 14% -4%, rgba(255, 179, 71, 0.22), transparent 62%),
    radial-gradient(820px 560px at 92% 4%, rgba(255, 122, 40, 0.14), transparent 62%),
    radial-gradient(900px 700px at 85% 90%, rgba(150, 90, 30, 0.10), transparent 62%),
    #140c05;
}
html.latent-live[data-world="ember"] .world-ambient {
  opacity: 1;
  background:
    radial-gradient(720px 480px at 14% -4%, rgba(255, 96, 44, 0.20), transparent 62%),
    radial-gradient(820px 560px at 92% 2%, rgba(110, 130, 255, 0.12), transparent 62%),
    radial-gradient(900px 700px at 85% 90%, rgba(255, 60, 30, 0.10), transparent 62%),
    #0e0605;
}
html.latent-live[data-world="abyss"] .world-ambient {
  opacity: 1;
  background:
    radial-gradient(720px 480px at 14% -4%, rgba(64, 120, 180, 0.16), transparent 62%),
    radial-gradient(820px 560px at 92% 2%, rgba(122, 84, 180, 0.13), transparent 62%),
    radial-gradient(900px 700px at 85% 90%, rgba(38, 224, 188, 0.16), transparent 62%),
    #04120f;
}
html.latent-live[data-world="shore"] .world-ambient {
  opacity: 1;
  background:
    radial-gradient(720px 480px at 14% -4%, rgba(64, 200, 140, 0.18), transparent 62%),
    radial-gradient(820px 560px at 92% 2%, rgba(70, 160, 255, 0.14), transparent 62%),
    radial-gradient(900px 700px at 85% 90%, rgba(150, 230, 90, 0.10), transparent 62%),
    #07130a;
}
@media (prefers-reduced-motion: reduce) {
  .slice img { animation: none; }
  .slice, .latent-band { transition-duration: 0.01s; }
}
@media (max-width: 760px) {
  .latent-band { height: 54vh; min-height: 320px; }
  .slice-cap strong { font-size: 21px; }
}

/* Latent Dreams in portrait: the split turns vertical — worlds stack,
   wavy edges run horizontally, seams ride the horizontal boundaries */
@media (orientation: portrait) {
  .latent-band { flex-direction: column; height: 68vh; }
  .latent-band.open {
  position: fixed;
  inset: var(--bar-h, 57px) 0 0 0;
  height: auto;
  min-height: 0;
  z-index: 40;
}
  .slice + .slice { margin-left: 0; margin-top: -16px; }
  .slice:not(:first-child) {
    clip-path: polygon(0 7px, 5% 6px, 17% 15px, 31% 4px, 45% 14px, 59% 5px, 73% 15px, 87% 4px, 100% 13px, 100% 100%, 0 100%);
  }
  .slice:nth-child(even):not(:first-child) {
    clip-path: polygon(0 13px, 13% 4px, 27% 15px, 42% 5px, 56% 14px, 71% 4px, 85% 15px, 100% 6px, 100% 100%, 0 100%);
  }
  .latent-band.open .slice.active { margin-top: 0; }
  .seam {
    left: 0;
    right: 0;
    top: -19px;
    bottom: auto;
    width: auto;
    height: 38px;
    background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.06), transparent);
    -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
    mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
    animation-name: seamSwayY;
  }
  @keyframes seamSwayY {
    from { transform: translateY(-5px); }
    to { transform: translateY(5px); }
  }
  .slice-title {
    writing-mode: horizontal-tb;
    left: 18px;
    bottom: 14px;
    transform: none;
  }
}


/* Zoom smoothness: while a world opens, its image is pinned at final
   screen size and merely revealed/panned by the growing slice, instead
   of being re-scaled by object-fit on every frame — that rescale kink
   was the jerk. The drift also pauses for the duration of the move. */
.latent-band.open .slice { will-change: flex-grow; }
.latent-band.open .slice img { animation-play-state: paused; }
.latent-band.open .slice.active img {
  position: absolute;
  top: 0;
  left: 50%;
  margin-left: -50vw;
  width: 100vw;
  max-width: none;
  height: calc(100vh - var(--bar-h, 57px));
  height: calc(100dvh - var(--bar-h, 57px));
  /* Subject-safe framing: when the viewport is wider than the image and
     cover has to crop vertically, take more from the bottom so the
     subject near the top survives inside the visible area. */
  object-position: 50% 32%;
}
@media (orientation: portrait) {
  .latent-band.open .slice.active img {
    left: 0;
    margin-left: 0;
    top: 50%;
    margin-top: calc(-50vh + var(--bar-half, 28px));
    margin-top: calc(-50dvh + var(--bar-half, 28px));
  }
}

.slice-cap {
  left: 14px;
  bottom: 14px;
  padding: 14px 16px;
  border-radius: 14px;
  transition: opacity 0.5s ease 0.35s, background-color 0.9s ease;
}
.latent-band.open .slice.active .slice-cap {
  backdrop-filter: blur(10px) saturate(1.3);
  -webkit-backdrop-filter: blur(10px) saturate(1.3);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
html.latent-live[data-world="golden"] .slice-cap { background: rgba(24, 14, 4, 0.36); }
html.latent-live[data-world="ember"] .slice-cap { background: rgba(18, 8, 5, 0.38); }

/* ---------- Latent Sitemap ---------- */
/* The nebula is a fixed full-page background; the page scrolls over it,
   and scrolling raises a frosted layer as the map's content fades. */
/* Touch model (rebuilt 2026-10-26 from the interaction research): the
   scroll-zone model. Ownership of a touch is decided by hit-testing
   at touchstart -- which element the finger lands on -- never by
   classifying the gesture after it begins. Direction classification
   is structurally unreliable on iOS: a claim must land on touchstart
   or the first touchmove, window/body listeners are passive by
   default in both engines, and moves turn uncancelable once the
   browser starts scrolling. Page-wide pan-y fails differently: it
   hands every vertical drag to the browser, so vertical steering
   dies with it, and WebKit has no pan-down for an asymmetric fix.
   - The canvas stays purely visual (pointer-events: none). Input
     lives on #latent-hit, a dedicated surface over it -- the
     canvas's SIBLING, never a parent or child, so its
     touch-action: none is never intersected with the zone's pan-y.
   - The scroll zone (.latent-scrollzone) is the first screen's one
     scrollable surface: touch-action: pan-y there, and it doubles
     as a button that scrolls to the list.
   - Once the page is scrolled to the content (body[data-view=
     "content"], flipped with hysteresis in the page script), the
     hit surface goes inert and the page scrolls like any other. */
body.sitemap-body { overscroll-behavior-y: none; }
body.sitemap-body a, body.sitemap-body button { touch-action: manipulation; }
#latent-hit {
  position: fixed;
  inset: 0;
  z-index: 2;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
body[data-view="content"] #latent-hit { pointer-events: none; }
#latent-map {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
}
/* No backdrop-filter here: a full-screen animated filter over a live
   canvas is what made WebKit flicker. The frost is baked instead —
   soft region-colored glows + a dark tint (inline on the element),
   with the canvas veiling itself as the glass rises. */
.latent-glass-layer {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  background: rgba(6, 8, 16, 0.55);
}
body.sitemap-body .site-header,
body.sitemap-body .site-footer { position: relative; z-index: 4; }
/* On Atlas the header floats over the living nebula, so the bar and
   the opened nav menu get the frosted glass the other pages don't
   need: same header, glassed. */
body.sitemap-body .site-header {
  background: rgba(8, 11, 22, 0.42);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
}
.sitemap-flow { position: relative; z-index: 3; pointer-events: none; }
.sitemap-flow a, .sitemap-flow button { pointer-events: auto; }
/* The Latent Dreams glass: tinted translucency, backdrop blur, a lit rim. */
.latent-glass {
  background: rgba(8, 11, 22, 0.44);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.09),
              inset 0 1px 0 rgba(255, 255, 255, 0.13);
  border-radius: 14px;
}
.latent-intro {
  position: relative;
  height: calc(100vh - 118px);
  height: calc(100svh - 118px);
  min-height: 440px;
}
/* The page header follows the same pattern as every other page —
   eyebrow label, heading, note — carried on frosted glass because the
   nebula is the whole page here. */
.latent-phead {
  position: absolute;
  top: 12px;
  left: 0;
  right: auto;
  width: min(540px, 100%);
  padding: 14px 18px 13px;
  background:
    radial-gradient(120% 150% at 8% 0%, rgba(34, 190, 205, 0.16), rgba(34, 190, 205, 0) 55%),
    radial-gradient(120% 160% at 96% 110%, rgba(216, 96, 158, 0.13), rgba(216, 96, 158, 0) 55%),
    linear-gradient(120deg, rgba(34, 190, 205, 0.07), rgba(124, 93, 250, 0.07) 55%, rgba(228, 96, 52, 0.06)),
    rgba(8, 11, 22, 0.36);
}
.latent-phead h1 { font-size: 30px; color: var(--heading); margin: 3px 0 5px; }
.latent-phead .phead-note { color: #a7b3c7; font-size: 14.5px; margin: 0; }
/* The scroll zone: a real button and the first screen's one
   scrollable surface -- this page's bottom-sheet peek. Full width,
   flush to the bottom edge (it has to reach the screen edges, not
   stop at the content column), 64px plus the safe-area inset, with
   the label naming where it goes and the chevron centered in it. Dragging on it scrolls natively
   (touch-action: pan-y beats the generic button rule on
   specificity); tapping it scrolls to the list. */
body.sitemap-body .latent-scrollzone {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 64px;
  padding: 9px 16px calc(9px + env(safe-area-inset-bottom, 0px));
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
  background: linear-gradient(to top, rgba(6, 8, 16, 0.8), rgba(6, 8, 16, 0.58));
  backdrop-filter: blur(10px) saturate(1.4);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
  color: rgba(226, 240, 255, 0.88);
  font: inherit;
  cursor: pointer;
  touch-action: pan-y;
  transition: opacity 0.35s ease;
}
.latent-scrollzone.cue-gone { opacity: 0; pointer-events: none; }
.zone-label {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-indent: 0.3em;
}
.cue-arrow { display: flex; flex-direction: column; align-items: center; }
.cue-arrow i {
  width: 14px;
  height: 14px;
  border-right: 2.5px solid rgba(94, 234, 212, 0.9);
  border-bottom: 2.5px solid rgba(94, 234, 212, 0.9);
  transform: rotate(45deg);
  margin-top: -5px;
  animation: cueDrop 1.8s ease-in-out infinite;
}
.cue-arrow i:last-child { animation-delay: 0.25s; }
@keyframes cueDrop {
  0%, 100% { opacity: 0.25; transform: rotate(45deg) translate(0, 0); }
  50% { opacity: 1; transform: rotate(45deg) translate(4px, 4px); }
}
@media (prefers-reduced-motion: reduce) { .cue-arrow i { animation: none; } }
.sitemap-list-sec { margin-top: 7vh; padding-bottom: 9vh; }
.sitemap-list-sec h2, .sitemap-list-sec .section-note { text-shadow: 0 1px 14px rgba(0, 0, 0, 0.65); }
.map-groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 26px;
  margin-top: 18px;
}
.map-group h3 {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  margin-bottom: 10px;
}
.map-group h3 a { color: var(--teal); text-decoration: none; }
.map-group ul { list-style: none; }
.map-group li { margin: 5px 0; }
.map-group li a { color: #a7b3c7; text-decoration: none; }
.map-group li a:hover, .map-group h3 a:hover { color: #f4f7fb; }
@media (max-width: 720px) {
  .latent-intro { min-height: 400px; }
  .latent-phead { padding: 13px 15px 12px; }
  .latent-phead h1 { font-size: 24px; }
}
