/* ============================================================================
   ASTROVE — CRYOSTAT
   ----------------------------------------------------------------------------
   A thermal descent gauge. Two renderers, ONE state engine (js/trace.js):

     CRYO RAIL     welded under the floating header, every viewport
     CRYO COLUMN   an illuminated gauge at the right edge, desktop only

   THE IDEA. Scrolling the page is a temperature drop. The reader begins at
   ambient warmth and descends, chapter by chapter, into the cold end of the
   spectrum -- amber, to ivory, through the freezing threshold, into ice, cyan,
   cobalt and finally ultraviolet. The instrument does not "fill up"; it COOLS.

   WHY A GRADIENT AND NOT A COLOUR. The rail carries the whole thermal ramp at
   once and reveals it as you travel, so the bar behind the head is a readable
   history of the descent and the ground ahead is dark. A progress bar answers
   "how much is left". This answers "how cold have we got".

   THE STOPS ARE NOT EVENLY SPACED. --thermal-stops is composed in JS from the
   REAL scroll offset of every chapter, so the colour under the head is always
   the colour of the chapter the reader is actually in. With fixed percentage
   stops the ramp would drift out of phase with the page within two chapters,
   because these sections differ in height by more than 6x.

   NO NUMERIC TEMPERATURES. Section 9 of the canonical claims reference lists
   process temperatures as PRODUCTION DATA REQUIRED. The figure was removed
   from the proof strip in index.html for that reason; process-engine.js does
   still animate it inside the Process section, which is a separate, flagged
   exposure. This gauge is site-wide and visible at EVERY scroll position, so
   putting a number on it would turn one sectional claim into a permanent one.
   The bands below are qualitative state names, not measurements.
   ========================================================================== */

.trace {
  --trace-p: 0;

  /* The thermal ramp. Warm through cold, monotonic -- there is no hue that
     doubles back, which is what keeps this a temperature scale and not a
     rainbow. Every value is either an existing token or a mix on the direct
     path between two of them. */
  --t-opening:    #ffb84d;   /* ambient    -- warm amber */
  --t-shop:       #ffa50a;   /* tempered   -- --nectar */
  --t-process:    #ffe2a8;   /* threshold  -- the last warm light */
  --t-mango:      #7fe4f7;   /* cold       -- first true ice */
  --t-jamun:      #2ee9ff;   /* deep cold  -- --electric-ice */
  --t-pineapple:  #1fa8ff;   /* glacial */
  --t-faq:        #2f5cff;   /* cryogenic */
  --t-harvest:    #1f3dff;   /* vitrified  -- --aurora-blue */
  --t-afterlight: #7a2dff;   /* absolute   -- --ultraviolet */

  /* Fallback ramp, used only if the engine has not composed the real one yet
     (it does so on first refresh). Even stops, correct colours: wrong phase for
     a few milliseconds is invisible; an unpainted bar is not. */
  --thermal-stops:
    var(--t-opening) 0%, var(--t-shop) 12.5%, var(--t-process) 25%,
    var(--t-mango) 37.5%, var(--t-jamun) 50%, var(--t-pineapple) 62.5%,
    var(--t-faq) 75%, var(--t-harvest) 87.5%, var(--t-afterlight) 100%;

  /* CURRENT TEMPERATURE as a `color` property, not a custom property. Custom
     properties do not interpolate without @property; `color` does, so every
     chapter change cross-fades over 520ms instead of cutting. Everything
     luminous below is drawn in currentColor and inherits that transition for
     free -- one colour on the instrument at any instant, never a rainbow. */
  color: var(--t-opening);
  transition: color 520ms var(--ease-out);
}

[data-trace-chapter="opening"]    { color: var(--t-opening); }
[data-trace-chapter="shop"]       { color: var(--t-shop); }
[data-trace-chapter="process"]    { color: var(--t-process); }
[data-trace-chapter="mango"]      { color: var(--t-mango); }
[data-trace-chapter="jamun"]      { color: var(--t-jamun); }
[data-trace-chapter="pineapple"]  { color: var(--t-pineapple); }
[data-trace-chapter="faq"]        { color: var(--t-faq); }
[data-trace-chapter="harvest"]    { color: var(--t-harvest); }
[data-trace-chapter="afterlight"] { color: var(--t-afterlight); }

/* ---------------------------------------------------------------------------
   THE BULB — shared by both renderers.

   A lit thermometer bulb, built from CSS only: no emoji, no icon font, no
   image. White-hot core, currentColor body, and a two-stage halo so it throws
   light onto the ground around it rather than sitting on it. 12px, against the
   5px point the previous system used -- this is the part that has to be
   legible from across a room.
   -------------------------------------------------------------------------- */
.cryo-bulb {
  position: absolute;
  z-index: 3;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0 30%, currentColor 58%, color-mix(in srgb, currentColor 55%, transparent) 78%, transparent 82%);
  box-shadow:
    0 0 8px currentColor,
    0 0 20px color-mix(in srgb, currentColor 70%, transparent),
    0 0 38px color-mix(in srgb, currentColor 35%, transparent);
  pointer-events: none;
}

/* One ignition per chapter entry: a cold flare that swells and settles. Not a
   loop and not a pulse -- the engine latches it behind a dead zone so drifting
   across a boundary cannot re-fire it. */
.trace.is-igniting .cryo-bulb { animation: cryo-flare 460ms var(--ease-out); }
@keyframes cryo-flare {
  0%   { scale: 1; }
  38%  { scale: 1.7; box-shadow: 0 0 16px currentColor, 0 0 40px currentColor, 0 0 72px color-mix(in srgb, currentColor 55%, transparent); }
  100% { scale: 1; }
}

/* ---------------------------------------------------------------------------
   CRYO RAIL — the top instrument.

   It is welded to the header by mirroring the header's own arithmetic, and it
   MORPHS WITH IT. At rest the header is a floating pill inset by the gutter
   with a 0.35rem radius, so the rail is an inset capsule sitting just beneath
   it and reads as an object. Past 120px the header goes flush and square, and
   the rail follows -- the two become one instrument panel bonded to the top of
   the page. This is the whole reason it cannot be mistaken for a browser
   loading bar: a loading bar is full-bleed and square from the first frame and
   is welded to nothing.

   z-index 235 sits under the header (240), far under the drawers (420) and the
   overlays (600-620). Nothing here takes pointer input.
   -------------------------------------------------------------------------- */
.cryo-rail {
  position: fixed;
  z-index: 235;
  /* Header rest geometry + a 6px gap, so the rail is a separate object rather
     than an underline welded to the pill's border. */
  top: calc(0.75rem + var(--safe-top, 0px) + 4.4rem + 6px);
  right: var(--gutter-right, var(--gutter));
  left: var(--gutter-left, var(--gutter));
  height: 6px;
  border-radius: 3px;
  pointer-events: none;
  /* The header transitions top / height / border-radius over 260ms but NOT
     left/right, which snap. Transitioning them here would desync the two
     during the condense, so this matches exactly what the header does. */
  transition: top 260ms var(--ease-out), border-radius 260ms var(--ease-out);
}

/* Condensed: flush to the header's new bottom edge, full bleed, square. */
body:has(.site-header.is-condensed) .cryo-rail {
  top: calc(2.9rem + var(--safe-top, 0px));
  right: 0;
  left: 0;
  border-radius: 0;
}

/* The narrow header is not the wide one scaled down: at <=720px it re-declares
   top:0.5rem / left:0.5rem / right:0.5rem and height:3.55rem, and it drops
   --safe-top from its own offset. Measured, its resting bottom edge is 64.8px
   at 390 against the 82.4px the wide arithmetic predicts. Mirror what the
   header actually computes, including the absent inset. The condensed rule
   above already matches at every width. */
@media (max-width: 720px) {
  .cryo-rail {
    top: calc(0.5rem + 3.55rem + 5px);
    right: 0.5rem;
    left: 0.5rem;
  }
}

/* The channel the thermal column sits in: a machined recess, not an empty
   progress track. Dark, with a lit top edge so it reads as cut into the page. */
.cryo-rail__track {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  /* Opaque enough to hold its own shape over the hero photograph. An ivory
     wash at 5% is invisible against a lit mango plate, which is exactly the
     ground the rail sits on at rest -- the unfilled channel has to read on
     bright and dark alike, or the instrument only exists once you scroll. */
  background: rgba(6, 6, 10, 0.62);
  box-shadow:
    inset 0 1px 0 rgba(255, 247, 230, 0.1),
    inset 0 0 0 1px rgba(255, 247, 230, 0.05);
}

/* THE THERMAL COLUMN. Full width always; the reveal is a clip, so the gradient
   never rescales and the colour at any point on the bar stays bonded to the
   chapter that owns it. clip-path also keeps this off the layout path -- there
   is no width write and nothing to reflow on a scroll frame. */
.cryo-rail__fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--thermal-stops));
  clip-path: inset(0 calc(100% - var(--trace-p) * 100%) 0 0);
  will-change: clip-path;
}

/* Light spilling off the head onto the ground. A positioned radial rather than
   a blurred copy of the bar: same read, no per-frame filter. */
.cryo-rail__bloom {
  position: absolute;
  top: 50%;
  left: calc(var(--trace-p) * 100%);
  width: 190px;
  height: 34px;
  translate: -100% -50%;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, currentColor 42%, transparent));
  filter: blur(7px);
  opacity: 0.85;
  pointer-events: none;
  will-change: left;
}

.cryo-rail .cryo-bulb {
  top: 50%;
  left: calc(var(--trace-p) * 100%);
  translate: -50% -50%;
  will-change: left;
}

/* ---------------------------------------------------------------------------
   CRYO COLUMN — the side instrument. Desktop, fine pointer only.

   Hidden by default so touch never receives it. The gate is horizontal room
   AND a hover-capable pointer, because a large touch tablet has the width but
   cannot show a hover label. 1181px pairs with the existing max-width:1180px
   breakpoint rather than inventing a new one. When it is absent the rail
   carries the whole idea on its own.
   -------------------------------------------------------------------------- */
.cryo-column { display: none; }

@media (min-width: 1181px) and (hover: hover) and (pointer: fine) {
  .cryo-column {
    position: fixed;
    z-index: 235;
    top: 16vh;
    right: 22px;
    display: block;
    width: 22px;
    height: 68vh;
    /* The housing is decorative; only the notches inside take pointer input. */
    pointer-events: none;
  }

  /* The gauge body: a recessed capsule with graduation marks ruled across it,
     which is what makes this read as an instrument scale rather than a rail. */
  .cryo-column__housing {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 7px;
    translate: -50% 0;
    border-radius: 4px;
    /* THE SAME DARK CHANNEL THE RAIL USES, and for the same reason its own
       comment gives: an ivory wash at 5% is invisible against a bright ground.
       The rail was built for the lit hero plate; the column was not, and once
       01 / THE PRINCIPLE became a blaze band the column ran its warm chapter
       colour over --signal at 1.7:1 and effectively vanished for the height of
       the section. A channel of its own makes the instrument independent of
       whatever it happens to be passing over. */
    background:
      repeating-linear-gradient(180deg, rgba(255, 247, 230, 0.14) 0 1px, transparent 1px 15px),
      rgba(6, 6, 10, 0.62);
    box-shadow:
      inset 0 0 0 1px rgba(255, 247, 230, 0.06),
      inset 1px 0 0 rgba(255, 247, 230, 0.05);
    overflow: hidden;
  }

  /* Same clip discipline as the rail, descending instead of advancing. */
  .cryo-column__fill {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, var(--thermal-stops));
    clip-path: inset(0 0 calc(100% - var(--trace-p) * 100%) 0);
    will-change: clip-path;
  }
  /* The graduations survive the fill, so the scale is legible along its lit
     length as well as its dark one. */
  .cryo-column__fill::after {
    content: "";
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(180deg, rgba(4, 2, 10, 0.28) 0 1px, transparent 1px 15px);
  }

  .cryo-column__bloom {
    position: absolute;
    top: calc(var(--trace-p) * 100%);
    left: 50%;
    width: 34px;
    height: 150px;
    translate: -50% -100%;
    background: linear-gradient(180deg, transparent, color-mix(in srgb, currentColor 38%, transparent));
    filter: blur(7px);
    opacity: 0.8;
    pointer-events: none;
    will-change: top;
  }

  .cryo-column .cryo-bulb {
    top: calc(var(--trace-p) * 100%);
    left: 50%;
    translate: -50% -50%;
    will-change: top;
  }

  /* --- chapter notches ---------------------------------------------------
     Real buttons. The tick reaches out of the gauge to the left so the target
     is generous without widening the instrument, and the label opens inward --
     never outward, which would push past the viewport edge. */
  .cryo-column__notch {
    position: absolute;
    top: var(--y, 0%);
    right: 0;
    display: flex;
    height: 26px;
    align-items: center;
    justify-content: flex-end;
    padding: 0;
    border: 0;
    background: none;
    /* NO `color: inherit` here. Every notch carries its own data-trace-chapter,
       so the temperature rules at the top of this file already give it its OWN
       colour -- inheriting would paint all ten notches in whichever chapter the
       reader happens to be in, and hovering "06 PINEAPPLE / DEEP COLD" would
       light up in the colour of some other band. Equal specificity, so this
       declaration would have won on source order; it is left out deliberately. */
    cursor: pointer;
    pointer-events: auto;
    translate: 0 -50%;
  }
  .cryo-column__tick {
    display: block;
    width: 11px;
    height: 2px;
    border-radius: 1px;
    background: rgba(255, 247, 230, 0.3);
    transition: width 220ms var(--ease-out), background-color 220ms var(--ease-out),
                box-shadow 220ms var(--ease-out);
  }
  .cryo-column__notch.is-current .cryo-column__tick,
  .cryo-column__notch:hover .cryo-column__tick,
  .cryo-column__notch:focus-visible .cryo-column__tick {
    width: 19px;
    background: currentColor;
    box-shadow: 0 0 10px currentColor;
  }

  /* Chapter number, name and thermal band. Absent until asked for. */
  .cryo-column__label {
    position: absolute;
    right: 26px;
    display: flex;
    gap: 0.55rem;
    align-items: baseline;
    padding: 0.32rem 0.6rem;
    border: 1px solid rgba(255, 247, 230, 0.12);
    border-radius: 0.2rem;
    background: rgba(6, 6, 9, 0.92);
    color: var(--moon);
    font-family: var(--font-ui);
    font-size: 0.55rem;
    letter-spacing: 0.18em;
    white-space: nowrap;
    text-transform: uppercase;
    opacity: 0;
    translate: 8px 0;
    pointer-events: none;
    transition: opacity 200ms var(--ease-out), translate 200ms var(--ease-out);
  }
  .cryo-column__label i {
    color: rgba(255, 247, 230, 0.4);
    font-style: normal;
    font-variant-numeric: tabular-nums;
  }
  /* The thermal band is the only place the temperature idea is ever spelled
     out in words, and it is a state name, never a measurement. */
  .cryo-column__label b {
    color: currentColor;
    font-size: 0.5rem;
    font-weight: 400;
    letter-spacing: 0.14em;
  }
  .cryo-column__notch:hover .cryo-column__label,
  .cryo-column__notch:focus-visible .cryo-column__label { opacity: 1; translate: 0 0; }
  .cryo-column__notch:focus-visible { outline: 1px solid var(--nectar); outline-offset: 5px; }
}

/* ---------------------------------------------------------------------------
   REDUCED MOTION — the instrument still reports, it stops performing.

   Position and temperature are INFORMATION and keep updating. What goes is the
   decoration: no colour sweep, no flare, no light spill, no travelling bloom.
   Navigation still works and jumps instantly rather than smooth-scrolling.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .trace,
  .cryo-rail,
  .cryo-column__tick,
  .cryo-column__label { transition: none; }
  .trace.is-igniting .cryo-bulb { animation: none; }
  .cryo-rail__bloom,
  .cryo-column__bloom { display: none; }
  .cryo-bulb { box-shadow: 0 0 0 1px rgba(4, 2, 10, 0.6); }
  .cryo-column__notch.is-current .cryo-column__tick,
  .cryo-column__notch:hover .cryo-column__tick,
  .cryo-column__notch:focus-visible .cryo-column__tick { box-shadow: none; }
}
