/* ============================================
   felt.md — Deep Space Terminal
   Metroid 2D × Sci-Fi UI × Brutalist Data

   Rules only. Every colour, size, space and duration comes from
   tokens.css, which every page loads first.
   ============================================ */

/* --- Reset & Base --- */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  background: var(--bg-deep);
}

body {
  font-family: var(--font-body);
  color: var(--text);
  background: var(--bg-deep);
  line-height: 1.7;
  font-size: 16px;
  font-weight: 400;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* --- Layer 1: Nebula (radial color fog) --- */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  background:
    radial-gradient(ellipse at 15% 30%, rgb(var(--accent-rgb) / 4%) 0%, transparent 55%),
    radial-gradient(ellipse at 85% 15%, rgb(var(--orange-rgb) / 3%) 0%, transparent 45%),
    radial-gradient(ellipse at 50% 90%, rgb(var(--brinstar-rgb) / 2%) 0%, transparent 50%),
    radial-gradient(ellipse at 70% 60%, rgb(var(--teal-rgb) / 2%) 0%, transparent 40%),
    radial-gradient(ellipse at 30% 70%, rgb(var(--accent-rgb) / 1.5%) 0%, transparent 35%);
  pointer-events: none;
  z-index: 1;
}

/* --- Layer 2: Starfield (CSS box-shadows) --- */
.starfield {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

.starfield::before,
.starfield::after {
  content: "";
  position: absolute;
  width: 2px;
  height: 2px;
  border-radius: var(--radius-round);
  background: transparent;
}

.starfield::before {
  box-shadow:
    120px 80px 0 0 rgb(255 255 255 / 25%),
    340px 200px 0 0 rgb(var(--accent-rgb) / 30%),
    580px 120px 0 0 rgb(255 255 255 / 15%),
    720px 340px 0 0 rgb(255 255 255 / 20%),
    950px 60px 0 0 rgb(var(--accent-rgb) / 20%),
    1100px 250px 0 0 rgb(255 255 255 / 12%),
    1300px 150px 0 0 rgb(255 255 255 / 25%),
    1500px 300px 0 0 rgb(var(--accent-rgb) / 15%),
    1700px 100px 0 0 rgb(255 255 255 / 18%),
    1850px 220px 0 0 rgb(var(--accent-rgb) / 22%),
    200px 450px 0 0 rgb(255 255 255 / 12%),
    450px 520px 0 0 rgb(255 255 255 / 20%),
    680px 600px 0 0 rgb(var(--accent-rgb) / 15%),
    900px 480px 0 0 rgb(255 255 255 / 10%),
    1150px 550px 0 0 rgb(255 255 255 / 22%),
    1400px 620px 0 0 rgb(var(--accent-rgb) / 18%),
    1650px 500px 0 0 rgb(255 255 255 / 15%),
    80px 700px 0 0 rgb(var(--accent-rgb) / 12%),
    350px 780px 0 0 rgb(255 255 255 / 18%),
    600px 850px 0 0 rgb(255 255 255 / 10%),
    850px 750px 0 0 rgb(var(--accent-rgb) / 20%),
    1050px 820px 0 0 rgb(255 255 255 / 14%),
    1280px 700px 0 0 rgb(255 255 255 / 22%),
    1550px 780px 0 0 rgb(var(--accent-rgb) / 12%),
    1800px 850px 0 0 rgb(255 255 255 / 16%);
  animation: star-drift 120s linear infinite;
}

.starfield::after {
  box-shadow:
    60px 150px 0 0.5px rgb(var(--accent-rgb) / 20%),
    280px 380px 0 0 rgb(255 255 255 / 18%),
    500px 50px 0 0.5px rgb(255 255 255 / 12%),
    800px 280px 0 0 rgb(var(--accent-rgb) / 25%),
    1020px 180px 0 0.5px rgb(255 255 255 / 15%),
    1250px 400px 0 0 rgb(var(--accent-rgb) / 18%),
    1450px 80px 0 0 rgb(255 255 255 / 20%),
    1750px 420px 0 0.5px rgb(var(--accent-rgb) / 15%),
    160px 920px 0 0 rgb(255 255 255 / 14%),
    420px 980px 0 0.5px rgb(var(--accent-rgb) / 18%),
    750px 950px 0 0 rgb(255 255 255 / 10%),
    1100px 1000px 0 0 rgb(var(--accent-rgb) / 12%),
    1350px 930px 0 0.5px rgb(255 255 255 / 20%),
    1600px 970px 0 0 rgb(255 255 255 / 15%);
  animation: star-drift 180s linear infinite reverse;
}

@keyframes star-drift {
  from { transform: translateY(0); }
  to { transform: translateY(-40px); }
}

/* --- Layer 3: Station SVG geometry --- */
.station-svg {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

.station-svg svg {
  width: 100%;
  height: 100%;
}

/* --- Layer 5: Raster texture (JPEG ambient detail) --- */
.raster-texture {
  position: fixed;
  inset: 0;
  background: url('assets/Gemini_Generated_Image_413ivf413ivf413i.jpeg') center / cover no-repeat;
  opacity: 0.06;
  pointer-events: none;
  z-index: 1;
  mix-blend-mode: screen;
}

/* --- Layer 6: Scanlines (topmost) --- */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 3px,
    rgb(0 0 0 / 2.5%) 3px,
    rgb(0 0 0 / 2.5%) 4px
  );
  pointer-events: none;
  z-index: 9999;
}

/* --- Layer 4: Grid Overlay --- */
.grid-overlay {
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(rgb(var(--accent-rgb) / 1.5%) 1px, transparent 1px),
    linear-gradient(90deg, rgb(var(--accent-rgb) / 1.5%) 1px, transparent 1px);
  background-size: 80px 80px;
  pointer-events: none;
  z-index: 2;
  mask-image: radial-gradient(ellipse at 50% 50%, black 20%, transparent 70%);
}

/* --- Selection --- */
::selection {
  background: var(--accent);
  color: var(--bg-deep);
}

a {
  color: var(--accent);
  text-decoration: none;
  transition: color var(--dur-fast);
}

a:hover {
  color: var(--orange);
}

/* ============================================
   Optical correction
   A line box is taller than the ink inside it. At line-height 1.7 a text
   block carries about 0.35em of half-leading above its first line and the
   same below its last, and Inter adds a further 0.24em of ascender above
   the capitals. A card padded 26px on four sides therefore shows 26px at
   the sides and about 36px above the text: measured symmetry, perceived
   top-heaviness. That is the gap a designer closes by hand and cannot
   explain, and every hand-tuned value is one more number nobody can check.

   text-box does the correction from the font's own metrics instead.
   trim-both drops the leading from the first and last line boxes, cap puts
   the top edge on the capitals, alphabetic puts the bottom edge on the
   baseline. What is left is the ink, so a gap between two of these boxes is
   the gap the reader sees, and every token below delivers the distance it
   names without a correction term.

   Measured on this page: a five-line paragraph goes from 129px to 114px,
   which is the 7.5px of dead air at each end and nothing else. Line spacing
   inside the block is untouched.

   Excluded on purpose: .channel-copy small, which clips with overflow
   hidden and would lose its descenders to the trim. A browser without
   text-box support ignores this rule and renders the old, slightly looser
   spacing — the hierarchy still reads, every gap is just carrying its
   leading again.
   ============================================ */

h1,
h2,
h3,
p,
cite,
footer,
.hero-name,
.section-label,
.section-heading,
.scan-panel-header,
.scan-panel .meta,
.stat,
.log-date,
.build-log a,
.channel-copy strong,
.contact-link,
.transmission-id,
.transmission-title,
.transmission-body,
.transmission-date,
.locked-title,
.locked-subtitle,
.locked-rule-text,
.map-header,
.planet-name,
.planet-tagline {
  text-box: trim-both cap alphabetic;
}

/* ============================================
   Vertical rhythm
   A gap belongs to the dominant element of the pair, and it is declared
   once. Below a heading, never above the body it introduces. Below a block,
   never above the label, the stat row or the source line that trails it.

   Declaring both sides does not add them: adjacent margins collapse to
   max(a, b), so the rendered distance is a number nobody wrote and nobody
   can find. `:has(+ x)` lets the block above name the gap whatever that
   block turns out to be, so one rule replaces one rule per predecessor.

   The relationship, and therefore the size, comes from what follows:
   a stat row belongs to the prose it measures, a source line closes the
   whole panel.
   ============================================ */

.scan-panel > :has(+ .stats-row),
.work-row > :has(+ .stats-row) {
  margin-bottom: var(--gap-close);
}

.scan-panel > :has(+ .adopters-label) {
  margin-bottom: var(--gap-group);
}

.scan-panel > :has(+ .endorsement),
.scan-panel > :has(+ .meta),
.contact > :has(+ .contact-links),
.contact > :has(+ .planet-crosslink) {
  margin-bottom: var(--gap-group);
}

/* The title above and the date below are both satellites of the body, so
   they sit at the same distance from it. The block itself separates from
   the next transmission a full step further out. */
.transmission > :has(+ .transmission-date) {
  margin-bottom: var(--gap-related);
}

/* ============================================
   Layout
   ============================================ */

.page-wrapper {
  position: relative;
  z-index: 1;
}

section {
  padding: var(--gap-page) var(--gap-group);
  max-width: var(--section-max);
  margin: 0 auto;
}

/* --- Section Label --- */
.section-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--green);
  letter-spacing: var(--track-widest);
  text-transform: uppercase;
  margin-bottom: var(--gap-unit);
  opacity: 0.5;
}

.section-label::before {
  content: "// ";
}

.planet-brinstar .section-label {
  color: var(--brinstar);
}

/* --- Section Heading ---
   A real heading in the terminal idiom, not a micro-label floating above a
   headline. Sized to be read, not to decorate. */
.section-heading {
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  font-weight: 500;
  color: var(--green);
  letter-spacing: var(--track-tight);
  margin-bottom: var(--gap-unit);
  opacity: 0.75;
}

.section-heading::before {
  content: "// ";
  opacity: 0.5;
}

/* A heading that opens a second group inside one section needs air above the
   group it closes over, or it reads as a caption on the last row. The gap is
   declared by the group that closes, not by the heading that opens: the pair
   gets one owner, so the rendered distance is the one written here and not a
   margin collapse between two of them. */
.work-rows:has(+ .section-heading) {
  margin-bottom: var(--gap-zone);
}

/* ============================================
   Hero
   ============================================ */

.hero {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: var(--gap-zone);
  padding-bottom: var(--gap-zone);
}

/* --- Hero Avatar (visor) --- */
.hero-visor {
  width: 120px;
  height: 48px;
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--gap-related);
  border: 1px solid rgb(var(--accent-rgb) / 12%);
  box-shadow: 0 0 30px rgb(var(--accent-rgb) / 8%);
}

.hero-avatar {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 35%;
  filter: saturate(1.1);
}

.hero-name {
  font-family: var(--font-pixel);
  font-size: clamp(1rem, 3.4vw, 1.75rem);
  color: var(--accent);
  margin-bottom: var(--gap-bonded);
  text-shadow: 0 0 40px rgb(var(--accent-rgb) / 25%);
  letter-spacing: var(--track-tight);
  animation: hero-glow 4s ease-in-out infinite alternate;
}

/* felt is me · roid is the machine · prime is the light */
.hero-name .n-felt { color: var(--accent); }
.hero-name .n-roid { color: var(--steel); }

.hero-name .n-prime {
  color: var(--gold);
  text-shadow: 0 0 24px rgb(var(--gold-rgb) / 45%);
}

@keyframes hero-glow {
  0% { text-shadow: 0 0 40px rgb(var(--accent-rgb) / 20%); }
  100% { text-shadow: 0 0 60px rgb(var(--accent-rgb) / 35%), 0 0 100px rgb(var(--accent-rgb) / 8%); }
}

.hero-handle {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--text-dim);
  margin-bottom: var(--gap-group);
  letter-spacing: var(--track-tight);
}

.hero h1 {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.5vw, 2.1rem);
  font-weight: 600;
  color: var(--text-bright);
  line-height: 1.5;
  max-width: 700px;
  margin-bottom: var(--gap-related);
  letter-spacing: -0.01em;
}

.hero-sub {
  color: var(--text-dim);
  margin-bottom: var(--gap-unit);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--track-tight);
}

/* --- CTAs --- */
.cta-group {
  display: flex;
  gap: var(--gap-related);
  flex-wrap: wrap;
}

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-tight);
  background: var(--orange);
  color: var(--bg-deep);
  font-family: var(--font-pixel);
  font-size: var(--fs-3xs);
  padding: var(--gap-related) var(--gap-unit);
  border: none;
  border-radius: var(--radius-sharp);
  cursor: pointer;
  text-decoration: none;
  font-weight: 700;
  letter-spacing: var(--track-tight);
  transition: all var(--dur-fast);
  box-shadow: var(--glow-orange);
  position: relative;
}

.btn-primary::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: var(--radius);
  background: linear-gradient(135deg, rgb(var(--orange-light-rgb) / 30%), transparent, rgb(var(--orange-rgb) / 20%));
  z-index: -1;
  opacity: 0;
  transition: opacity var(--dur-fast);
}

.btn-primary:hover {
  background: var(--orange-glow);
  color: var(--bg-deep);
  transform: translateY(-2px);
  box-shadow: 0 0 40px rgb(var(--orange-rgb) / 35%), 0 4px 20px rgb(0 0 0 / 40%);
}

.btn-primary:hover::before {
  opacity: 1;
}

/* --- Text Link CTA (demoted secondary) --- */
.cta-text-link {
  display: inline-block;
  margin-top: var(--gap-unit);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  text-decoration: none;
  letter-spacing: var(--track-tight);
  transition: color var(--dur-fast);
}

.cta-text-link:hover {
  color: var(--accent);
}

/* ============================================
   Scan Visor Panels
   ============================================ */

.scan-panel {
  background: linear-gradient(135deg, var(--bg-panel), var(--scrim-panel));
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  padding: var(--gap-unit);
  margin-bottom: var(--gap-group);
  position: relative;
  transition: border-color var(--dur), background var(--dur), transform var(--dur-fast);
  overflow: hidden;
}

.scan-panel::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 100px;
  height: 100px;
  background: radial-gradient(circle at top right, rgb(var(--accent-rgb) / 3%), transparent 70%);
  pointer-events: none;
}

.scan-panel:hover {
  border-color: rgb(var(--accent-rgb) / 25%);
  background: linear-gradient(135deg, var(--bg-panel-hover), var(--scrim-panel-hover));
  transform: translateX(3px);
}

.planet-brinstar .scan-panel:hover {
  border-color: rgb(var(--brinstar-rgb) / 25%);
}

.scan-panel::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 2px;
  height: 100%;
  background: linear-gradient(180deg, var(--accent), transparent);
  opacity: 0.3;
  transition: opacity var(--dur);
}

.scan-panel:hover::before {
  opacity: 0.9;
}

.scan-panel-header {
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--accent);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  margin-bottom: var(--gap-related);
  display: flex;
  align-items: center;
  gap: var(--gap-close);
}

.scan-panel-header .tag {
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--bg-deep);
  background: var(--accent);
  padding: var(--gap-bonded) var(--gap-tight);
  border-radius: var(--radius-sharp);
  font-weight: 700;
  letter-spacing: var(--track-tight);
}

.scan-panel-header .tag.orange {
  background: var(--orange);
}

.scan-panel-header .tag.green {
  background: var(--green);
}

.scan-panel p {
  color: var(--text);
  font-size: var(--fs-md);
  margin-bottom: var(--gap-unit);
  line-height: 1.7;
}

.scan-panel code {
  font-family: var(--font-mono);
  font-size: 0.82em;
  color: var(--accent);
  background: rgb(var(--accent-rgb) / 7%);
  padding: 0.1em 0.35em;
  border-radius: var(--radius);
}

.scan-panel .meta {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
}

/* ============================================
   Adopters
   Each mark is the organisation's own, and links to the manifest that
   declares the dependency. A logo here is a claim, so it has to be checkable.
   ============================================ */

.adopters-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  margin-bottom: var(--gap-tight);
  letter-spacing: var(--track-tight);
}

.adopters {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-close);
}

.adopters a {
  display: block;
  width: 44px;
  height: 44px;
  padding: var(--gap-bonded);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  background: var(--wash-1);
  transition: border-color var(--dur-fast), transform var(--dur-fast);
}

.adopters img {
  width: 100%;
  height: 100%;
  border-radius: var(--radius-md);
  object-fit: contain;

  /* The marks arrive at mixed saturation. Damping them keeps the row reading
     as one set, and hover returns each to full colour. */
  filter: grayscale(35%);
  opacity: 0.85;
  transition: filter var(--dur-fast), opacity var(--dur-fast);
}

.adopters a:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
}

.adopters a:hover img {
  filter: none;
  opacity: 1;
}

.scan-panel .meta a {
  color: var(--accent-dim);
  transition: color var(--dur-fast);
}

.scan-panel .meta a:hover {
  color: var(--accent);
}

/* --- Stats Row --- */
.stats-row {
  display: flex;
  gap: var(--gap-related);
  margin-bottom: var(--gap-related);
  flex-wrap: wrap;
}

.stat {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
}

.stat-value {
  color: var(--green);
  font-weight: 700;
}

.stat-label {
  color: var(--text-dim);
  margin-left: var(--gap-bonded);
}

/* --- Primary panel ---
   One entry carries this page. It gets the weight. */
.scan-panel.primary {
  padding: var(--gap-group);
  border-left: 2px solid var(--accent);
  background: linear-gradient(135deg, var(--bg-panel-hover), var(--scrim-panel-deep));
}

.scan-panel.primary .scan-panel-header {
  font-size: var(--fs-sm);
}

.scan-panel.primary p {
  font-size: var(--fs-md);
}

/* ============================================
   Work Rows (secondary Built entries)
   No card chrome. Grouped by a single hairline, split title / body.
   ============================================ */

/* No margin-top here: whatever precedes a work list — a section heading or the
   panel above it — already declares the gap down to it. */

.work-row {
  display: grid;

  /* Column gap only. The vertical gaps between the blocks in the right
     column are relationships, and each one is declared by the block above
     it, so a single row-gap cannot state them. */
  gap: 0 var(--gap-group);
  padding: var(--gap-unit) 0;
  border-top: 1px solid var(--border-color);
}

/* The default tie inside one entry: consecutive prose, which is one
   argument continued. Grid items do not collapse margins, so each of these
   gaps renders at exactly the value named.

   This is one rung higher than it looks like it should be, and the content
   is why. Trimmed to the ink, a 16px paragraph break puts 16px between one
   baseline and the next capital — and the leading inside the paragraph
   already puts about 15px there. The two are indistinguishable, so the
   break disappears and the entry reads as one slab. A paragraph break has
   to beat its own line spacing to be a break at all. */
.work-row > * {
  margin-bottom: var(--gap-unit);
}

/* The numbers sit under the prose they measure, so they belong to it. */
.work-row > .stats-row {
  margin-bottom: var(--gap-related);
}

/* A dated log is a different kind of block from the prose around it: a
   weaker tie, so more space on both of its edges. */
.work-row > :has(+ .build-log),
.build-log {
  margin-bottom: var(--gap-group);
}


@media (width >= 720px) {
  .work-row {
    grid-template-columns: 1fr 2fr;
  }

  /* Title sits alone in the left column. Everything else stacks in the right
     column, however many blocks the entry has. Placing the children
     explicitly rather than spanning the title, because a row span cannot know
     how many rows auto-placement will create. */
  .work-row h3 {
    grid-column: 1;
    grid-row: 1;
  }

  .work-row > :not(h3) {
    grid-column: 2;
  }
}

/* --- Build log ---
   Dated entries, each linking to the post it quotes. Showing the work as it
   happened, which a finished description cannot do. */
.build-log {
  list-style: none;
  border-left: 1px solid var(--border-color);
  padding-left: var(--gap-related);
}

.build-log li {
  display: grid;
  gap: var(--gap-bonded) var(--gap-related);
  padding: var(--gap-tight) 0;
}

@media (width >= 900px) {
  .build-log li {
    grid-template-columns: 3.4rem 1fr;
  }
}

.log-date {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--accent-dim);
  letter-spacing: var(--track-tight);

  /* No optical nudge. This used to carry 4px of hand-tuned padding to drop
     the date onto the entry's first line; both boxes are now trimmed to cap
     height, so the grid aligns their capitals with no correction. */
}

.build-log a {
  color: var(--text-dim);
  font-size: var(--fs-sm);
  line-height: 1.6;
  transition: color var(--dur-fast);
}

.build-log a:hover {
  color: var(--text);
}

.work-row h3 {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text-bright);
  letter-spacing: var(--track-tight);
}

.work-row p {
  color: var(--text);
  font-size: var(--fs-md);
  line-height: 1.7;
}

/* A run of source links is one list, so it sets tighter than prose. */
.work-row > a {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--accent-dim);
  word-break: break-all;
  margin-bottom: var(--gap-tight);
}

/* Nothing declares space against the container edge: the row's own padding
   is what separates it from the rule below. Last, so it outranks the
   equally specific rule above it. */
.work-row > :last-child {
  margin-bottom: 0;
}

.work-row > a::before {
  content: "→ ";
}

.work-row > a:hover {
  color: var(--accent);
}

/* ============================================
   Endorsement
   Third-party words. The one thing the page cannot self-supply.
   ============================================ */

.endorsement {
  /* The block above declares the space down to a quote; the quote declares
     nothing upward. Nested in a panel it needs no bottom margin either — the
     block below it, or the panel's own padding, closes it. */
  margin: 0;
  padding: var(--gap-related) 0 var(--gap-related) var(--gap-unit);
  border-left: 2px solid var(--orange);
}

.endorsement p {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 500;
  color: var(--text-bright);
  line-height: 1.6;
  margin-bottom: var(--gap-close);
}

.endorsement cite {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-style: normal;
  color: var(--text-dim);
}

.endorsement cite a {
  color: var(--accent-dim);
}

.endorsement cite a:hover {
  color: var(--accent);
}

/* ============================================
   Contact Section
   ============================================ */

.contact {
  text-align: center;
  padding-bottom: var(--gap-page);
}

.contact h2 {
  font-family: var(--font-display);
  font-size: clamp(1rem, 2.5vw, 1.3rem);
  font-weight: 600;
  color: var(--accent);
  margin-bottom: var(--gap-related);
  letter-spacing: var(--track-tight);
}

/* This paragraph is the ask. It has to be read, so it gets body colour and a
   measure, not the dim treatment the old rhetorical one-liner had. */
.contact p {
  color: var(--text);
  margin: 0 auto var(--gap-unit);
  max-width: 54ch;
  font-size: var(--fs-md);
  line-height: 1.7;
}

.contact-alt {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: var(--gap-related);
}

.contact-alt a {
  color: var(--accent-dim);
}

/* The Signal handle is not a link — nothing to open. Select-all makes one
   click grab the whole thing, which is all a reader wants to do with it. */
.contact-alt .handle {
  color: var(--accent);
  user-select: all;
}

.contact-alt a:hover {
  color: var(--accent);
}

.contact-links {
  display: flex;
  justify-content: center;
  gap: var(--gap-group);
  flex-wrap: wrap;
}

.contact-link {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--text-dim);
  transition: color var(--dur-fast), transform var(--dur-fast);
  display: flex;
  align-items: center;
  gap: var(--gap-tight);
}

.contact-link:hover {
  color: var(--accent);
  transform: translateY(-1px);
}

.contact-link .icon {
  font-size: var(--fs-md);
}

/* Norfair communications console. It sits on a visible floor rail so the
   last call to action feels built into the station, not floated over it. */
.contact-terminal {
  max-width: 960px;
  padding-bottom: var(--gap-unit);
}

.contact-shell {
  position: relative;
  overflow: hidden;
  text-align: left;
  background:
    linear-gradient(90deg, rgb(var(--accent-rgb) / 3%) 1px, transparent 1px) 0 0 / 42px 42px,
    linear-gradient(rgb(var(--accent-rgb) / 3%) 1px, transparent 1px) 0 0 / 42px 42px,
    linear-gradient(145deg, var(--bg-raised), var(--bg-sunken) 72%);
  border: 1px solid rgb(var(--accent-rgb) / 28%);
  border-top: 3px solid var(--orange);
  box-shadow:
    0 28px 60px rgb(0 0 0 / 45%),
    inset 0 0 70px rgb(0 0 0 / 22%);
  clip-path: polygon(18px 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 0 100%, 0 18px);
}

.contact-shell::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(110deg, transparent 42%, var(--sheen) 50%, transparent 58%);
}

.contact-status {
  display: flex;
  justify-content: space-between;
  gap: var(--gap-related);
  padding: var(--gap-close) var(--gap-unit);
  border-bottom: 1px solid rgb(var(--accent-rgb) / 15%);
  background: var(--scrim);
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--text-dim);
  letter-spacing: var(--track-wider);
}

.contact-brief {
  padding: var(--gap-group) var(--gap-group) var(--gap-unit);
}

.contact .contact-kicker {
  margin: 0 0 var(--gap-close);
  font-family: var(--font-pixel);
  font-size: var(--fs-3xs);
  color: var(--orange-glow);
  letter-spacing: var(--track-wider);
}

.contact-terminal h2 {
  margin-bottom: var(--gap-related);
  color: var(--text-bright);
  font-size: clamp(1.35rem, 3vw, 2rem);
}

.contact-terminal .contact-brief > p:last-child {
  max-width: 68ch;
  margin: 0;
  color: var(--text-dim);
  font-size: var(--fs-md);
}

.channel-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  margin: 0 var(--gap-group);
  background: rgb(var(--accent-rgb) / 18%);
  border: 1px solid rgb(var(--accent-rgb) / 18%);
}

.channel-card {
  min-width: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0 var(--gap-related);
  align-items: center;
  padding: var(--gap-unit);
  background: var(--scrim-strong);
  border: 0;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-fast), box-shadow var(--dur-fast);
}

.channel-card:hover,
.channel-card:focus-visible {
  background: rgb(var(--accent-rgb) / 8%);
  color: var(--text-bright);
  box-shadow: inset 3px 0 0 var(--orange);
  outline: none;
}

.channel-logo {
  grid-row: 1 / span 2;
  width: 2.4rem;
  height: 2.4rem;
  padding: var(--gap-tight);
  fill: currentcolor;
  border: 1px solid currentcolor;
  color: var(--brand-telegram);
  filter: drop-shadow(0 0 7px rgb(var(--brand-telegram-rgb) / 25%));
}

.channel-signal .channel-logo {
  color: var(--brand-signal);
  filter: drop-shadow(0 0 7px rgb(var(--brand-signal-rgb) / 30%));
}

.channel-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.channel-copy strong {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  letter-spacing: var(--track-tight);
}

.channel-copy small {
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.channel-action {
  grid-column: 2;
  align-self: end;
  margin-top: var(--gap-close);
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--accent-mid);
  letter-spacing: var(--track);
}

.contact-terminal .contact-links {
  justify-content: flex-start;
  align-items: center;
  gap: var(--gap-unit);
  margin: 0;
  padding: var(--gap-related) var(--gap-group) var(--gap-unit);
}

.aux-label {
  padding-right: var(--gap-unit);
  border-right: 1px solid rgb(var(--accent-rgb) / 16%);
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--text-dim);
  letter-spacing: var(--track-wide);
}

/* ============================================
   Planet Nav Button (discreet MAP)
   ============================================ */

.planet-nav-btn {
  position: fixed;
  bottom: var(--gap-unit);
  left: var(--gap-unit);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: var(--gap-tight);
  background: rgb(var(--accent-rgb) / 3%);
  border: 1px solid rgb(var(--accent-rgb) / 8%);
  border-radius: var(--radius);
  padding: var(--gap-tight) var(--gap-close);
  cursor: pointer;
  text-decoration: none;
  transition: all var(--dur);
  backdrop-filter: blur(8px);
}

.planet-nav-btn:hover {
  background: rgb(var(--accent-rgb) / 8%);
  border-color: rgb(var(--accent-rgb) / 25%);
  box-shadow: 0 0 25px rgb(var(--accent-rgb) / 8%);
  color: var(--accent);
}

.planet-nav-btn .map-icon {
  display: grid;
  grid-template-columns: repeat(3, 5px);
  gap: 2px;
}

.planet-nav-btn .map-icon span {
  width: 5px;
  height: 5px;
  border-radius: var(--radius-round);
  border: 1px solid var(--accent-dim);
  transition: all var(--dur-fast);
}

.planet-nav-btn .map-icon span.active {
  background: var(--accent);
  box-shadow: 0 0 4px var(--accent);
  border-color: var(--accent);
}

.planet-nav-btn .map-icon span.locked {
  border-color: var(--line-locked);
  opacity: 0.3;
}

.planet-nav-btn .map-label {
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--text-dim);
  letter-spacing: var(--track-wider);
  font-weight: 500;
}

.planet-nav-btn:hover .map-label {
  color: var(--accent);
}

/* ============================================
   Planet Map Overlay
   ============================================ */

.planet-map-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: var(--scrim-full);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease), visibility var(--dur);
  backdrop-filter: blur(20px);
  overflow: hidden;
}

/* Map background image */
.map-bg-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.06;
  pointer-events: none;
  filter: saturate(0.5) brightness(0.8);
}

.planet-map-overlay.open {
  opacity: 1;
  visibility: visible;
}

.map-header {
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-dim);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  margin-bottom: var(--gap-zone);
}

.map-close {
  position: absolute;
  top: var(--gap-unit);
  right: var(--gap-unit);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  background: none;
  border: 1px solid var(--wash-2);
  border-radius: var(--radius);
  padding: var(--gap-tight) var(--gap-close);
  cursor: pointer;
  transition: all var(--dur-fast);
  letter-spacing: var(--track-wide);
}

.map-close:hover {
  color: var(--accent);
  border-color: var(--accent-dim);
}

.planet-grid {
  display: flex;
  align-items: center;
  gap: var(--gap-group);
}

@media (width <= 640px) {
  .planet-grid {
    flex-direction: column;
    gap: var(--gap-unit);
  }
}

/* --- Planet Node --- */
.planet-node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gap-related);
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--dur) var(--ease);
}

.planet-node:hover {
  transform: scale(1.08);
  color: inherit;
}

.planet-orb {
  width: 110px;
  height: 110px;
  border-radius: var(--radius-round);
  position: relative;
  transition: box-shadow var(--dur);
}

/* Norfair — lava */
.planet-node.norfair .planet-orb {
  background: var(--orb-norfair);
  box-shadow:
    0 0 40px rgb(var(--orange-rgb) / 25%),
    0 0 80px rgb(var(--orange-rgb) / 8%),
    inset 0 0 20px rgb(0 0 0 / 30%);
}

.planet-node.norfair:hover .planet-orb {
  box-shadow:
    0 0 50px rgb(var(--orange-rgb) / 40%),
    0 0 100px rgb(var(--orange-rgb) / 15%),
    inset 0 0 20px rgb(0 0 0 / 30%);
}

/* Brinstar — organic */
.planet-node.brinstar .planet-orb {
  background: var(--orb-brinstar);
  box-shadow:
    0 0 35px rgb(var(--brinstar-rgb) / 20%),
    0 0 70px rgb(var(--brinstar-rgb) / 6%),
    inset 0 0 20px rgb(0 0 0 / 30%);
}

.planet-node.brinstar:hover .planet-orb {
  box-shadow:
    0 0 50px rgb(var(--brinstar-rgb) / 35%),
    0 0 100px rgb(var(--brinstar-rgb) / 12%),
    inset 0 0 20px rgb(0 0 0 / 30%);
}

/* Tourian — locked */
.planet-node.tourian .planet-orb {
  background: var(--orb-tourian);
  box-shadow: 0 0 20px rgb(var(--tourian-rgb) / 10%);
  opacity: 0.4;
}

.planet-node.tourian {
  cursor: default;
  opacity: 0.5;
}

.planet-node.tourian:hover {
  transform: none;
}

.planet-name {
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
}

.planet-node.norfair .planet-name { color: var(--orange); }
.planet-node.brinstar .planet-name { color: var(--brinstar); }
.planet-node.tourian .planet-name { color: var(--tourian); }

.planet-tagline {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  max-width: 180px;
  text-align: center;
  line-height: 1.5;
}

.lock-badge {
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--tourian);
  background: rgb(var(--tourian-rgb) / 10%);
  border: 1px solid rgb(var(--tourian-rgb) / 20%);
  border-radius: var(--radius-sharp);
  padding: var(--gap-bonded) var(--gap-tight);
  letter-spacing: var(--track-wider);
}

/* Connector */
.connector {
  width: 50px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--wash-2), transparent);
}

@media (width <= 640px) {
  .connector {
    width: 1px;
    height: 30px;
    background: linear-gradient(180deg, transparent, var(--wash-2), transparent);
  }
}

/* Current planet ring */
.planet-node.current .planet-orb::after {
  content: "";
  position: absolute;
  inset: calc(var(--gap-close) * -1);
  border-radius: var(--radius-round);
  border: 1px solid var(--wash-3);
  animation: orbit-pulse 3.5s ease-in-out infinite;
}

@keyframes orbit-pulse {
  0%, 100% {
    transform: scale(1);
    opacity: 0.2;
  }

  50% {
    transform: scale(1.1);
    opacity: 0.6;
  }
}

/* ============================================
   Page Nav (sub-pages)
   ============================================ */

.page-nav {
  position: fixed;
  top: var(--gap-unit);
  left: var(--gap-unit);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: var(--gap-tight);
}

.planet-indicator {
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  letter-spacing: var(--track-wide);
  padding: var(--gap-bonded) var(--gap-tight);
  border-radius: var(--radius-sharp);
}

.planet-indicator.norfair {
  color: var(--orange);
  background: rgb(var(--orange-rgb) / 5%);
  border: 1px solid rgb(var(--orange-rgb) / 15%);
}

.planet-indicator.brinstar {
  color: var(--brinstar);
  background: rgb(var(--brinstar-rgb) / 5%);
  border: 1px solid rgb(var(--brinstar-rgb) / 15%);
}

.planet-indicator.tourian {
  color: var(--tourian);
  background: rgb(var(--tourian-rgb) / 5%);
  border: 1px solid rgb(var(--tourian-rgb) / 15%);
}

/* Cross-link to another planet, at the foot of a page */
.planet-crosslink {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  opacity: 0.3;
}

.planet-crosslink a {
  opacity: 0.6;
}

/* ============================================
   Transmissions (Brinstar)
   ============================================ */

.transmission {
  border-left: 1px solid rgb(var(--brinstar-rgb) / 15%);
  padding-left: var(--gap-unit);
  margin-bottom: var(--gap-group);
  position: relative;
}

.transmission::before {
  content: "";
  position: absolute;
  left: -3px;
  top: 0;
  width: 5px;
  height: 5px;
  border-radius: var(--radius-round);
  background: var(--brinstar);
  box-shadow: 0 0 8px rgb(var(--brinstar-rgb) / 40%);
}

.transmission-id {
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--brinstar);
  letter-spacing: var(--track-wider);
  margin-bottom: var(--gap-tight);
  opacity: 0.4;
}

.transmission-title {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 600;
  color: var(--text-bright);
  margin-bottom: var(--gap-related);
  line-height: 1.4;
}

.transmission-body {
  color: var(--text);
  font-size: var(--fs-md);
  line-height: 1.85;
}

.transmission-body em {
  color: var(--text-bright);
  font-style: italic;
}

.transmission-date {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-dim);
  opacity: 0.35;
}

/* ============================================
   Tourian Locked
   ============================================ */

.locked-container {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--gap-unit);
}

.locked-icon {
  font-size: var(--fs-display);
  margin-bottom: var(--gap-group);
  opacity: 0.2;
  animation: static-flicker 5s ease-in-out infinite;
  color: var(--tourian);
}

@keyframes static-flicker {
  0%, 100% { opacity: 0.2; }
  48% { opacity: 0.15; }
  50% { opacity: 0.35; }
  52% { opacity: 0.1; }
  54% { opacity: 0.25; }
}

.locked-title {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--tourian);
  letter-spacing: var(--track-widest);
  text-transform: uppercase;
  margin-bottom: var(--gap-close);
}

.locked-subtitle {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  opacity: 0.4;
  margin-bottom: var(--gap-group);
  max-width: 400px;
}

.locked-rules-preview {
  display: flex;
  flex-direction: column;
  gap: var(--gap-tight);
  max-width: 520px;
  width: 100%;
}

.locked-rule {
  display: flex;
  align-items: center;
  gap: var(--gap-related);
  padding: var(--gap-close) var(--gap-related);
  background: rgb(var(--tourian-rgb) / 3%);
  border: 1px solid rgb(var(--tourian-rgb) / 6%);
  border-radius: var(--radius);
  transition: border-color var(--dur-fast);
}

.locked-rule:hover {
  border-color: rgb(var(--tourian-rgb) / 15%);
}

.locked-rule-number {
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--tourian);
  white-space: nowrap;
  letter-spacing: var(--track);
}

.locked-rule-text {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  color: var(--text-dim);
  filter: blur(4px);
  user-select: none;
}

/* ============================================
   Footer
   ============================================ */

footer {
  text-align: center;
  padding: var(--gap-group) var(--gap-unit);
  border-top: 1px solid var(--wash-1);
  font-family: var(--font-mono);
  font-size: var(--fs-3xs);
  color: var(--text-dim);
  opacity: 0.35;
  letter-spacing: var(--track-wide);
}

/* ============================================
   Divider
   ============================================ */

.section-divider {
  max-width: var(--section-max);
  margin: 0 auto;
  border: none;
  border-top: 1px solid var(--wash-1);
}

/* ============================================
   Animations
   ============================================ */

/* Staggered fade-in.
   Only hide it when scripting is on — the IntersectionObserver in script.js is
   what reveals it. With JS off (or on a browser without `scripting:`), the rule
   never applies and the content stays visible. Fail open. */
@media (scripting: enabled) {
  .fade-in {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
  }
}

.fade-in.visible {
  opacity: 1;
  transform: translateY(0);
}

/* Stagger children */
.fade-in:nth-child(2) { transition-delay: 0.08s; }
.fade-in:nth-child(3) { transition-delay: 0.16s; }
.fade-in:nth-child(4) { transition-delay: 0.24s; }
.fade-in:nth-child(5) { transition-delay: 0.32s; }

/* Hero entrance */
.hero > * {
  opacity: 0;
  transform: translateY(20px);
  animation: hero-enter 0.8s var(--ease) forwards;
}

.hero > *:nth-child(1) { animation-delay: 0.1s; }
.hero > *:nth-child(2) { animation-delay: 0.2s; }
.hero > *:nth-child(3) { animation-delay: 0.3s; }
.hero > *:nth-child(4) { animation-delay: 0.4s; }
.hero > *:nth-child(5) { animation-delay: 0.5s; }
.hero > *:nth-child(6) { animation-delay: 0.6s; }
.hero > *:nth-child(7) { animation-delay: 0.7s; }

@keyframes hero-enter {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Glitch on hover */
.glitch-hover:hover {
  animation: glitch 0.3s ease;
}

@keyframes glitch {
  0% { transform: translate(0); }
  20% { transform: translate(-2px, 1px); }
  40% { transform: translate(2px, -1px); }
  60% { transform: translate(-1px, -1px); }
  80% { transform: translate(1px, 1px); }
  100% { transform: translate(0); }
}

/* Energy bar pulse */
@keyframes pulse-glow {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}

/* --- Reduced motion: keep the look, drop the movement --- */
@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;
  }

  /* These start hidden and are revealed by an animation/observer — make sure
     they land visible rather than stuck mid-transition. */
  .fade-in,
  .hero > * {
    opacity: 1;
    transform: none;
  }

  .starfield,
  .starfield::before,
  .starfield::after,
  .glitch-hover:hover {
    animation: none;
  }
}

/* ============================================
   Responsive
   ============================================ */

@media (width <= 640px) {
  section {
    padding: var(--gap-zone) var(--gap-related);
  }

  .hero {
    min-height: 92dvh;
  }

  /* Narrow viewport rewrites the relationship: at 390px a 42px inset is a
     tenth of the screen on each side, so the panel's own frame starts
     competing with the text for the measure. Both weights collapse to one
     step, and .primary is named explicitly because it outranks .scan-panel
     on specificity and would otherwise keep its desktop inset. */
  .scan-panel,
  .scan-panel.primary {
    padding: var(--gap-related);
  }

  .cta-group {
    flex-direction: column;
  }

  .btn-primary {
    width: 100%;
    justify-content: center;
  }

  .contact-terminal {
    padding-inline: var(--gap-related);
  }

  .contact-status {
    padding-inline: var(--gap-related);
  }

  .contact-brief {
    padding: var(--gap-unit) var(--gap-related) var(--gap-unit);
  }

  .channel-grid {
    grid-template-columns: 1fr;
    margin-inline: var(--gap-related);
  }

  .contact-terminal .contact-links {
    gap: var(--gap-related);
    padding-inline: var(--gap-related);
  }

  .aux-label {
    width: 100%;
    padding: 0 0 var(--gap-tight);
    border-right: 0;
    border-bottom: 1px solid rgb(var(--accent-rgb) / 16%);
  }

  .cta-text-link {
    display: block;
    text-align: center;
  }

  .planet-nav-btn {
    bottom: var(--gap-related);
    left: var(--gap-related);
  }

  .page-nav {
    top: var(--gap-related);
    left: var(--gap-related);
  }

  .planet-orb {
    width: 85px;
    height: 85px;
  }
}
