/* ==========================================================================
   SITE FOOTER — presentation for the upgraded footer
   Partner file: js/site-footer.js, which moves the page's own anchors into the
   structure this file styles. Built Aug 11 2026.

   EVERYTHING IS SCOPED UNDER `.footer.is-upgraded`, which the script adds only
   after it has successfully built. A page whose JS never runs keeps the
   footer it has always had, untouched, with every link live -- so this
   stylesheet can be loaded unconditionally and can never leave a page worse
   than it found it.

   IT DOES NOT EDIT css/components.css. That file styles the original footer on
   five pages that are byte-identical to main, and the original is the fallback
   this whole approach depends on. Changing it would move the floor.
   ========================================================================== */

/* THE ORIGINAL GOES NON-PAINTING, NOT DELETED. Its anchors have been MOVED
   into the new structure, so what is left in .footer__inner is the empty
   scaffolding around them; hiding it rather than removing it keeps the page's
   own markup intact for anything that reads the DOM after us. */
.footer.is-upgraded .footer__inner { display: none; }

.footer.is-upgraded {
  border-top: var(--border-hairline);
  padding-block: 0;
  position: relative;
  overflow: hidden;          /* the wordmark is cropped BY the footer */
}

.footer.is-upgraded .sf {
  position: relative;
  /* ~520px of block, which is the size the owner settled on against a
     full-height treatment. The site already has one immersive final scene --
     the entrance -- and one per site is the right number. A footer that
     demands a whole screen competes with the thing it exists to send you back
     to. */
  min-height: 480px;
  display: flex; flex-direction: column;
  padding: var(--space-10, 48px) 0 0;
}

/* THE PAGE'S OWN SPINE RUNS BEHIND THIS ON SOME PAGES, and that is the site's
   chosen design -- css/spine-bg.css lifts .footer to z-index 1 precisely so
   the column passes behind it. A taller footer simply exposes more of the
   column than the old ~200px strip ever did.

   AS OF Aug 11 2026 NO PAGE CARRYING THIS FOOTER SHOWS THE COLUMN, and it is
   worth writing down because it is not obvious from the stylesheets. Only
   index.html and about.html load js/spine-bg.js at all -- archive.html and
   transmissions.html link the CSS but never run the script that builds the
   layer, and transmissions/001.html has neither. Both pages that DO run it are
   switched off: html.page-home since 2026-08-06, html.page-about as of today.

   So the values below are currently being judged against plain black. If the
   column is ever turned back on under this footer, re-check them -- and fix
   any collision with LAYOUT rather than a scrim, which css/spine-bg.css rules
   out in writing.

   NO SCRIM. The obvious fix is a dark wash behind the columns, and
   css/spine-bg.css already ruled it out in writing: over flat page black a
   #03040F scrim is invisible, but over a column sitting at L 70 the same wash
   is far darker than its surroundings and reads as a dark ellipse floating on
   the spine. "Attenuate the backdrop, never paint over it." So legibility here
   comes from the ink instead: the link rows carry their own hairline rules and
   sit at --text-secondary or brighter, and everything that could not win that
   fight -- the seed's labels -- was moved out of the column band entirely. */

/* ==========================================================================
   THE SUBSTRATE — Seed of Life, behind everything
   Seven circles for seven centres. It is the connective tissue between this
   footer and the navigator's field: the same figure, the same seven, the same
   frequencies, so the two surfaces are visibly one system rather than two
   things that happen to share a palette.
   ========================================================================== */
/* CONFINED TO THE LOWER HALF, and this is a collision fix rather than a
   preference. Given the whole footer the figure's labels sat exactly where the
   link rows are -- MULADHARA printed across TRANSMISSIONS. The links are the
   footer's job; the substrate moves. Below 38% it shares space only with the
   instrument band and the wordmark, both of which are texture themselves. */
.footer.is-upgraded .sf__seed {
  position: absolute; left: 0; right: 0; top: 38%; bottom: 0;
  width: 100%; height: 62%;
  pointer-events: none; z-index: 0;
}
.footer.is-upgraded .sf__seed-ring circle {
  fill: none; stroke: rgba(214, 213, 208, 0.09); stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
/* SUBSTRATE ALPHAS, not label alphas. These were first set as if the names
   were content to be read; over the page's own lit spine column they became
   the third thing competing for the same centre line. They are here to be
   FOUND, not read -- the instrument band above states the same seven at
   readable contrast, so nothing is lost by making these quiet. */
.footer.is-upgraded .sf__seed-name,
.footer.is-upgraded .sf__seed-hz {
  text-anchor: middle; font-family: var(--font-mono);
  fill: rgba(214, 213, 208, 0.20);
}
.footer.is-upgraded .sf__seed-name { font-size: 8px; letter-spacing: 0.16em; }
.footer.is-upgraded .sf__seed-hz {
  font-size: 7px; letter-spacing: 0.12em; fill: rgba(var(--node-color), 0.24);
}

/* ==========================================================================
   TIER 1 — the four columns. Links first, and findable.
   ========================================================================== */
.footer.is-upgraded .sf__cols {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: var(--space-8, 32px);
  padding-inline: var(--space-6);
}
.footer.is-upgraded .sf-col__head {
  font-family: var(--font-mono); font-size: 10px; font-weight: 500;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--color-bone); margin: 0 0 var(--space-4, 16px);
}
.footer.is-upgraded .sf-col--id .sf-col__head {
  font-family: var(--font-display); font-size: 22px; font-weight: 800;
  letter-spacing: 0.02em; color: var(--text-primary);
}
.footer.is-upgraded .sf-col__blurb {
  font-family: var(--font-body); font-size: var(--fs-caption);
  color: var(--text-muted); max-width: 30ch; margin: 0;
}
.footer.is-upgraded .sf-col__list { list-style: none; margin: 0; padding: 0; }

/* A ROW IS A LINK AND ITS STATE, side by side. The state is what turns a dead
   link into information -- see the STANDBY note below. */
.footer.is-upgraded .sf-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-4, 16px); padding: 5px 0;
  border-bottom: 1px solid rgba(214, 213, 208, 0.07);
}
.footer.is-upgraded .sf-row__link,
.footer.is-upgraded .sf-row__dead {
  font-family: var(--font-mono); font-size: var(--fs-mono);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  text-decoration: none;
}
.footer.is-upgraded .sf-row__link { color: var(--text-secondary); transition: color var(--motion-fast); }
.footer.is-upgraded .sf-row__link:hover,
.footer.is-upgraded .sf-row__link:focus-visible { color: var(--text-primary); }

/* STANDBY IS NOT A DISABLED LINK, IT IS A STATEMENT ABOUT A CHANNEL.
   TikTok and Spotify have shipped as href="#" for the whole life of this
   footer: they look live, they do nothing, and clicking one teaches the reader
   that the site is broken rather than that the channel is not open yet. Muted
   ink and an explicit word say the true thing instead, and the script removes
   the anchor entirely so it is not focusable and offers no pointer. */
.footer.is-upgraded .sf-row__dead { color: var(--text-muted); }
/* 9px, NOT the 8px this shipped at. At 8px the glyph never reaches full
   coverage: measured 2026-08-11, the peak alpha anywhere on the STANDBY chip
   was 0.92 and only ONE pixel of the whole word cleared 0.90. A word whose ink
   never fully lands cannot be rescued by changing its colour, which is why the
   band's own fix below was not enough for this chip on its own.
   The size lives on the shared rule so OPEN and STANDBY stay the same size --
   they sit in the same column and a half-step between them reads as a mistake. */
.footer.is-upgraded .sf-row__state {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.18em;
  padding: 2px 6px; border: 1px solid transparent; white-space: nowrap;
}
.footer.is-upgraded .sf-row__state--open {
  color: rgba(var(--node-color), 0.75); border-color: rgba(var(--node-color), 0.28);
}
/* MEASURED 1.2:1 AGAINST THE REAL SKY, in the link tier -- the part this file
   calls the footer's only job. gray-500 here was settled against flat page
   black; over the lit nebula the word is an empty bordered box. Bone plus the
   site's two-pass shadow measures ~8.5:1 on the same pixels.
   Note --open has NOT been judged: it is rgba(var(--node-color), 0.75) and its
   contrast depends on which node colour the row carries. */
.footer.is-upgraded .sf-row__state--standby {
  color: var(--color-bone);
  text-shadow: 0 0 3px rgba(3, 4, 15, 0.98), 0 1px 2px rgba(3, 4, 15, 0.92);
  border-color: rgba(214, 213, 208, 0.16);
}

/* ==========================================================================
   TIER 2 — the instrument band. BESIDE the links, never around them.
   Faint enough to read as texture, real enough to rewarding looking at: the
   runtime is the sum of all 28 durations in data/tracks.json, the geometry
   counts are counted, and the calibration is the same seven the substrate
   draws.
   ========================================================================== */
/* FOUR EVEN COLUMNS AGAIN.
   This was briefly five tracks with an empty one down the middle, because the
   two inner blocks were landing on the page's lit spine column and GEOMETRY's
   values could not be read over it. The owner has since switched that column
   off on this page (--spine-on: 0 in css/spine-bg.css's html.page-about), so
   the gutter now has nothing to dodge -- and an empty track with no reason
   behind it reads as a mistake rather than as spacing. Reverted.

   If the column ever comes back on a page carrying this footer, the fix is
   the five-track grid and not a scrim: css/spine-bg.css rules that out in
   writing. */
.footer.is-upgraded .sf__instr {
  position: relative; z-index: 2;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-8, 32px);
  margin-top: var(--space-8, 32px);
  padding: var(--space-5, 20px) var(--space-6) 0;
  border-top: 1px solid rgba(214, 213, 208, 0.07);
  /* OPACITY 1, AND THE DIAL IS GONE ON PURPOSE. Owner's call 2026-08-11.
     0.7 was never a brightness control -- it is a GROUP opacity, so it
     composited the whole band toward whatever was behind it
     (0.7*ink + 0.3*backdrop). On flat black that merely dimmed the band. Over
     the lit nebula it INVERTS: where the sky is brighter than the ink, the
     keys come out darker than the pixels behind them and the label dissolves.
     Measured against real pixels at 1366x768 -- the worst viewport, where
     GEOMETRY sits on the lit track -- the keys ran 1.5:1 against a 4.5:1
     requirement, while the values beside them held 6.6:1. That asymmetry is
     the whole bug: the block lost its LABELS and kept its numbers, so it read
     as decoration rather than as an instrument.
     Dimming now belongs to the ink colours below, which composite against
     nothing and cannot invert. If the band ever needs to come down again,
     change those -- do not reintroduce a group opacity here. */
  opacity: 1;
}
.footer.is-upgraded .sf-instr__block { display: flex; flex-direction: column; gap: 3px; }
/* THE KEYS ARE gray-300 AND THAT IS THE FLOOR, NOT A PREFERENCE.
   Computed against the same backdrop: gray-400 (#6B6B6B) at opacity 1 lands at
   3.94:1, still under AA's 4.5:1 for text this size. gray-300 (#8F8F8F) is the
   lightest step DOWN from bone that clears it -- measured 6.1:1 on the glyph
   core. Going darker than gray-300 puts the labels back under the line.

   The two-pass shadow is here for the worst case rather than the average:
   individual stars and the nebula's bright core run straight through the
   glyphs, and a shadow darkens the local surround so contrast survives them.
   BE CAREFUL HOW YOU MEASURE THAT. The shipped numbers above (keys 1.5 -> 5.3)
   moved opacity, colour AND shadow at once, so they do NOT isolate the
   shadow's share -- and the obvious check cannot find it either: comparing a
   shadowed glyph's CORE against the bare backdrop shows a shadow doing
   literally nothing, because it changes neither of those two pixels. To
   measure it, compare the core against a backdrop captured WITH the shadow
   painted and the fill transparent (a shadow still paints from a transparent
   glyph's outline). The shadow's isolated contribution here has not been
   measured; it is carried on the precedent in spine-bg.css, which uses this
   exact pair three times for small text over the lit column. */
.footer.is-upgraded .sf-instr__head {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--color-gray-300); margin-bottom: 4px;
  min-height: 12px;          /* the fourth block has no head; keep the grid level */
  text-shadow: 0 0 3px rgba(3, 4, 15, 0.98), 0 1px 2px rgba(3, 4, 15, 0.92);
}
.footer.is-upgraded .sf-instr__row {
  display: flex; justify-content: space-between; gap: var(--space-4, 16px);
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.08em;
  color: var(--color-gray-300);
  text-shadow: 0 0 3px rgba(3, 4, 15, 0.98), 0 1px 2px rgba(3, 4, 15, 0.92);
}
.footer.is-upgraded .sf-instr__v { color: var(--color-bone); font-weight: 400; }

/* ==========================================================================
   THE BASE — KUNDALINI SPINES, outline only, cropped
   ========================================================================== */
.footer.is-upgraded .sf__mark {
  position: relative; z-index: 1;
  display: block; width: 100%; height: auto;
  margin-top: auto;          /* pinned to the bottom of the 520px block */
}
.footer.is-upgraded .sf__mark-text {
  font-family: var(--font-display, "Archivo", sans-serif);
  /* THE WORDMARK CUT, NOT THE DEFAULT WIDTH. Archivo at wdth 100 sets ~20%
     wider than the Big Shoulders this replaced, and sixteen characters at
     132px overran the 1000-unit viewBox — the sides clipped to "UNDALINI
     SPIN". wdth 62 is the lockup's own cut (css/wordmark.css); the JS also
     sets textLength on both copies so the fit is guaranteed whatever font
     actually loads. */
  font-weight: 800; font-size: 132px; letter-spacing: 0.01em;
  font-stretch: 62%;
  font-variation-settings: "wdth" 62, "wght" 800;
  /* The stroke takes a GRADIENT, which is why this is SVG text and not
     -webkit-text-stroke: that property accepts a colour and nothing else. */
  /* 1.6, not 1.25: an outline over a lit nebula needs weight before it
     needs colour, and at 1.25 both the core and the shine were being eaten by
     the backdrop rather than being too dim in themselves. */
  stroke-width: 1.35; vector-effect: non-scaling-stroke;
  paint-order: stroke;
}

/* Reduced motion: the torch does not chase, so it is parked warm and even
   rather than switched off. The effect exists to reward moving the pointer;
   with motion suppressed it should still look like something. */
/* Reduced motion or no fine pointer: the CORE alone. It is already a complete
   resting state -- LINI lit, falling away to KUNDA and NES -- so there is
   nothing to substitute. The travelling copy simply does not paint. */
.footer.sf-torch-static.is-upgraded .sf__mark-text--torch { display: none; }

.footer.is-upgraded .sf__foot {
  position: relative; z-index: 2;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-6); padding: var(--space-4, 16px) var(--space-6) var(--space-6);
  border-top: 1px solid rgba(214, 213, 208, 0.07);
}
.footer.is-upgraded .sf__copy {
  margin: 0; width: auto; color: var(--text-muted); font-size: var(--fs-caption);
}
.footer.is-upgraded .sf__seal {
  font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.24em;
  color: rgba(214, 213, 208, 0.22);
}

/* ==========================================================================
   NARROW
   The columns collapse before the instrument band does, because the links are
   the footer's job and the band is its texture -- when space runs out, texture
   goes first.
   ========================================================================== */
@media (max-width: 1000px) {
  .footer.is-upgraded .sf__cols { grid-template-columns: 1fr 1fr; }
  .footer.is-upgraded .sf__instr { grid-template-columns: 1fr 1fr; }
  .footer.is-upgraded .sf__mark-text { font-size: 96px; }
}
@media (max-width: 640px) {
  .footer.is-upgraded .sf { min-height: 0; }
  .footer.is-upgraded .sf__cols { grid-template-columns: 1fr; gap: var(--space-6); }
  .footer.is-upgraded .sf__instr { display: none; }
  .footer.is-upgraded .sf__mark-text { font-size: 64px; }
  .footer.is-upgraded .sf__foot { flex-direction: column; gap: var(--space-2); }
}

/* ==========================================================================
   THE TUNING PANEL — no longer lives here.

   The footer torch had its own fixed box in this file: bottom-left, 240px
   wide, with a hide-to-chip button and a specificity note about the doubled
   `.sf-tune.sf-tune--min` selector. It is a tab in the shared shell now --
   js/tune-panel.js owns the box, the chrome and the /?tune gate, and
   js/site-footer.js contributes only sliders. The specificity lesson moved
   with the rules; the note is in tune-panel.js beside `.ks-tune--min`.

   Nothing named .sf-tune should reappear here. A tuner that styles itself is
   how /?tune ended up with four boxes in four corners.
   ========================================================================== */
