/* ============================================================
   RESET & BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, h4, p, ul, ol, figure, dl, pre { margin: 0; padding: 0; }
ul[class], ol[class] { list-style: none; }
img, svg { max-width: 100%; display: block; }
a { color: inherit; }
button { font: inherit; cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   TOKENS — sci-fi console palette
   ============================================================ */
:root {
  --c-orange:  #ff9966;
  --c-peach:   #ffaa99;
  --c-rust:    #cc6666;
  --c-gold:    #ffcc66;
  --c-beige:   #ffcc99;
  --c-purple:  #9999cc;
  --c-blue:    #99ccff;
  --c-mint:    #99e6cc;

  --c-bg:        #06031a;
  --c-panel:     rgba(8, 6, 22, 0.62);
  --c-panel-edge:rgba(255, 204, 153, 0.22);
  --c-text:      #f4ecd8;
  --c-text-2:    #c4b89e;
  --c-text-3:    #6e6a5a;

  --font-display: 'Orbitron', 'Helvetica Neue', sans-serif;
  --font-mono:    'Share Tech Mono', ui-monospace, 'Cascadia Code', monospace;
  --font-body:    'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  --step-0:  clamp(0.95rem, 0.91rem + 0.22vw, 1.08rem);
  --step-1:  clamp(1.13rem, 1.06rem + 0.34vw, 1.32rem);
  --step-2:  clamp(1.35rem, 1.24rem + 0.5vw, 1.6rem);
  --step-3:  clamp(1.6rem,  1.45rem + 0.7vw, 1.95rem);
  --step-5:  clamp(2.27rem, 1.96rem + 1.45vw, 3rem);
  --step-7:  clamp(3.2rem,  2.6rem + 3.0vw, 4.8rem);
}

html { background: var(--c-bg); color: var(--c-text); scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  background: transparent;
  color: var(--c-text);
  min-height: 100vh;
  overflow-x: hidden;
}

::selection { background: var(--c-orange); color: var(--c-bg); }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.2;
  text-transform: uppercase;
}

.wrapper {
  max-width: 80rem;
  margin: 0 auto;
  padding-left: clamp(1rem, 4vw, 4rem);
  padding-right: clamp(1rem, 4vw, 4rem);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--c-orange);
  color: var(--c-bg);
  padding: 0.5rem 1rem;
  z-index: 100;
  font-family: var(--font-mono);
}
.skip-link:focus { left: 0; }

/* ============================================================
   HERO INTRO  — pinned "zoom into a letter" reveal.
   ============================================================ */
.hero-intro {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  z-index: 2;
}

.hero-bg-svg,
.hero-mask-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* Splitting the backdrop and the text into TWO sibling SVGs is
     itself the perf win — the browser already gives each SVG its
     own raster pipeline, so the heavy nebula filters no longer
     re-rasterise on every text-scale frame. We deliberately do NOT
     add transform / will-change / backface-visibility here:
     promoting both SVGs to forced GPU layers caused a rendering
     glitch on scroll-reverse (a black band where the layer's
     compositor state fought GSAP's autoAlpha reversal). */
}
.hero-mask-svg {
  /* The whole SVG can fade out at the very end of the intro for a
     clean transition into the live space scene. */
}

/* Visible name + cutout text — make sure they're allowed to overflow
   the SVG when scaled enormously. (No will-change: transform here —
   forcing the scaling group onto its own compositor layer caused a
   reverse-scroll rendering glitch.) */
.hero-mask-svg #visible-text,
.hero-mask-svg #cutout-text {
  overflow: visible;
}

/* ============================================================
   HERO BACKDROP NEBULA + STARS
   True continuous LEFT → RIGHT flow. Each nebula layer holds
   two copies of its cloud set (one off-screen left, one on
   screen). The layer translates +1200 px per cycle in linear
   timing; at the end of the cycle the off-screen copy is
   exactly where the on-screen copy started, so the loop is
   visually seamless — the clouds appear to flow across the
   screen forever instead of bobbing back and forth.
   Background flows slower than foreground = parallax depth.
   Stars pulse independently via .hero-twinkle.
   ============================================================ */
.hero-neb {
  will-change: transform;
}

/* Background layer — slow continuous flow */
.hero-neb--bg {
  animation: hero-neb-flow 38s linear infinite;
}
/* Foreground layer — faster flow (parallax depth) */
.hero-neb--fg {
  animation: hero-neb-flow 22s linear infinite;
}
@keyframes hero-neb-flow {
  from { transform: translateX(0); }
  to   { transform: translateX(1200px); }
}

@keyframes hero-twinkle {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 1;    }
}
.hero-twinkle {
  animation: hero-twinkle 2.5s ease-in-out infinite;
  will-change: opacity;
}

@media (prefers-reduced-motion: reduce) {
  .hero-neb, .hero-twinkle { animation: none; }
}

.hero-hint {
  position: absolute;
  bottom: 8vh;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.3em;
  color: var(--c-text-2);
  text-transform: uppercase;
  animation: hintBlink 2s ease-in-out infinite;
  z-index: 3;
  pointer-events: none;
  transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}
/* Once the hero name has faded into the space scene (body gains
   .is-hero-done from the hero ScrollTrigger), retire the scroll
   hint so it doesn't linger over the parallax scene. Using
   visibility (with a delay matching the fade) keeps it from
   re-appearing as a screen-reader-readable phantom. */
body.is-hero-done .hero-hint {
  opacity: 0;
  visibility: hidden;
  animation: none;
  transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}

/* ============================================================
   STAR TWINKLE  — pure CSS animation (way cheaper than ~50 GSAP
   tweens running on every frame). nth-of-type variants stagger
   timing so stars don't twinkle in sync.
   ============================================================ */
@keyframes twinkle {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1;   }
}
.twinkle {
  animation: twinkle 2.4s ease-in-out infinite;
  will-change: opacity;
}
.twinkle:nth-of-type(2n)   { animation-duration: 1.8s; animation-delay: -0.3s; }
.twinkle:nth-of-type(3n)   { animation-duration: 2.7s; animation-delay: -0.7s; }
.twinkle:nth-of-type(5n)   { animation-duration: 2.1s; animation-delay: -1.2s; }
.twinkle:nth-of-type(7n)   { animation-duration: 3.0s; animation-delay: -0.5s; }
.twinkle:nth-of-type(11n)  { animation-duration: 2.3s; animation-delay: -1.6s; }

@media (prefers-reduced-motion: reduce) {
  .twinkle { animation: none; opacity: 0.85; }
}

/* Will-change hints on the heavily-animated foreground elements */
.parallax #ship,
.parallax #cube-ship,
.parallax #nebula-mid,
.parallax #planet,
.parallax #planet-cyan {
  will-change: transform;
}

/* Hint the compositor that the idle wrappers will be transformed */
[id$="-idle"] { will-change: transform; }

/* ============================================================
   FIXED PARALLAX SVG  +  CRT OVERLAY
   ============================================================ */
.parallax {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100vh;
  z-index: -2;
  pointer-events: none;
  background: var(--c-bg);
}

.crt-overlay {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    repeating-linear-gradient(
      to bottom,
      transparent 0,
      transparent 2px,
      rgba(255, 255, 255, 0.018) 2px,
      rgba(255, 255, 255, 0.018) 3px
    ),
    radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.55) 100%);
  mix-blend-mode: screen;
}

/* ============================================================
   SITE HEAD  (fixed over the scene)
   ============================================================ */
.site-head {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 5;
  padding: 1rem 0 0;
  pointer-events: none;
  /* Hidden during the hero-intro pin (the name-zoom sequence) so
     nothing competes with the title. Revealed via the .is-hero-done
     class set on <body> by ScrollTrigger when the pin releases — see
     setupHeroIntro() in js/scene.js. */
  transform: translateY(-110%);
  opacity: 0;
  transition:
    transform 0.55s cubic-bezier(0.25, 1, 0.5, 1),
    opacity   0.45s ease;
}
body.is-hero-done .site-head {
  transform: translateY(0);
  opacity: 1;
}
.site-head a, .site-head .nav, .site-head .hud { pointer-events: auto; }

.site-head__inner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 1.5rem;
}

.logo {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 900;
  letter-spacing: 0.12em;
  font-size: 1rem;
  color: var(--c-orange);
}
.logo__pill {
  width: 2rem;
  height: 1rem;
  background: var(--c-orange);
  border-radius: 0 999px 999px 0;
  margin-left: -1rem;
  display: inline-block;
}
.logo__sub {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--c-text-3);
  margin-left: 0.5rem;
  letter-spacing: 0.18em;
}

/* ============================================================
   BORG-NAME HOME BUTTON
   The home link is an inline SVG that pulls the same
   #borg-cube-tex pattern + #text-glow-borg filter the hero
   title uses, so the small "JAMES COOKE" mark in the header
   matches the big titlecard exactly.

   Height is constrained so the SVG slots into the existing
   header row without resizing it. overflow: visible lets the
   filter glow extend a few pixels past the SVG box (the glow
   filter uses big bounds so the bloom isn't clipped).
   ============================================================ */
.logo--borg {
  height: 54px;
  padding: 0;
}
.logo__borg-svg {
  height: 100%;
  width: auto;
  overflow: visible;
  display: block;
  filter: drop-shadow(0 0 6px rgba(51, 255, 102, 0.18));
  transition: transform 0.25s cubic-bezier(0.25, 1, 0.5, 1);
}
.logo--borg:hover .logo__borg-svg,
.logo--borg:focus-visible .logo__borg-svg {
  transform: scale(1.04);
}

/* ============================================================
   BORG CONDUIT PULSE ANIMATION
   The pulse paths inside #borg-cube-tex (see index.html) all
   carry the same path geometry as the static conduit traces,
   but are drawn as a (6-on / 102-off) dashed stroke. Animating
   the dash offset by exactly one cycle (-108) over 2.4 s makes
   a short bright "data packet" appear to flow continuously
   through each trace. Three sub-groups stagger their start
   times so the surface always has at least one packet in view.

   Because the pattern is referenced by both the hero title AND
   the small Borg-style home button in the header, BOTH places
   pulse for free — single source of motion, two viewers.
   ============================================================ */
@keyframes borg-pulse-flow {
  to { stroke-dashoffset: -108; }
}
.borg-pulse {
  stroke-dasharray: 6 102;
  stroke-dashoffset: 0;
  animation: borg-pulse-flow 2.4s linear infinite;
  /* The pulse stroke is bright, but we still want the underlying
     #5fff66 trace to be the dominant green at rest — slightly
     boost the pulse so a moving packet pops above the trace. */
  filter: drop-shadow(0 0 1.4px #aaffaa);
}
.borg-pulse--b { animation-delay: -0.8s; }
.borg-pulse--c { animation-delay: -1.6s; }

@media (prefers-reduced-motion: reduce) {
  /* Honour reduced-motion: hold the pulse layer still and let
     it sit at offset 0 (one tiny dash visible per trace, no
     flow). Keeps the visual richness without any motion. */
  .borg-pulse,
  .borg-pulse--b,
  .borg-pulse--c { animation: none; }
}

.nav__list {
  display: flex;
  justify-content: center;
  gap: 0.4rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.18em;
}
.nav__list a {
  text-decoration: none;
  display: inline-block;
  padding: 0.45rem 0.95rem;
  background: rgba(8, 6, 22, 0.55);
  border-left: 3px solid var(--c-purple);
  color: var(--c-text);
  border-radius: 0 999px 999px 0;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition: background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
.nav__list a:hover,
.nav__list a[aria-current="true"] {
  background: var(--c-orange);
  color: var(--c-bg);
  transform: translateX(2px);
}

.hud {
  display: grid;
  gap: 0.15rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-align: right;
  background: rgba(8, 6, 22, 0.55);
  padding: 0.5rem 0.75rem;
  border-radius: 6px;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.hud__row {
  display: inline-flex;
  gap: 0.6rem;
  justify-content: flex-end;
  align-items: center;
}
.hud__label { color: var(--c-text-3); }
.hud__val { color: var(--c-gold); min-width: 8ch; text-align: right; }
.hud__val--ok { color: var(--c-mint); }

/* ============================================================
   HEADER — RESPONSIVE LAYERS

   Three layers, tightest first:
   - <= 800px (tablet/phone): logo + HUD on row 1, nav scrolls
     horizontally on row 2 so even very long tab labels never clip.
   - <= 560px (phone): shrink the Borg home-button SVG, drop the
     PHASE/SYS rows of the HUD (keep the clock), and reduce the
     nav font so all three tabs sit on one scrollable row.
   - <= 380px (tiny phone): hide the HUD entirely and pad less.
   ============================================================ */
@media (max-width: 50rem) {
  .site-head { padding: 0.6rem 0 0; }
  .site-head__inner {
    grid-template-columns: auto auto;
    row-gap: 0.55rem;
    column-gap: 0.75rem;
  }
  /* Nav gets its own scroll context on tablets/phones; the
     wrapper provides the fade indicator on the right edge that
     hints at horizontally scrollable content. */
  .nav {
    grid-column: 1 / -1;
    order: 3;
    position: relative;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* Soft fade on the right edge so users see there's more
       scrollable content beyond the visible tabs. The mask fades
       to transparent in the last 24px of the visible nav. */
    -webkit-mask-image: linear-gradient(
      to right, #000 0, #000 calc(100% - 24px), transparent 100%);
            mask-image: linear-gradient(
      to right, #000 0, #000 calc(100% - 24px), transparent 100%);
  }
  .nav::-webkit-scrollbar { display: none; }
  .nav__list {
    justify-content: flex-start;
    flex-wrap: nowrap;
    padding: 0 1.25rem 0 0.25rem; /* trailing space so last tab clears the fade */
  }
  .nav__list li { flex: 0 0 auto; }
  .nav__list a { white-space: nowrap; }
  .hud { font-size: 0.65rem; padding: 0.4rem 0.6rem; }
  .hud__val { min-width: 6ch; }
}

/* Default: show the canonical themed labels, hide the short
   forms. On phones (<35rem) we flip these so the abbreviated
   labels take over. The full text remains in each link's
   aria-label so assistive tech still announces the proper name. */
.nav-short { display: none; }

@media (max-width: 35rem) {
  .logo--borg { height: 38px; }
  .nav__list { font-size: 0.7rem; gap: 0.3rem; }
  .nav__list a { padding: 0.4rem 0.7rem; letter-spacing: 0.14em; }
  .nav-full  { display: none; }
  .nav-short { display: inline; }
  .hud { font-size: 0.6rem; padding: 0.35rem 0.5rem; }
  /* Hide PHASE + SYS rows on phone so the header doesn't crowd
     the small viewport. Keep only the UTC clock (last row). */
  .hud__row:nth-child(1),
  .hud__row:nth-child(2) { display: none; }
}

@media (max-width: 24rem) {
  .logo--borg { height: 32px; }
  .hud { display: none; }
  .site-head__inner { grid-template-columns: 1fr; }
  .nav__list { justify-content: center; }
}

/* ============================================================
   HERO INTRO + SCROLL HINT — phone tweaks
   The hero mask SVG uses preserveAspectRatio="meet" so the
   JAMES COOKE name always *fits* the viewport, but at the
   default font-size of 180 the bounding box of the text is
   ~the full SVG viewBox width, so on phones the J and E end
   up hugging the screen edges with no breathing room.

   CSS overrides SVG presentation attributes, so reducing the
   #visible-text font-size at narrow widths shrinks the title
   and gives it comfortable side margins. The Borg pattern
   inside the letters scales with them, so the look is
   identical, just sized for the smaller screen.
   ============================================================ */
@media (max-width: 50rem) {
  #visible-text { font-size: 150px; }
}
@media (max-width: 35rem) {
  #visible-text { font-size: 140px; }
  .hero-hint { bottom: 5vh; font-size: 0.75rem; letter-spacing: 0.25em; }
}
@media (max-width: 24rem) {
  #visible-text { font-size: 130px; }
}

/* ============================================================
   MAIN — placeholder content panels overlaid on the parallax scene
   ============================================================
   Each section is intentionally tall (~110vh) so total scroll length
   is enough to drive the GSAP scene timeline. You'll restyle the
   actual `.placeholder__inner` boxes later.
   ============================================================ */
.main {
  position: relative;
  z-index: 1;
}

.placeholder {
  min-height: 110vh;          /* gives the scene timeline scroll distance */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(6rem, 12vh, 10rem) clamp(1rem, 4vw, 4rem);
}

.placeholder__inner {
  width: min(56rem, 100%);
  background: var(--c-panel);
  border: 1px solid var(--c-panel-edge);
  border-radius: 18px;
  padding: clamp(1.5rem, 4vw, 3rem);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  display: grid;
  gap: 1rem;
}

.placeholder__chip {
  display: inline-block;
  width: fit-content;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  padding: 0.3rem 0.85rem;
  background: var(--c-orange);
  color: var(--c-bg);
  border-radius: 999px;
}

.placeholder__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.2em;
  color: var(--c-gold);
  text-transform: uppercase;
}

.placeholder__title {
  font-size: var(--step-5);
  color: var(--c-orange);
}

.placeholder--hero .placeholder__title {
  font-size: var(--step-7);
  text-transform: none;
  letter-spacing: 0.02em;
  color: var(--c-text);
  text-shadow: 0 4px 30px rgba(0, 0, 0, 0.7);
}
/* The final scene shows the bright vortex — give the contact panel
   a slightly stronger backdrop so its content stays legible. */
.placeholder--end .placeholder__inner {
  background: rgba(8, 6, 22, 0.78);
}

/* ============================================================
   QUOTE INTERLUDE — Roddenberry passage between the hero
   intro and the first job listing. Each .quote-word is faded
   from ~6% opacity → 100% by ScrollTrigger (see scene.js
   setupQuoteSection). The block sits above the parallax with
   no background panel so the space scene reads through it.
   ============================================================ */
.quote-section {
  position: relative;
  z-index: 2;
  min-height: 80vh;                     /* tight — about one screen of scroll */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem clamp(1rem, 4vw, 3rem);
  pointer-events: none;
}
.quote-section__inner {
  max-width: 58rem;
  text-align: center;
  /* Soft elliptical wash behind the text so it stays legible
     against the brightest parts of the parallax without looking
     like a hard panel. */
  padding: 3rem clamp(1.2rem, 3vw, 2.4rem);
  background:
    radial-gradient(ellipse at center, rgba(8, 6, 22, 0.55) 0%, transparent 70%);
}
.quote-section__text {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.15rem, 2.1vw, 1.85rem);
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: var(--c-text);
  margin: 0 auto;
  text-shadow:
    0 0 28px rgba(255, 153, 102, 0.16),
    0 2px 6px rgba(0, 0, 0, 0.85);
}
.quote-word {
  display: inline-block;
  margin-right: 0.32em;
  /* Fully invisible until the GSAP timeline scrubs each word up
     to opacity 1 — no faint "shadow" should be visible before the
     reveal animation fires. */
  opacity: 0;
  transform: translateY(2px);
  will-change: opacity, transform;
}
.quote-section__attr {
  margin: 2rem 0 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--c-gold);
  text-shadow: 0 0 14px rgba(255, 204, 102, 0.45);
  opacity: 0;                           /* fades in last via scrub */
}
.quote-section__rule {
  display: inline-block;
  width: clamp(2.2rem, 5vw, 4rem);
  height: 1px;
  background: linear-gradient(to right,
    transparent 0%,
    var(--c-gold) 50%,
    transparent 100%);
}

@media (prefers-reduced-motion: reduce) {
  /* No scroll-driven reveal — show the passage at full opacity
     for users who opt out of motion. */
  .quote-word { opacity: 1; transform: none; }
  .quote-section__attr { opacity: 1; }
}

.placeholder__lede {
  font-size: var(--step-1);
  color: var(--c-text-2);
  max-width: 55ch;
}

.placeholder__hint {
  font-family: var(--font-mono);
  letter-spacing: 0.3em;
  color: var(--c-gold);
  margin-top: 1.5rem;
  text-align: center;
  animation: hintBlink 2s ease-in-out infinite;
}
@keyframes hintBlink {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

.placeholder__grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  margin-top: 1rem;
}

.placeholder__box {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.18em;
  color: var(--c-text);
  background: rgba(0, 0, 0, 0.45);
  border: 1px dashed rgba(255, 204, 153, 0.3);
  border-radius: 10px;
  padding: 2rem 1rem;
  text-align: center;
  min-height: 140px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.site-foot {
  text-align: center;
  padding: 2.4rem 1rem 3rem;
  color: var(--c-text-3);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  background: rgba(6, 3, 26, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.site-foot p { margin: 0; }
.site-foot__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--c-orange);
  text-shadow: 0 0 12px rgba(255, 153, 102, 0.35);
}
.site-foot__tag {
  margin-top: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.18em;
  color: var(--c-text-2);
}
.site-foot__credit {
  margin-top: 1.1rem;
  letter-spacing: 0.18em;
  color: var(--c-text-3);
}

/* ============================================================
   FOOTER SOCIAL ROW
   Pill-style external link with the LinkedIn glyph + label.
   Resting state uses LinkedIn's signature blue inside a dark
   themed capsule (matches LCARS aesthetic). On hover/focus the
   capsule fills to the brand blue and the icon flips to white,
   with a soft glow + slight lift — same interaction language
   as the nav pills above.
   ============================================================ */
.site-foot__social {
  list-style: none;
  margin: 1.4rem 0 0;
  padding: 0;
  display: flex;
  justify-content: center;
  gap: 0.8rem;
}
.social-btn {
  --brand: #0a66c2;                    /* LinkedIn blue */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: rgba(8, 6, 22, 0.7);
  color: var(--brand);
  text-decoration: none;
  border: 1px solid rgba(10, 102, 194, 0.55);
  border-radius: 50%;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition:
    background 0.2s ease,
    color      0.2s ease,
    border     0.2s ease,
    transform  0.2s ease,
    box-shadow 0.2s ease;
}
.social-btn:hover,
.social-btn:focus-visible {
  background: var(--brand);
  color: #ffffff;
  border-color: var(--brand);
  transform: translateY(-2px);
  box-shadow:
    0 0 0 1px rgba(10, 102, 194, 0.45),
    0 6px 18px rgba(10, 102, 194, 0.5);
  outline: none;
}
.social-btn__icon {
  width: 22px;
  height: 22px;
  fill: currentColor;
  flex-shrink: 0;
}

/* ============================================================
   HEXAGON BOXES — portfolio cards as hex portals.
   Sizing math is the EXACT spec from the source snippet
   (220 × 254 px). Theme styling layered on top: sci-fi HUD
   gradient rim, scanline texture, Orbitron + mono caption.
   The clip-path / dimensions are never altered.
   ============================================================ */
:root {
  --hex-width:  340px;
  --hex-gap:    24px;
  --sqrt3:      1.73205;
  --hex-height: calc(var(--hex-width) * 2 / var(--sqrt3));
  --hex-margin-x: calc(var(--hex-gap) / 2);
  --hex-margin-y: calc(
    (var(--sqrt3) / 4 * var(--hex-gap)) - (0.125 * var(--hex-height))
  );
}
@media (max-width: 760px) {
  :root { --hex-width: 260px; --hex-gap: 16px; }
}
@media (max-width: 520px) {
  :root { --hex-width: 215px; --hex-gap: 10px; }
}
@media (max-width: 380px) {
  :root { --hex-width: 185px; --hex-gap: 8px; }
}

.hex {
  width:  var(--hex-width);
  height: var(--hex-height);
  margin: var(--hex-margin-y) var(--hex-margin-x);
  position: relative;
  cursor: pointer;
  filter:
    drop-shadow(0 0 14px rgba(255, 153, 102, 0.28))
    drop-shadow(0 6px 14px rgba(0, 0, 0, 0.55));
  transform: translateY(0);
  transition:
    transform 0.5s cubic-bezier(0.25, 1, 0.5, 1),
    filter    0.5s cubic-bezier(0.25, 1, 0.5, 1);
}
.hex:focus-visible { outline: none; }

/* Outer hex = the gradient rim (theme accent ring) */
.hex::before {
  content: "";
  position: absolute;
  inset: 0;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  background:
    linear-gradient(135deg,
      var(--c-orange) 0%,
      var(--c-gold)   45%,
      var(--c-blue)   100%);
  z-index: 0;
  transition: opacity 0.5s ease, filter 0.5s ease;
}

/* Inner hex = the image clip, inset 2px to reveal the rim.
   Background uses an rgba bg so the parallax shows through. */
.hex-shape {
  position: absolute;
  inset: 2px;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  overflow: hidden;
  background-color: rgba(6, 3, 26, 0.28);
  z-index: 1;
}
/* Vignette for depth (fades on hover) */
.hex-shape::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 50% 50%, transparent 38%, rgba(6, 3, 26, 0.85) 120%);
  pointer-events: none;
  z-index: 2;
  transition: opacity 0.5s ease;
}
/* Subtle CRT scanlines so the hexes belong with the HUD overlay */
.hex-shape::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(
      to bottom,
      transparent 0,
      transparent 2px,
      rgba(0, 0, 0, 0.18) 3px
    );
  pointer-events: none;
  z-index: 3;
  mix-blend-mode: overlay;
  opacity: 0.55;
}

.hex img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  /* Warm sepia/orange tint + semi-transparency so the parallax shows through */
  opacity: 0.62;
  filter: grayscale(55%) sepia(35%) hue-rotate(-12deg) brightness(0.7) contrast(1.1);
  transform: scale(1.02);
  backface-visibility: hidden;
  will-change: transform;
  transition:
    transform 0.6s cubic-bezier(0.25, 1, 0.5, 1),
    filter    0.6s ease,
    opacity   0.6s ease;
}

/* ============================================================
   LOGO-DISPLAY HEX VARIANT
   For hexes that hold an ORGANISATION LOGO. Two key things:
     1. Per-hex brand colour via the --logo-bg custom property,
        set inline (style="--logo-bg: #582C83") so each hex's
        background matches the org's identity.
     2. ~30% padding around the logo so the source 256 px image
        renders at ≤180 px on screen — that's a DOWNSCALE
        (browser supersamples = crisp) instead of an UPSCALE
        (which is what was making it blurry).
   Photo filters / vignettes / scanlines are stripped so the
   brand mark reads clean.
   ============================================================ */
.hex--logo .hex-shape {
  background-color: var(--logo-bg, #f3eedf);
}
.hex--logo .hex-shape::after,
.hex--logo .hex-shape::before {
  display: none;                       /* no vignette / scanlines over a logo */
}
.hex--logo img {
  object-fit: contain;                 /* whole logo visible, no crop */
  padding: 8%;                         /* minimal breathing room so the mark fills the hex */
  opacity: 1;
  filter: none;
  transform: none;
}
.hex--logo:hover img,
.hex--logo:focus-visible img {
  opacity: 1;
  filter: none;
  transform: scale(1.05);
}

/* ============================================================
   PORTRAIT HEX VARIANT (personal brand)
   The contact hex uses a real headshot photo as the entire hex
   background. On hover the photo fades out to reveal a black
   panel and the standard .hex-caption ("James Cooke" + sub)
   becomes the focal point — same caption treatment used on
   every other hex when hovered, just over a clean black panel.
   ============================================================ */
.hex--portrait .hex-shape {
  background-color: #000;              /* shows through once the photo fades */
}
.hex--portrait .hex-shape::after,
.hex--portrait .hex-shape::before {
  display: none;                       /* no vignette / scanlines over the headshot */
}
.hex--portrait img {
  object-fit: cover;                   /* fill the hex, crop as needed */
  padding: 0;
  opacity: 1;                          /* always fully visible by default */
  filter: none;
  transform: none;
  transition:
    opacity   0.45s ease,
    transform 0.6s cubic-bezier(0.25, 1, 0.5, 1);
}
/* Compound selector .hex.hex--portrait outranks the global
   .hex:hover img rule that would otherwise force opacity:0.9 on
   the photo, so the fade-out on hover actually fires. */
.hex.hex--portrait:hover img,
.hex.hex--portrait:focus-within img {
  opacity: 0;                          /* fade out so the black panel shows through */
  transform: scale(1.04);
  filter: none;
}
/* On hover the caption sits dead-centre instead of bottom-aligned
   so the name reads as the new focal point — and we kill the
   gradient backdrop because the panel itself is already solid black. */
.hex.hex--portrait:hover .hex-caption,
.hex.hex--portrait:focus-within .hex-caption {
  background: transparent;
  justify-content: center;
  padding-bottom: 0;
}

.hex:hover,
.hex:focus-within {
  z-index: 10;
  transform: translateY(-6px);
  filter:
    drop-shadow(0 0 22px rgba(255, 204, 102, 0.55))
    drop-shadow(0 22px 30px rgba(0, 0, 0, 0.75));
}
.hex:hover .hex-shape::after,
.hex:focus-within .hex-shape::after { opacity: 0; }
.hex:hover img,
.hex:focus-visible img {
  transform: scale(1.15);
  opacity: 0.9;
  filter: grayscale(0%) sepia(15%) hue-rotate(-8deg) brightness(1.05) contrast(1.05);
}

/* Caption overlay (slides in on hover) — sci-fi HUD typography */
.hex-caption {
  position: absolute;
  inset: 0;
  z-index: 4;
  background: linear-gradient(to top,
    rgba(6, 3, 26, 0.95)  0%,
    rgba(6, 3, 26, 0.55) 45%,
    rgba(6, 3, 26, 0)    100%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  padding: 0 1.1rem 22%;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.hex-caption h3 {
  font-family: var(--font-display);
  color: var(--c-orange);
  font-size: 1.4rem;
  font-weight: 700;
  margin: 0 0 0.45rem 0;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-shadow: 0 0 10px rgba(255, 153, 102, 0.55), 0 2px 4px rgba(0, 0, 0, 0.85);
  transform: translateY(14px);
  transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
}
.hex-caption p {
  font-family: var(--font-mono);
  color: var(--c-gold);
  font-size: 0.85rem;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.28em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  transform: translateY(14px);
  transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
}
.hex:hover .hex-caption,
.hex:focus-visible .hex-caption { opacity: 1; }
.hex:hover .hex-caption h3,
.hex:focus-visible .hex-caption h3 { transform: translateY(0); }
.hex:hover .hex-caption p,
.hex:focus-visible .hex-caption p {
  transform: translateY(0);
  transition-delay: 0.05s;
}

/* Pair layout — diagonal stagger along the EDGES (not the points).
   First hex's bottom-right slanted edge (4 o'clock) lines up with the
   second hex's upper-left slanted edge (11 o'clock).
   Math:  for a pointy-top hex tessellation the down-right diagonal
          neighbour sits at (0.5 * W, 0.75 * H) — that's the offset
          where their slanted edges become collinear (true edge-share).
   A small perpendicular gap (~6/10 px) keeps both rims visible. */
.hex-pair {
  position: relative;
  width:  calc(var(--hex-width)  * 1.5  + 6px);
  height: calc(var(--hex-height) * 1.75 + 10px);
  margin: 0 auto;
}
.hex-pair .hex {
  position: absolute;
  margin: 0;                 /* override the tessellation margin */
}
.hex-pair .hex:nth-child(1) {
  top: 0;
  left: 0;
}
.hex-pair .hex:nth-child(2) {
  top:  calc(var(--hex-height) * 0.75 + 10px);
  left: calc(var(--hex-width)  * 0.5  + 6px);
}

@media (prefers-reduced-motion: reduce) {
  .hex, .hex img, .hex-caption,
  .hex-caption h3, .hex-caption p { transition: none; }
}

/* The first hex of each pair is a decorative static portal.
   Strip the cursor + keyboard affordances so it's not mistaken
   for an actionable button. */
.hex.hex--static {
  cursor: default;
}
.hex.hex--static:focus,
.hex.hex--static:focus-visible {
  outline: none;
}

.hex.hex--clickable {
  cursor: pointer;
}
.hex.hex--clickable:focus-visible {
  outline: 2px solid var(--c-orange);
  outline-offset: 4px;
}

/* ============================================================
   CLICKABLE-HEX VARIANT (the lower "Duties / Channels Open" hex)
   ------------------------------------------------------------
   Goal: see-through by default with the action word always
   visible, then on hover/focus the inside fills with black and
   the word grows slightly — gives a "lit-up button" feel.

   Implementation notes:
     • Inner .hex-shape goes transparent so the parallax shows
       through the rim. Vignette + scanline pseudo-elements are
       suppressed so nothing dims the see-through panel.
     • .hex-caption stays at full opacity and the h3 sits at a
       baseline size from page load (it's no longer a hover-only
       overlay for these hexes). The underlying gradient backdrop
       is killed; the dark fill comes from .hex-shape on hover.
     • On hover/focus the .hex-shape's bg fades to near-opaque
       black and the h3 scales up + glows. The hint paragraph
       ("Tap to Brief") is hidden by default and fades in.
   ============================================================ */
/* Kill the outer gradient FILL on the rim pseudo (.hex::before) —
   without this the gradient shows straight through any transparent
   inset overlay. The rim itself is recreated as an inline SVG hex
   stroke painted into the .hex-shape, so the hex still reads as a
   defined shape with a gradient outline. */
.hex.hex--clickable::before {
  background: transparent;
}
.hex--clickable .hex-shape {
  background-color: transparent;
  /* Inline-SVG hex outline = a 2-px gradient-coloured rim that
     follows the polygon. Fill is none, so the inside of the SVG
     contributes zero pixels and the parallax behind shows through. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'><defs><linearGradient id='g' x1='0' y1='0' x2='1' y2='1'><stop offset='0%25' stop-color='%23ffaa66'/><stop offset='45%25' stop-color='%23ffcc88'/><stop offset='100%25' stop-color='%2378b9ff'/></linearGradient></defs><polygon points='50,1 99,25 99,75 50,99 1,75 1,25' fill='none' stroke='url(%23g)' stroke-width='2.4' stroke-linejoin='round'/></svg>");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  transition: background-color 0.4s ease;
}
.hex--clickable .hex-shape::after,
.hex--clickable .hex-shape::before {
  display: none;                       /* no vignette / scanlines on a transparent panel */
}
.hex--clickable .hex-caption {
  opacity: 1;                          /* always visible (override default hover-only) */
  background: transparent;             /* dark fill now lives on .hex-shape, not here */
  justify-content: center;             /* center the action word vertically */
  padding: 0 1rem;
}
.hex--clickable .hex-caption h3 {
  transform: none;                     /* no slide-in — present from page load */
  font-size: 2.1rem;                   /* baseline — large, readable from a distance */
  margin: 0;
  letter-spacing: 0.12em;
  transition:
    font-size   0.35s cubic-bezier(0.25, 1, 0.5, 1),
    color       0.35s ease,
    text-shadow 0.35s ease;
}
.hex--clickable .hex-caption p {
  opacity: 0;                          /* hint fades in only on hover */
  margin-top: 0.55rem;
  transform: translateY(8px);
  transition:
    opacity   0.35s ease,
    transform 0.4s cubic-bezier(0.25, 1, 0.5, 1) 0.05s;
}
.hex--clickable:hover .hex-shape,
.hex--clickable:focus-visible .hex-shape {
  background-color: rgba(0, 0, 0, 0.92);
}
.hex--clickable:hover .hex-caption h3,
.hex--clickable:focus-visible .hex-caption h3 {
  font-size: 2.6rem;                   /* punches up on hover — clear "lit-up" cue */
  text-shadow:
    0 0 18px rgba(255, 153, 102, 0.85),
    0 2px 4px rgba(0, 0, 0, 0.9);
}
.hex--clickable:hover .hex-caption p,
.hex--clickable:focus-visible .hex-caption p {
  opacity: 1;
  transform: translateY(0);
}

/* ============================================================
   CONSOLE MODAL — sci-fi terminal pop-up triggered by the
   second (clickable) hex of every section. Themed with the
   same Orbitron / Share Tech Mono / orange-on-dark palette as
   the HUD. Includes corner brackets, a status bar, body lines
   prefixed with a gold "> " prompt, and a "DISCONNECT" button.
   ============================================================ */
.console-modal[hidden] { display: none; }
.console-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}
.console-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(4, 2, 16, 0.78);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.console-modal__panel {
  position: relative;
  width: min(40rem, 100%);
  max-height: calc(100vh - 3rem);
  display: flex;
  flex-direction: column;
  background:
    linear-gradient(180deg, rgba(18, 10, 40, 0.95), rgba(8, 4, 22, 0.95));
  border: 1px solid var(--c-orange);
  box-shadow:
    0 0 0 1px rgba(255, 153, 102, 0.2),
    0 0 32px rgba(255, 153, 102, 0.35),
    0 24px 60px rgba(0, 0, 0, 0.7),
    inset 0 0 80px rgba(0, 0, 0, 0.55);
  font-family: var(--font-mono);
  color: var(--c-text);
}
/* Corner brackets — small L-shapes in each corner */
.console-modal__bracket {
  position: absolute;
  width: 18px;
  height: 18px;
  border: 2px solid var(--c-gold);
  pointer-events: none;
  filter: drop-shadow(0 0 4px rgba(255, 204, 102, 0.55));
}
.console-modal__bracket--tl { top: -4px;    left:  -4px; border-right: none;  border-bottom: none; }
.console-modal__bracket--tr { top: -4px;    right: -4px; border-left:  none;  border-bottom: none; }
.console-modal__bracket--bl { bottom: -4px; left:  -4px; border-right: none;  border-top:    none; }
.console-modal__bracket--br { bottom: -4px; right: -4px; border-left:  none;  border-top:    none; }

.console-modal__header {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.7rem 1.1rem;
  border-bottom: 1px solid rgba(255, 153, 102, 0.3);
  background: linear-gradient(to right,
    rgba(255, 153, 102, 0.18) 0%,
    rgba(255, 153, 102, 0.0) 70%);
}
.console-modal__chip {
  font-family: var(--font-mono);
  color: var(--c-gold);
  font-size: 0.7rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  white-space: nowrap;
}
.console-modal__title {
  flex: 1;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1rem, 2.6vw, 1.2rem);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-orange);
  text-shadow: 0 0 10px rgba(255, 153, 102, 0.55);
  text-align: right;
}
.console-modal__close {
  background: transparent;
  color: var(--c-orange);
  border: 1px solid rgba(255, 153, 102, 0.5);
  width: 28px;
  height: 28px;
  font-family: var(--font-mono);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.18s, color 0.18s, box-shadow 0.18s;
}
.console-modal__close:hover,
.console-modal__close:focus-visible {
  background: var(--c-orange);
  color: var(--c-bg);
  box-shadow: 0 0 14px rgba(255, 153, 102, 0.7);
  outline: none;
}

/* Status bar — fake "comms" indicators under the header */
.console-modal__statusbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 1.1rem;
  font-size: 0.65rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--c-text-2);
  border-bottom: 1px solid rgba(255, 153, 102, 0.18);
  background: rgba(255, 153, 102, 0.04);
}
.console-modal__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--c-mint);
  box-shadow: 0 0 8px rgba(153, 230, 204, 0.85);
  animation: console-dot-blink 1.6s ease-in-out infinite;
}
.console-modal__bar {
  flex: 1;
  height: 1px;
  background: linear-gradient(to right, rgba(255, 153, 102, 0.4), transparent);
}
.console-modal__sig {
  color: var(--c-gold);
}
@keyframes console-dot-blink {
  0%, 100% { opacity: 0.45; transform: scale(0.85); }
  50%      { opacity: 1;    transform: scale(1);    }
}

.console-modal__body {
  padding: 1.4rem 1.25rem 1.1rem;
  overflow-y: auto;
  font-size: 0.92rem;
  line-height: 1.65;
  color: var(--c-text);
  background:
    repeating-linear-gradient(
      to bottom,
      transparent 0,
      transparent 2px,
      rgba(255, 153, 102, 0.05) 3px
    );
}
.console-modal__body p {
  margin: 0 0 0.45rem;
  position: relative;
  padding-left: 1.1rem;
}
.console-modal__body p::before {
  content: "›";
  position: absolute;
  left: 0;
  color: var(--c-gold);
  font-weight: 700;
}
.console-modal__body p:last-child { margin-bottom: 0; }

/* Caret blink on the LAST line so it feels like a live terminal */
.console-modal__body p:last-child::after {
  content: "";
  display: inline-block;
  width: 8px;
  height: 1em;
  margin-left: 4px;
  vertical-align: text-bottom;
  background: var(--c-orange);
  animation: console-caret-blink 1s step-end infinite;
}
@keyframes console-caret-blink {
  0%, 50%   { opacity: 1; }
  51%, 100% { opacity: 0; }
}

.console-modal__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1.1rem;
  border-top: 1px solid rgba(255, 153, 102, 0.3);
  background: linear-gradient(to right,
    rgba(255, 153, 102, 0.0),
    rgba(255, 153, 102, 0.12) 100%);
}
.console-modal__hint {
  font-size: 0.7rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--c-text-3);
}
.console-modal__disconnect {
  background: transparent;
  color: var(--c-orange);
  border: 1px solid var(--c-orange);
  padding: 0.45rem 1.4rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  cursor: pointer;
  transition: all 0.18s;
}
.console-modal__disconnect:hover,
.console-modal__disconnect:focus-visible {
  background: var(--c-orange);
  color: var(--c-bg);
  box-shadow: 0 0 18px rgba(255, 153, 102, 0.65);
  outline: none;
}


@media (prefers-reduced-motion: reduce) {
  .console-modal__dot,
  .console-modal__body p:last-child::after { animation: none; }
}

/* ============================================================
   CONSOLE MODAL — phone tweaks
   Tighten the wrapper padding so the panel can use more of the
   viewport, shrink the chip/title row so the long titles wrap
   gracefully, and let the body take up the full vertical space
   minus the header+footer so long bios scroll inside.
   ============================================================ */
@media (max-width: 35rem) {
  .console-modal { padding: 0.7rem; }
  .console-modal__panel {
    width: 100%;
    max-height: calc(100vh - 1.4rem);
    max-height: calc(100dvh - 1.4rem);
  }
  .console-modal__header {
    flex-wrap: wrap;
    padding: 0.55rem 0.8rem;
    gap: 0.5rem;
  }
  .console-modal__chip {
    flex: 1 1 100%;
    font-size: 0.62rem;
    letter-spacing: 0.18em;
    white-space: normal;
  }
  .console-modal__title {
    flex: 1;
    font-size: 0.95rem;
    text-align: left;
  }
  .console-modal__statusbar { padding: 0.35rem 0.8rem; font-size: 0.62rem; }
  .console-modal__body {
    padding: 1rem 0.85rem 0.9rem;
    font-size: 0.85rem;
    line-height: 1.55;
  }
  .console-modal__footer { padding: 0.55rem 0.8rem; }
  .console-modal__hint { font-size: 0.6rem; letter-spacing: 0.18em; }
  .console-modal__disconnect {
    padding: 0.4rem 1rem;
    font-size: 0.7rem;
    letter-spacing: 0.18em;
  }
}

/* ============================================================
   PLACEHOLDER SECTIONS + FOOTER — phone tweaks
   The placeholder padding uses clamp() so it already shrinks
   gracefully on small screens, but the footer LinkedIn pill
   row + tagline benefit from a touch less vertical breathing
   so the full footer fits without an extra screen of scroll.
   ============================================================ */
@media (max-width: 35rem) {
  .placeholder { min-height: 100vh; padding-top: clamp(4rem, 9vh, 6rem); }
  .site-foot__name { font-size: 1rem; }
  .site-foot__tag {
    font-size: 0.7rem;
    letter-spacing: 0.14em;
    line-height: 1.5;
  }
  .site-foot__social { margin-top: 1rem; }
}

/* ============================================================
   PRINT
   ============================================================ */
@media print {
  .parallax, .crt-overlay { display: none; }
  body { background: white; color: black; }
  .placeholder__inner { background: white; box-shadow: none; }
}
