/* ============ tokens ============ */
:root {
  --black: #050507;
  --ink: #0c0c10;
  --white: #f2f2f0;
  --gray-1: #9a9aa0;
  --gray-2: #55555c;
  --gray-3: #232328;
  --line: rgba(242, 242, 240, 0.12);
  --font-display: "Space Grotesk", system-ui, sans-serif;
  --font-body: "Inter", system-ui, sans-serif;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ============ base ============ */
* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; overflow-x: clip; }
/* while the freeflight overlay is open, fully lock the page behind it — kills the
   native scrollbar AND stops scroll velocity (which drives the warp/scroll sound) */
html.ff-lock { overflow: hidden; }

body {
  background: var(--black);
  color: var(--white);
  font-family: var(--font-body);
  font-weight: 300;
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  /* the site reads as a transmission, not a document — text is not selectable
     (also keeps the monochrome frame clean: no stray highlight rectangles) */
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-touch-callout: none; /* iOS: suppress long-press selection/callout */
}

/* thin monochrome scrollbar */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--black); }
::-webkit-scrollbar-thumb { background: var(--gray-3); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--gray-2); }

/* ============ background layers ============ */
#space {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -2;
  display: block;
}

.grain {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.035;
  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='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ============ scroll progress ============ */
.progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: 100;
  background: transparent;
}
.progress span {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--gray-2), var(--white));
  transition: width 0.1s linear;
}

/* ============ skip link (keyboard a11y) ============ */
.skip-link {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 300;
  padding: 0.7rem 1.1rem;
  background: var(--white);
  color: var(--black);
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-decoration: none;
  transform: translateY(-130%);
  transition: transform 0.2s ease;
}
.skip-link:focus { transform: translateY(0); outline: none; }

/* ============ nav ============ */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.4rem 3rem;
  mix-blend-mode: difference;
}

.nav-logo {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.35em;
  color: var(--white);
  text-decoration: none;
}

.nav-links { display: flex; gap: 2.2rem; }

.nav-links a {
  position: relative;
  color: var(--gray-1);
  text-decoration: none;
  font-size: 0.8rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  transition: color 0.35s var(--ease-out);
}
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: -5px;
  width: 100%;
  height: 1px;
  background: var(--white);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.45s var(--ease-out);
}
.nav-links a:hover { color: var(--white); }
.nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }

/* ============ layout ============ */
.section {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh; /* dynamic vh — no jump when the mobile URL bar shows/hides */
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 clamp(1.5rem, 6vw, 6rem);
  max-width: 1400px;
  margin: 0 auto;
}

/* ============ typography ============ */
.eyebrow {
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--gray-1);
  margin-bottom: 1.6rem;
}

.big {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.6rem, 8vw, 7rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
}

.medium {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2rem, 4.5vw, 3.4rem);
  line-height: 1.1;
}

.outline {
  color: var(--white); /* solid fallback if -webkit-text-stroke is unsupported */
}
@supports (-webkit-text-stroke: 1px #fff) {
  .outline {
    color: transparent;
    -webkit-text-stroke: 1.5px var(--white);
  }
}

/* ============ hero ============ */
.hero { align-items: flex-start; }

.hero-kicker {
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.4em;
  color: var(--gray-1);
  margin-bottom: 2rem;
}

.hero-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(3.2rem, 11vw, 9rem);
  line-height: 1.02;
  letter-spacing: -0.025em;
}

.hero-sub {
  margin-top: 2.2rem;
  max-width: 460px;
  color: var(--gray-1);
  font-size: clamp(0.95rem, 1.4vw, 1.1rem);
}

.scroll-cue {
  position: absolute;
  bottom: 3rem;
  left: clamp(1.5rem, 6vw, 6rem);
  display: flex;
  align-items: center;
  gap: 1rem;
  color: var(--gray-2);
  font-size: 0.72rem;
  letter-spacing: 0.35em;
  text-transform: uppercase;
}

.scroll-line {
  width: 70px;
  height: 1px;
  background: var(--gray-3);
  position: relative;
  overflow: hidden;
}
.scroll-line::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--white);
  animation: scroll-sweep 2.4s var(--ease-out) infinite;
}
@keyframes scroll-sweep {
  0%   { transform: translateX(-100%); }
  55%  { transform: translateX(0); }
  100% { transform: translateX(100%); }
}

/* ============ statements ============ */
.statement { text-align: center; align-items: center; }

/* ============ freeflight (link to the drift sub-experience) ============ */
.hero-freeflight {
  display: inline-block; margin-top: 2.4rem;
  font-family: var(--font-display); font-size: 0.72rem; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--gray-1); text-decoration: none; border-bottom: 1px solid var(--gray-3); padding-bottom: 0.35rem; width: fit-content;
  transition: color 0.3s ease, border-color 0.3s ease;
}
.hero-freeflight:hover { color: var(--white); border-color: var(--gray-1); }
.freeflight { text-align: center; align-items: center; }
.freeflight-sub { margin-top: 1.6rem; max-width: 480px; color: var(--gray-1); font-size: clamp(0.95rem, 1.4vw, 1.1rem); line-height: 1.6; }
.freeflight-enter {
  display: inline-block; margin-top: 2.8rem;
  font-family: var(--font-display); font-size: clamp(0.9rem, 2vw, 1.1rem); letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--white); text-decoration: none; border: 1px solid var(--gray-2); padding: 0.9rem 1.9rem;
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
.freeflight-enter:hover { background: var(--white); color: var(--black); border-color: var(--white); }
/* the site's sign-off — sits below the freeflight CTA as the final marker */
.transmission-end { margin-top: clamp(2.6rem, 7vh, 5rem); opacity: 0.6; }

/* freeflight in-page overlay (the drift loads here as an iframe; one site, no nav) */
.ff-overlay {
  position: fixed; inset: 0; z-index: 200; background: var(--black);
  opacity: 0; visibility: hidden; transition: opacity 0.5s ease, visibility 0.5s;
}
.ff-overlay.open { opacity: 1; visibility: visible; }
.ff-overlay iframe { width: 100%; height: 100%; border: 0; display: block; }
.ff-warning {
  position: fixed; left: 50%; top: clamp(1.4rem, 6vh, 3.4rem); z-index: 210;
  transform: translate(-50%, -8px); max-width: 82vw; pointer-events: none;
  font-family: var(--font-display); font-size: 0.66rem; letter-spacing: 0.22em;
  text-transform: uppercase; text-align: center; color: var(--gray-1);
  padding: 0.7rem 1.05rem; border: 1px solid var(--gray-3); background: rgba(5, 5, 7, 0.72);
  opacity: 0; visibility: hidden;
  transition: opacity 0.6s ease, transform 0.6s ease, visibility 0.6s;
}
.ff-warning.show { opacity: 0.92; visibility: visible; transform: translate(-50%, 0); }
@media (max-width: 600px) {
  /* tighten + drop below the fixed nav on small screens so it never overflows or collides */
  .ff-warning { max-width: 90vw; font-size: 0.58rem; letter-spacing: 0.16em; padding: 0.55rem 0.8rem; top: clamp(3.6rem, 9vh, 4.6rem); }
}

/* ============ about ============ */
.about-grid {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: clamp(2rem, 6vw, 6rem);
  align-items: start;
}

/* ---- manifest: telemetry-style key/value readout ---- */
.manifest {
  display: flex;
  flex-direction: column;
  max-width: 46ch;
}
.manifest-row {
  display: grid;
  grid-template-columns: minmax(6.5rem, 0.4fr) 1fr;
  gap: 1.6rem;
  align-items: baseline;
  padding: 1.05rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.11);
}
.manifest-row:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.11); }
.manifest dt {
  font-family: var(--font-display);
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gray-1); /* was --gray-2 (2.75:1, failed WCAG AA on black) */
  white-space: nowrap;
}
.manifest dd {
  color: var(--white);
  font-size: 1.05rem;
  line-height: 1.5;
}
.manifest-row:last-child dd { color: var(--gray-1); } /* the mission reads as body copy */

.about-tags {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 2.2rem;
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gray-1);
}
.about-tags li:not(:last-child)::after {
  content: "·";
  margin: 0 0.85rem;
  color: var(--gray-2);
}

/* ============ projects ============ */
.projects { justify-content: center; }

.projects-head { margin-bottom: clamp(2.4rem, 6vh, 4.5rem); }
.projects-title { margin-top: 0.7rem; }
.projects-sub {
  margin-top: 1rem;
  color: var(--gray-1);
  font-size: 1rem;
  max-width: 38ch;
}

.project-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
}

.project {
  display: flex;
  align-items: center;
  gap: clamp(1.2rem, 4vw, 3.5rem);
  padding: 2.4rem 0.5rem;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  color: inherit;
  isolation: isolate; /* blend children against the sweep only, not the starfield */
}

/* hover inversion sweep: white block enters from the left,
   exits through the right; children flip via difference blend */
.project::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--white);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.45s cubic-bezier(0.76, 0, 0.24, 1);
}
.project > *:not(.row-line) {
  position: relative;
  z-index: 1;
  mix-blend-mode: difference;
}
/* hover affordances only where hover actually exists — on touch,
   sticky tap-hover would leave a row permanently inverted */
@media (hover: hover) {
  .project:hover::before,
  .project:focus-visible::before {
    transform: scaleX(1);
    transform-origin: left;
  }
  .project:hover .project-num,
  .project:focus-visible .project-num {
    color: var(--white); /* difference flips it to near-black on the sweep */
  }
}
/* keyboard parity outside the hover gate too (hover-less devices w/ a keyboard) */
.project:focus-visible::before {
  transform: scaleX(1);
  transform-origin: left;
}
.project:focus-visible .project-num { color: var(--white); }

.project-num {
  font-family: var(--font-display);
  font-size: 0.85rem;
  color: var(--gray-2);
  letter-spacing: 0.2em;
  transition: color 0.4s var(--ease-out);
}

.project-body h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.3rem, 2.6vw, 2rem);
  margin-bottom: 0.3rem;
}
.project-body p { color: var(--gray-1); font-size: 0.95rem; }

.project-arrow {
  margin-left: auto;
  font-size: 1.4rem;
  color: var(--gray-2);
  transition: transform 0.4s var(--ease-out), color 0.4s var(--ease-out);
}
@media (hover: hover) {
  .project:hover .project-arrow,
  .project:focus-visible .project-arrow {
    transform: translateX(8px);
    color: var(--white);
  }
}

/* ============ contact (igloo-style particle channels) ============ */
.contact { text-align: center; align-items: center; }

/* reserves the central column the WebGL orb floats in (#space renders it) */
.orb-stage {
  flex: 1 1 auto;
  min-height: 44vh;
  width: 100%;
  pointer-events: none;
}

/* side chevrons at the orb's flanks */
.orb-arrow {
  position: absolute;
  top: 46%;
  width: 48px;
  height: 48px;
  background: none;
  border: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--gray-2);
  z-index: 6;
  transition: color 0.35s var(--ease-out), transform 0.35s var(--ease-out), opacity 0.5s ease;
}
.orb-arrow::before {
  content: "";
  width: 13px;
  height: 13px;
  border-left: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
}
.orb-arrow-l { left: clamp(0.8rem, 6vw, 5rem); }
.orb-arrow-l::before { transform: rotate(45deg); margin-left: 4px; }
.orb-arrow-r { right: clamp(0.8rem, 6vw, 5rem); }
.orb-arrow-r::before { transform: rotate(-135deg); margin-right: 4px; }
@media (hover: hover) {
  .orb-arrow:hover { color: var(--white); }
  .orb-arrow-l:hover { transform: translateX(-5px); }
  .orb-arrow-r:hover { transform: translateX(5px); }
}
.orb-arrow:focus-visible { color: var(--white); outline: 1px solid var(--white); outline-offset: 4px; }

/* bottom channel selector */
.orb-console {
  position: relative;
  display: inline-flex;
  gap: clamp(1.4rem, 5vw, 3.4rem);
  align-items: center;
  margin-top: 1rem;
}
.orb-channel {
  position: relative;
  z-index: 2;
  font-family: var(--font-display);
  font-size: 0.82rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gray-1);
  text-decoration: none;
  padding: 0.5rem 0.45rem;
  white-space: nowrap;
  opacity: 0.6; /* was 0.42 — inactive channels were ~2.1:1, near-invisible */
  transition: color 0.4s var(--ease-out), opacity 0.4s var(--ease-out);
}
.orb-channel.active { color: var(--white); opacity: 1; }
.orb-channel.active::after { content: " ↗"; font-size: 0.78em; opacity: 0.55; }
@media (hover: hover) {
  .orb-channel:hover { color: var(--white); opacity: 0.85; }
}
.orb-channel:focus-visible { color: var(--white); opacity: 1; outline: 1px solid var(--white); outline-offset: 4px; }

/* reticle frame — JS sets left/top/width/height to overlay the active channel */
.orb-frame {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 1;
  transition: left 0.5s var(--ease-out), top 0.5s var(--ease-out),
    width 0.5s var(--ease-out), height 0.5s var(--ease-out);
}
.orb-frame i {
  position: absolute;
  width: 7px;
  height: 7px;
  border: 1.5px solid var(--white);
}
.orb-frame i:nth-child(1) { left: -6px; top: -6px; border-right: 0; border-bottom: 0; }
.orb-frame i:nth-child(2) { right: -6px; top: -6px; border-left: 0; border-bottom: 0; }
.orb-frame i:nth-child(3) { left: -6px; bottom: -6px; border-right: 0; border-top: 0; }
.orb-frame i:nth-child(4) { right: -6px; bottom: -6px; border-left: 0; border-top: 0; }

@media (max-width: 760px) {
  .orb-channel { font-size: 0.72rem; letter-spacing: 0.16em; }
  .orb-arrow { width: 40px; height: 40px; }
}

.nav-links a:focus-visible {
  color: var(--white);
}
.nav-links a:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* ============ footer ============ */
.footer {
  display: flex;
  justify-content: center;
  gap: 0.8rem;
  padding: 2.5rem 1rem;
  color: var(--gray-1); /* was --gray-2 (failed WCAG AA on black) */
  font-size: 0.78rem;
  letter-spacing: 0.15em;
}

/* ============ sound toggle (transmission-style text control) ============ */
.sound-btn {
  position: fixed;
  bottom: 2rem;
  right: 2rem;
  z-index: 95;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0.1rem;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--gray-1);
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color 0.4s var(--ease-out), opacity 0.5s ease;
}
/* reticle brackets, echoing the contact selector + loader UI */
.sound-btn::before { content: "["; margin-right: 0.55rem; }
.sound-btn::after { content: "]"; margin-left: 0.55rem; }
.sound-btn::before,
.sound-btn::after { color: var(--gray-2); transition: color 0.4s var(--ease-out); }
.sound-btn:hover,
.sound-btn:focus-visible { color: var(--white); }
.sound-btn:focus-visible { outline: 1px solid var(--white); outline-offset: 4px; }
.sound-btn:hover::before, .sound-btn:hover::after,
.sound-btn:focus-visible::before, .sound-btn:focus-visible::after { color: var(--gray-1); }
.sound-btn.on { color: var(--white); }
.sound-btn.on::before, .sound-btn.on::after { color: var(--white); }

/* gentle invite pulse when OFF — nudges first-timers to enter the transmission */
.sound-btn:not(.on) .sound-label { animation: sound-invite 2.6s ease-in-out infinite; }
@keyframes sound-invite { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }

/* equalizer bars: tucked away when off, slide + dance in when the signal is live */
.eq {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 12px;
  width: 0;
  opacity: 0;
  overflow: hidden;
  transition: width 0.45s var(--ease-out), opacity 0.45s var(--ease-out);
}
.sound-btn.on .eq { width: 19px; opacity: 1; }
.eq-bar {
  width: 2px;
  height: 4px;
  background: currentColor;
  transition: height 0.3s var(--ease-out);
}
.sound-btn.on .eq-bar:nth-child(1) { animation: eq-bounce 0.9s ease-in-out infinite; }
.sound-btn.on .eq-bar:nth-child(2) { animation: eq-bounce 0.7s ease-in-out 0.1s infinite; }
.sound-btn.on .eq-bar:nth-child(3) { animation: eq-bounce 1.1s ease-in-out 0.2s infinite; }
.sound-btn.on .eq-bar:nth-child(4) { animation: eq-bounce 0.8s ease-in-out 0.05s infinite; }
@keyframes eq-bounce {
  0%, 100% { height: 4px; }
  50% { height: 11px; }
}
/* live mode: JS drives the bars from the real audio spectrum —
   the CSS bounce above is only the no-analyser fallback */
.sound-btn.live .eq-bar {
  animation: none;
  height: 12px;
  transform: scaleY(0.22);
  transform-origin: center bottom;
  transition: none;
}

@media (max-width: 760px) {
  .sound-btn { bottom: 1.2rem; right: 1.2rem; font-size: 0.62rem; letter-spacing: 0.16em; }
  .sound-btn::before { margin-right: 0.4rem; }
  .sound-btn::after { margin-left: 0.4rem; }
}

/* ============ split-text machinery (anime.js path) ============ */
.ch-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
.ch {
  display: inline-block;
  will-change: transform;
}
.line-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
.line-inner {
  display: inline-block;
  will-change: transform;
}
.ht-line { display: inline-block; }
.word {
  display: inline-block;
  white-space: nowrap;
}

/* ============ pre-ready gate ============ */
/* until the loader hands off (html.is-ready), nothing but the loader
   and the starfield may be visible — covers nav, content, chrome */
html:not(.is-ready) .nav,
html:not(.is-ready) .sound-btn,
html:not(.is-ready) main,
html:not(.is-ready) .footer {
  opacity: 0 !important;
  pointer-events: none;
}

/* ============ preloader ============ */
#loader {
  position: fixed;
  inset: 0;
  z-index: 200;
  /* translucent veil — the starfield glows dimly behind it while loading,
     so the curtain exit reads as lifting a veil off space */
  background: rgba(5, 5, 7, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
}
.loader-inner {
  width: min(420px, 80vw);
}
.loader-word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.4em;
  color: var(--white);
}
.loader-status {
  display: flex;
  justify-content: space-between;
  margin-top: 1.3rem;
  font-family: var(--font-display);
  font-size: 0.68rem;
  letter-spacing: 0.3em;
  color: var(--gray-1);
}
.loader-count { font-variant-numeric: tabular-nums; }
.loader-bar {
  margin-top: 0.7rem;
  height: 1px;
  background: var(--gray-3);
  overflow: hidden;
}
.loader-bar-fill {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--white);
  transform: scaleX(0);
  transform-origin: left;
}

/* ============ contact meta (arrival readout) ============ */
.contact-meta {
  margin-top: 1.6rem;
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--gray-1);
}

/* ============ custom cursor ============ */
.has-cursor,
.has-cursor * {
  cursor: none !important;
}
.cursor-dot,
.cursor-ring {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 250; /* above the transmission overlay (200), below skip-link (300) */
  opacity: 0;
  border-radius: 50%;
  /* a dark halo keeps the white cursor visible on BOTH the dark space and the
     bright particle icon (difference-blend vanished against the white glyph) */
  filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9));
  transition: opacity 0.3s ease;
}
.cursor-dot {
  width: 8px;
  height: 8px;
  background: #fff;
}
.cursor-ring {
  width: 38px;
  height: 38px;
  border: 1px solid rgba(255, 255, 255, 0.65);
}
/* freeflight overlay open → hide the v7 page cursor (the iframe draws its own). Otherwise the
   parent stops getting pointermove while the mouse is over the iframe, so its custom cursor freezes
   in place — a second, stale cursor on top of the freeflight's. */
html.ff-lock .cursor-dot, html.ff-lock .cursor-ring { opacity: 0 !important; }

/* typewriter cursor (about heading) */
.type-cursor {
  display: inline-block;
  margin-left: 0.06em;
  color: var(--white);
  animation: cursor-blink 0.75s steps(1) infinite;
}
.type-cursor.done { animation: cursor-blink 0.75s steps(1) 3, cursor-fade 0.6s ease 2.2s forwards; }
@keyframes cursor-blink { 50% { opacity: 0; } }
@keyframes cursor-fade { to { opacity: 0; } }

/* project rows: JS-drawn lines replace the static borders */
.project { position: relative; }
.fx-rows .project { border-bottom-color: transparent; }
.row-line {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--line);
  transform-origin: left;
  pointer-events: none;
}

/* ============ reveal animation ============ */
.reveal {
  opacity: 0;
  transform: translateY(46px);
  transition:
    opacity 1.1s var(--ease-out),
    transform 1.1s var(--ease-out);
  will-change: opacity, transform;
}
.reveal.visible { opacity: 1; transform: translateY(0); }
.d-1 { transition-delay: 0.12s; }
.d-2 { transition-delay: 0.24s; }

/* ============ reduced motion ============ */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .scroll-line::after { animation: none; }
  .sound-btn:not(.on) .sound-label { animation: none; opacity: 1; }
  html { scroll-behavior: auto; }
  .project::before,
  .project-arrow,
  .project-num,
  .nav-links a::after,
  .sound-btn,
  .orb-frame,
  .orb-arrow,
  .progress span { transition: none; }
}

/* ============ responsive ============ */
@media (max-width: 760px) {
  .nav { padding: 1.1rem 1.4rem; gap: 1rem; }
  .nav-logo { font-size: 0.78rem; letter-spacing: 0.18em; }
  .nav-links { gap: 1.1rem; }
  .nav-links a { font-size: 0.68rem; letter-spacing: 0.12em; }
  .about-grid { grid-template-columns: 1fr; }
  .project { flex-wrap: wrap; }
  .project-arrow { display: none; }
  /* narrow phones: let the manifest label wrap and keep the channel row from
     overflowing (which would also mis-place the reticle frame) */
  .manifest dt { white-space: normal; }
  .orb-console { max-width: 100%; gap: 1.1rem; }
  .orb-channel { padding: 0.85rem 0.55rem; } /* ~44px tap target on touch */
  /* taller tap target for the sound toggle (longhand overrides only the
     vertical side of the base 0.45rem shorthand) */
  .sound-btn { padding-top: 0.95rem; padding-bottom: 0.95rem; }
  /* projects content is tall on phones; centering pushed the eyebrow/title up
     behind the fixed nav — top-align it with clearance for the nav instead */
  .section.projects { justify-content: flex-start; padding-top: clamp(5rem, 13vh, 7rem); padding-bottom: 2.5rem; }
}

/* ============ transmission detail overlay (project case study) ============ */
.tx-overlay {
  position: fixed;
  inset: 0;
  z-index: 200; /* above nav(90)/sound(95), below skip-link(300) */
  display: flex;
  align-items: center;
  justify-content: center;
  perspective: 1400px; /* gives the panel's cursor-tilt real 3D depth */
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.tx-panel { will-change: transform; }
.tx-overlay.open { pointer-events: auto; }
.tx-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(5, 5, 7, 0.6); /* page content is hidden on open — this just sits over the bare starfield */
  -webkit-backdrop-filter: blur(2.5px);
  backdrop-filter: blur(2.5px);
}

/* glitch "decode" — a mono channel-split + horizontal slice-displacement tear,
   applied to a heading while its text scrambles in (the computer/terminal feel) */
@keyframes tx-glitch {
  0%   { transform: translateX(-3px); clip-path: inset(0 0 0 0);      text-shadow: 3px 0 rgba(242,242,240,0.6), -2px 0 rgba(242,242,240,0.35); }
  18%  { transform: translateX(4px);  clip-path: inset(0 0 68% 0); }
  34%  { transform: translateX(-2px); clip-path: inset(55% 0 0 0);    text-shadow: -4px 0 rgba(242,242,240,0.5), 3px 0 rgba(242,242,240,0.3); }
  50%  { transform: translateX(3px);  clip-path: inset(0 0 34% 0); }
  66%  { transform: translateX(-1px); clip-path: inset(26% 0 30% 0);  text-shadow: 2px 0 rgba(242,242,240,0.4), -1px 0 rgba(242,242,240,0.25); }
  82%  { transform: translateX(1px);  clip-path: inset(0 0 0 0); }
  100% { transform: none;             clip-path: inset(0 0 0 0);      text-shadow: none; }
}
.tx-glitching { animation: tx-glitch 0.5s steps(7, jump-none) both; will-change: transform, clip-path; }
@media (prefers-reduced-motion: reduce) { .tx-glitching { animation: none; } }
.tx-panel {
  position: relative; /* centered by the overlay's flexbox — so GSAP's x glitch doesn't fight a transform-based centering */
  width: min(92vw, 720px);
  max-height: 86vh;
  overflow-y: auto;
  padding: clamp(1.5rem, 4vw, 2.8rem);
  text-align: left;
}
/* reticle corner-brackets (same language as the contact .orb-frame) */
.tx-frame { position: absolute; inset: 0; pointer-events: none; }
.tx-frame i { position: absolute; width: 13px; height: 13px; border: 1.5px solid var(--white); }
.tx-frame i:nth-child(1) { left: 0; top: 0; border-right: 0; border-bottom: 0; }
.tx-frame i:nth-child(2) { right: 0; top: 0; border-left: 0; border-bottom: 0; }
.tx-frame i:nth-child(3) { left: 0; bottom: 0; border-right: 0; border-top: 0; }
.tx-frame i:nth-child(4) { right: 0; bottom: 0; border-left: 0; border-top: 0; }
.tx-close {
  position: absolute;
  top: clamp(0.9rem, 3vw, 1.8rem);
  right: clamp(0.9rem, 3vw, 1.8rem);
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--gray-1);
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  transition: color 0.35s var(--ease-out);
}
.tx-close::before { content: "[ "; color: var(--gray-2); }
.tx-close::after { content: " ]"; color: var(--gray-2); }
.tx-close:hover, .tx-close:focus-visible { color: var(--white); }
.tx-close:focus-visible { outline: 1px solid var(--white); outline-offset: 4px; }
.tx-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  padding-right: 4.5rem; /* clear the CLOSE control */
}
.tx-id { color: var(--gray-1); }
.tx-status { color: var(--gray-2); white-space: nowrap; }
.tx-codename { margin: 0.5rem 0 0; }
.tx-rule { display: block; height: 1px; background: var(--line); margin: 1.1rem 0 1.4rem; }
.tx-marker {
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.22em;
  color: var(--gray-2);
  margin-top: 1.5rem;
}
.tx-summary {
  color: var(--gray-1);
  /* monospace = every glyph is the same width, so the static/decode noise can
     never re-wrap the lines (no layout jump) — and it reads like a terminal log */
  font-family: ui-monospace, "Cascadia Code", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.92rem;
  line-height: 1.7;
  letter-spacing: 0;
  max-width: 58ch;
  margin-top: 0.5rem;
}
/* preserve spaces literally (no collapsing) so the per-frame noise can't jitter
   the line width — with monospace + a fixed char count the width is now constant */
.tx-summary, .tx-summary .tx-line { font-family: inherit; white-space: pre-wrap; }
.tx-stack { margin-top: 0.7rem; }
.tx-links { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2rem; margin-top: 1.9rem; }
.tx-link {
  font-family: var(--font-display);
  font-size: 0.82rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--white);
  text-decoration: none;
}
.tx-link .tx-slash { color: var(--gray-2); }
.tx-arrow { display: inline-block; transition: transform 0.3s var(--ease-out); }
.tx-link:hover .tx-arrow, .tx-link:focus-visible .tx-arrow { transform: translate(3px, -3px); }
.tx-link:focus-visible { outline: 1px solid var(--white); outline-offset: 4px; }
.tx-link[hidden] { display: none; }
/* work-in-progress projects: a muted, non-clickable "COMING SOON" instead of a repo link */
.tx-link.is-soon { color: var(--gray-2); cursor: default; pointer-events: none; }
.tx-nudge {
  display: block;
  margin-top: 1.7rem;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--gray-1);
  font-family: var(--font-display);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  transition: color 0.35s var(--ease-out);
}
.tx-nudge:hover, .tx-nudge:focus-visible { color: var(--white); }
.tx-nudge[hidden] { display: none; }
/* prev / next chevrons (same language as the contact .orb-arrow) */
.tx-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  cursor: pointer;
}
.tx-prev { left: clamp(0.4rem, 3vw, 2.4rem); }
.tx-next { right: clamp(0.4rem, 3vw, 2.4rem); }
.tx-nav::before {
  content: "";
  width: 11px;
  height: 11px;
  border-top: 1.5px solid var(--gray-1);
  border-right: 1.5px solid var(--gray-1);
  transition: border-color 0.35s var(--ease-out);
}
.tx-prev::before { transform: rotate(-135deg); margin-left: 5px; }
.tx-next::before { transform: rotate(45deg); margin-right: 5px; }
.tx-nav:hover::before, .tx-nav:focus-visible::before { border-color: var(--white); }
.tx-nav:focus-visible { outline: 1px solid var(--white); outline-offset: 2px; }

/* tune-in dial — drag to lock the signal (decryption + warp + audio + meter follow) */
.tx-tuner { margin: 1.4rem 0 0.4rem; max-width: 54ch; }
.tx-tuner-row { display: flex; justify-content: space-between; gap: 1rem; font-family: var(--font-display); font-size: 0.66rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gray-1); } /* was --gray-2 (2.75:1, failed AA) */
.tx-tuner-freq, .tx-tuner-meter { color: var(--gray-1); }
.tx-tuner-meter { letter-spacing: 0.14em; }
.tx-tuner-track { position: relative; height: 2px; margin: 1rem 0 0.7rem; background: var(--line); touch-action: none; }
.tx-tuner-track::before { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(90deg, var(--gray-3) 0 1px, transparent 1px 13px); opacity: 0.6; }
.tx-tuner-lock { position: absolute; top: 50%; transform: translate(-50%, -50%); width: 1px; height: 12px; background: var(--gray-1); opacity: 0.7; }
.tx-tuner-handle { position: absolute; top: 50%; left: 0; transform: translate(-50%, -50%); width: 30px; height: 30px; padding: 0; background: none; border: 0; cursor: grab; touch-action: none; }
.tx-tuner-handle::before { content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 2px; height: 18px; background: var(--white); }
.tx-tuner-handle::after { content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 14px; height: 22px; border: 1px solid rgba(242, 242, 240, 0.5); }
.tx-tuner-handle:active { cursor: grabbing; }
.tx-tuner-handle:focus-visible { outline: 1px solid var(--white); outline-offset: 4px; }
.tx-tuner-hint { display: block; font-family: var(--font-display); font-size: 0.6rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gray-2); text-align: center; }
/* signal-locked "haptic" pop on the dial handle (keeps the centering translate) */
@keyframes tx-lock-pop {
  0%   { transform: translate(-50%, -50%) scale(1.7); }
  55%  { transform: translate(-50%, -50%) scale(0.9); }
  100% { transform: translate(-50%, -50%) scale(1); }
}
.tx-tuner-handle.lock-pop { animation: tx-lock-pop 0.5s var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .tx-tuner-handle.lock-pop { animation: none; } }

@media (max-width: 760px) {
  .tx-panel { width: 92vw; max-height: 84vh; }
  .tx-links { gap: 1.3rem; }
  /* stack the header so the long id + status don't wrap mid-line */
  .tx-head { display: block; padding-right: 4rem; }
  .tx-status { display: block; margin-top: 0.25rem; }
  /* chevrons drop to the bottom corners; swipe also flips */
  .tx-nav { top: auto; bottom: 1rem; transform: none; }
}
