/* ==========================================================================
   TEMassist — THE INSTRUMENT SKIN
   The signature components.  This is the file that makes the site read as an
   instrument rather than a funnel, and it is the layer the product's own
   brief does NOT share (blueprint: tokens verbatim, skin the site's).

   Contains, in build order (CD §16):
     1. the instrument frame      — the one recurring layout idea
     2. the depth stack           — L0 sky / L1 weather / L2 foreground
     3. the severity spine        — four ranks + the non-severity gap
     4. buttons & the nav
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. THE INSTRUMENT FRAME (CD §9)
   Hairline border, corner ticks, a mono micro-label in the top-left.  Every
   framed panel looks like it is READ FROM AN INSTRUMENT.  Use wherever
   structure is needed; never for decoration.
   -------------------------------------------------------------------------- */

.frame {
  position: relative;
  border: 1px solid var(--line);
  padding: var(--sp-6);
  background: transparent;
}

/* Corner ticks — machined instrument metal (CD §8).  Drawn with two
   gradients per pseudo-element so all four corners come from two boxes. */
.frame::before,
.frame::after {
  content: "";
  position: absolute;
  width: 10px; height: 10px;
  pointer-events: none;
}
.frame::before {
  top: -1px; left: -1px;
  border-top: 1px solid var(--ink);
  border-left: 1px solid var(--ink);
  opacity: .45;
}
.frame::after {
  bottom: -1px; right: -1px;
  border-bottom: 1px solid var(--ink);
  border-right: 1px solid var(--ink);
  opacity: .45;
}

/* The micro-label sits ON the top-left rule, the way a panel is stencilled. */
.frame__label {
  position: absolute;
  top: 0;
  left: var(--sp-5);
  transform: translateY(-50%);
  background: var(--paper);
  padding-inline: var(--sp-3);
  font-family: var(--mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   2. THE DEPTH STACK (CD §10, blueprint §The depth stack)
   Three layers, three parallax rates (0.04 / 0.09 / 1.0).  Depth is REAL —
   light and value, not a fake box-shadow.  One light source, always the
   same side, so the page reads as one lit space.
   -------------------------------------------------------------------------- */

.depth { position: relative; isolation: isolate; }

.layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

/* ⛔ L0 · SKY — A REAL PLATE NOW, NOT EIGHT RADIAL GRADIENTS. Captain-shot in Midjourney and
   processed to the direction's own numbers before it landed (2026-09-11):

     * **one light source, top-left** — the source carried a spurious brightening in the BOTTOM-left
       (measured L=29.4 against the top-left's 9.0), which reads as light from below in a frame
       whose whole premise is "no horizon, nothing below". A smooth vertical falloff removed it
       without touching the cirrus band, where the light legitimately is. Now 15.2 against 9.0.
     * **no star brighter than `--ink` at 35%** — `--ink` is #e6edf3 (L≈236), so the ceiling is 83.
       A few pixels reached **206**: one bright hero star is the thing the brief rejects by name,
       and on a page that reserves "brightest" for a CRITICAL row it would be a lie in the one
       channel that carries meaning. Soft-clipped to a max of 79, so a clipped star stays a star
       rather than becoming a flat disc.
     * **≤5% of the frame brighter than the ground** — 14.15% before, **4.85%** after.
     * **21 KB**, against a 200 KB budget, at 1456px. Not upscaled to hit a number: downscaling is
       free and upscaling is not, and a near-black atmosphere layer has nothing to resolve.

   ⚠ WHAT IT REPLACED, AND WHY THAT WAS NOT OBVIOUS. The CSS starfield cost zero bytes, drifted,
   and scaled to any viewport — a real plate has to BEAT that, not merely be photographic, which is
   why the swap was offered as optional rather than assumed. It wins because it has a cirrus band
   with structure, and eight radial gradients cannot have one. */
.layer--sky {
  background-image: url("../img/sky.webp");
  background-size: cover;
  background-position: center;
  animation: sky-drift var(--t-drift-sky) linear infinite;
}

/* ⛔ THE HORIZON — THE ONE PLACE A WARM HUE EXISTS ON THIS SITE, AND IT IS ON PROBATION.
   Captain-ruled 2026-09-11: a faint light line below the route, from a real high-altitude dusk
   shot. He is right that it reads as ALTITUDE — a band of light at the bottom edge says "you are
   above this" in a way a full-bleed texture cannot.

   ⚠ AND IT ARGUES WITH THE RULE IT SITS UNDER, WHICH IS WHY THE NUMBERS ARE HERE. CD §7 reserves
   warmth for meaning — `--signal` and `--high` mark the one thing that needs attention — and Act
   III's brightest object is a HIGH SIGMET in `--high`. A dawn band is warm, so the risk is
   measurable and specific: if the horizon out-glows the threat, the eye goes to the sky instead
   of to the weather, and the dark-cockpit law is broken by decoration.

   ⛔ SO IT WAS MEASURED RATHER THAN JUDGED, and these are the numbers to re-check if anyone
   retunes it. The SIGMET core measures **mean L 77 / p99 L 106** on screen. The source photo
   cropped to its glow measured p99 **85.8**, which is the same order — too close. Dimmed ×0.52 it
   is **p99 44.1, max 76.8**: the horizon's BRIGHTEST pixel only reaches the SIGMET's AVERAGE, so
   the threat stays the brightest object in the act by a clear margin, at every width.

   ⛔ REPRODUCE IT WITH THE COMMAND, NOT BY EYE:

       python3 tools/make_atmosphere_plate.py --mode horizon --src <frame> --ceiling 44

   The tool's own default is 50 and **44 is deliberate here**: the captain approved a specific
   render, and regenerating at the default made the band measurably brighter than the one he
   looked at (background under the worst text line rgb(51,20,2) against the approved rgb(29,7,1)).
   That is movement toward the exact thing he said he was watching for — *"if colours start to
   blend in, I will change"* — so the approved value is the one that ships. A tool default is not
   a ruling.

   It is also placed where nothing competes: the bottom edge, under the route, outside the map's
   own frame — and feathered to transparent at the top so it meets the sky plate with no seam.
   6.6 KB. Captain's own photograph, and the reason it beats the gradient I first wrote is the
   same reason the sky plate beat eight radial gradients: there is real cloud structure along that
   horizon, and a ramp cannot have any.

   ⚠ AND THE SECOND RISK WAS MEASURED AND DID NOT MATERIALISE, which is worth recording so nobody
   re-opens it on a hunch. The act's bottom screen puts the phase timeline OVER the band, so the
   question was whether text survives a warm ground. Median background under the worst line —
   "Clear · KLAX wx ok" in `--low` green, sitting on the brightest part — is rgb(29, 7, 1), and
   every text colour on that ground measures **6.2 to 7.7:1** against an AA floor of 4.5. The band
   is bright in the CENTRE of the frame, which is exactly where the layout has no text. */
.layer--horizon {
  background-image: url("../img/horizon.webp");
  background-repeat: no-repeat;
  background-position: center bottom;
  /* Full width, and a FIXED SLICE of the act's height — not `cover`, which would scale the band
     with the viewport and make the horizon thicker on a tall screen. A horizon does not get
     taller; it stays a line and the sky above it grows. */
  background-size: 100% 34%;
}

/* The vertical light falloff — low-key, single-source (CD §7.4). */
.layer--falloff {
  background:
    radial-gradient(120% 80% at 50% -10%,
      color-mix(in srgb, var(--ink) 6%, transparent) 0%,
      transparent 60%),
    linear-gradient(to bottom,
      transparent 0%,
      color-mix(in srgb, var(--ink) 4%, transparent) 100%);
}

/* L1 · WEATHER — ONE soft, heavily blurred band suggesting a system.
   Abstract.  Never a literal cloud photograph. */
.layer--weather::before {
  content: "";
  position: absolute;
  width: 62%;
  aspect-ratio: 2 / 1;
  left: 8%;
  top: 40%;
  border-radius: 50%;
  background:
    radial-gradient(closest-side, var(--band-a), transparent 72%),
    radial-gradient(closest-side, var(--band-b), transparent 68%);
  filter: blur(58px);
  animation: band-drift var(--t-drift-band) ease-in-out infinite alternate;
}

/* ⛔ THE ATMOSPHERE IS NIGHT-ONLY, AND BY DAY THERE IS NONE OF IT. CD v2 §7.2: **"Day is flat.
   No radial gradients, no washes."** These three layers — the starfield, the light falloff and
   the weather band — are the site's sky (CD §8 allows haze in Acts 0 and III, and nowhere else),
   and the sky belongs to the night. Rendered over parchment they became a soft grey wash across
   the hero: the first tell on CD §15's list of generated-page looks, on the first screen a
   visitor sees.

   Written as three explicit gates rather than one wrapper, because the site has THREE theme
   states, not two: an explicit stamp either way, and the un-stamped default where only
   `prefers-color-scheme` separates them. A layer painted in the un-stamped light state is the
   classic unreadable-artifact bug with the colours swapped.

   ⚠ AND THIS IS THE SLOT THE MIDJOURNEY PLATES DROP INTO (`docs/creative/HANDOFF_midjourney_
   plates.md`). They replace the CSS starfield and the blurred band with real plates, night-only,
   for exactly this reason — the v1 render already proved a plate washes parchment to grey. The
   gate lands before the images so the images land into a page that is already correct. */
/* ⛔ THE ISOBAR FIELD WAS TRIED IN ACT III AND RULED OUT — captain, 2026-09-11: the render
   "looks horrible", and a night sky with faint stars is the better answer. He is right on the
   drawing as well as the taste: Act III ALREADY has a line system, `.route__graticule`, so a
   second one put two grids behind one map and the route had to compete with both. The generator
   survives at `tools/make_isobar_field.py` — it is standalone and documents why a diffusion model
   cannot draw a contour — but nothing on the page calls it, and the asset is deleted rather than
   left orphaned. */

.layer--sky, .layer--falloff, .layer--weather, .layer--horizon { display: none; }
:root[data-theme="night"] .layer--sky,
:root[data-theme="night"] .layer--falloff,
:root[data-theme="night"] .layer--weather,
:root[data-theme="night"] .layer--horizon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="day"]) .layer--sky,
  :root:not([data-theme="day"]) .layer--falloff,
  :root:not([data-theme="day"]) .layer--weather,
  :root:not([data-theme="day"]) .layer--horizon { display: block; }
}

/* L2 — content sits above by stacking context, not by z-index soup. */
.layer--front { position: relative; z-index: 1; }

@keyframes sky-drift {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-340px, -120px, 0); }
}
@keyframes band-drift {
  from { transform: translate3d(-4%, 0, 0) scale(1); }
  to   { transform: translate3d(6%, -3%, 0) scale(1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .layer--sky, .layer--weather::before { animation: none !important; }
}

/* --------------------------------------------------------------------------
   3. THE SEVERITY SPINE (blueprint §The severity spine, Component 3)
   Four ranks — critical / high / caution / clear — in the product's own
   order (_SEV_RANK), plus a FIFTH, NON-SEVERITY signal: the gap.

   ⛔ --accent is NEVER a threat rank.  It is the honest-degradation
   colour — "we don't have this".  That invariant is what makes the
   product trustworthy, rendered in one hue.

   Colour is never the only carrier: every rank ships a GLYPH and a WORD
   as well (CD §7.1, §14).
   -------------------------------------------------------------------------- */

.spine { display: grid; gap: 0; }

.spine__row {
  display: grid;
  grid-template-columns: 1.25rem 6.5rem minmax(0, 1fr);   /* see brief.css .row — same rule */
  align-items: baseline;
  gap: var(--sp-4);
  padding-block: var(--sp-4);
  border-top: 1px solid var(--hairline);
}
.spine__row:last-child { border-bottom: 1px solid var(--hairline); }

.spine__glyph {
  font-family: var(--mono);
  font-size: var(--fs-data);
  line-height: 1;
  text-align: center;
}
.spine__rank {
  font-family: var(--mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
}
.spine__reads { color: var(--muted); font-family: var(--serif); }

/* The five colours.  One class per meaning — used identically here, on the
   brief card, and on the map, so a pin and a row always name the same thing. */
/* TWO CHANNELS PER RANK, because a bar and a word need different floors.
     --rank       the product's token, verbatim. Fills, stripes, bars, marks — 3:1 territory,
                  and every rank passes it on both grounds.
     --rank-text  the same hue at a contrast a READER can read. Identical to --rank at night
                  and for critical/clear in day; darkened for --high and --medium on parchment,
                  where the true tokens measure 3.59:1 and 2.82:1 as text.
   `color` takes the text channel so every rank WORD is AA; anything painting a shape reaches
   for var(--rank) explicitly. */
.sev-critical { --rank: var(--critical); --rank-text: var(--critical-text); }
.sev-high     { --rank: var(--high);     --rank-text: var(--high-text); }
.sev-caution  { --rank: var(--medium);   --rank-text: var(--medium-text); }
.sev-clear    { --rank: var(--low);      --rank-text: var(--low-text); }
.sev-gap      { --rank: var(--accent);   --rank-text: var(--accent); }

.sev-critical, .sev-high, .sev-caution, .sev-clear, .sev-gap { color: var(--rank-text); }

/* --------------------------------------------------------------------------
   4. BUTTONS — an invitation, not a funnel (CD §12)
   The CTA verb is the product's own.  No gradient, no bounce, no arrow that
   slides.  The underline draws; that is the whole gesture.
   -------------------------------------------------------------------------- */

.cta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--mono);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  text-decoration: none;
  color: var(--ink);
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--ink);
  background: transparent;
  transition: background var(--t-hover) var(--ease),
              color var(--t-hover) var(--ease),
              border-color var(--t-hover) var(--ease);
}
.cta:hover { background: var(--ink); color: var(--paper); }

/* The Navigraph mark on the sign-in CTAs. `.cta` is already an outline control on a transparent
   ground, so the mark needs sizing and nothing else — it keeps its own colours through the hover
   inversion, which is correct: a logo is not themed furniture.
   Obligation and placement: docs/vendor/NAVIGRAPH_DISPLAY_CONTRACT.md */
.cta--ng .ng-mark { flex: 0 0 auto; display: block; width: 18px; height: 18px; }

.cta--ghost {
  border: 0;
  padding-inline: 0;
  color: var(--muted);
  position: relative;
}
.cta--ghost::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1px;
  background: var(--signal);
  transform: scaleX(0);
  transform-origin: left;
  /* A hover, so it takes the hover cap — it was on --t-std (700ms), 4.7x the budget. */
  transition: transform var(--t-hover) var(--ease);
}
.cta--ghost:hover { background: transparent; color: var(--ink); }
.cta--ghost:hover::after { transform: scaleX(1); }

/* --------------------------------------------------------------------------
   NAV (Component 1) — sticky, transparent, mono.  Condenses on scroll and a
   hairline appears.  No logo image: the wordmark is type (open decision 5).
   -------------------------------------------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: saturate(1.1) blur(8px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--t-micro) var(--ease),
              padding var(--t-micro) var(--ease);
}
.nav.is-condensed { border-bottom-color: var(--hairline); }

.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding-block: var(--sp-4);
  transition: padding var(--t-micro) var(--ease);
}
.nav.is-condensed .nav__inner { padding-block: var(--sp-3); }

.nav__mark {
  font-family: var(--serif);
  font-size: 1.125rem;
  letter-spacing: -.01em;
  text-decoration: none;
  margin-right: auto;
}

.nav__links { display: flex; gap: var(--sp-5); }
.nav__links a {
  font-family: var(--mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--muted);
  text-decoration: none;
  transition: color var(--t-hover) var(--ease);
}
.nav__links a:hover { color: var(--ink); }

/* ⚠ THE ANCHORS ARE NOT HIDDEN ON SMALL SCREENS. They used to be `display:none` below
   56rem with nothing in their place, so a phone reader had a wordmark, a status dot and
   no way to reach any section of the page. They scroll horizontally instead — the whole
   nav stays reachable, and the row keeps the instrument register. */
@media (max-width: 56rem) {
  .nav__inner { flex-wrap: wrap; row-gap: var(--sp-3); }
  .nav__links {
    order: 3;
    width: 100%;
    overflow-x: auto;
    gap: var(--sp-4);
    padding-bottom: var(--sp-1);
    scrollbar-width: none;
  }
  .nav__links::-webkit-scrollbar { display: none; }
  .nav__links a { white-space: nowrap; }
  .nav__mark { margin-right: auto; }
}

/* ⛔ THE "BRIEFING READY" DOT IS RETIRED (EA v2 §2), AND ITS RULES GO WITH IT — a dead selector
   is the next session's ghost. It was a green dot pulsing in the nav of a page where no brief
   exists and no reader is signed in: a state indicator indicating nothing, which is the opposite
   of the dark-cockpit law this whole skin is built on (nothing is lit unless it needs attention).
   The markup is gone from index.html in the same commit — a rule and its element leave together
   or one of them becomes a mystery. */

/* The sample page says what it is where the dot used to claim what it was not. */
.nav__sample { color: var(--muted); }

/* Theme control — mono, instrument register.  Not in the blueprint's nav
   spec; added so both palettes can be reviewed, and because "night is the
   showcase but never forced" needs a way to say so. */
/* Same rule as the surface control: a toggle that cannot toggle is not shown. */
.themectl { display: none; }

.has-js .themectl {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  padding: var(--sp-2) var(--sp-3);
  cursor: pointer;
  transition: color var(--t-hover) var(--ease),
              border-color var(--t-hover) var(--ease);
}
.themectl:hover { color: var(--ink); border-color: var(--ink); }

@media (max-width: 44rem) { .has-js .themectl { display: none; } }

/* The nav's CTA (Component 1: wordmark + anchors + dot + CTA). Tighter than the page CTAs
   so it reads as chrome rather than competing with Act 0's invitation. */
.cta--nav { padding: var(--sp-2) var(--sp-4); }
@media (max-width: 44rem) { .cta--nav { padding: var(--sp-2) var(--sp-3); } }


/* --------------------------------------------------------------------------
   SHARED COMPONENTS — used by BOTH pages, so they cannot live in acts.css.
   Moved here 2026-09-07: brief.html loads tokens/base/instrument/brief/surface
   and NOT acts.css, so its footer and its "not shown on map" panel — the
   honest-degradation showpiece the blueprint says must read "as prominently as
   the map itself" — were rendering with no styling at all. A component used by
   two pages belongs in the shared file, not in the one named after one page.
   -------------------------------------------------------------------------- */

/* The "not shown on map" panel — styled as prominently as the map itself.
   Honest degradation as a feature, not a footnote. */
.notshown { margin-top: var(--sp-6); }
.notshown__item {
  font-family: var(--mono);
  font-size: var(--fs-small);
  color: var(--accent);
  line-height: 1.6;
  padding-block: var(--sp-2);
}
.notshown__note {
  margin-top: var(--sp-3);
  font-family: var(--mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--faint);
}

/* --- footer --------------------------------------------------------------- */

.foot { padding-block: var(--sp-7) var(--sp-8); border-top: 1px solid var(--hairline); }

.foot__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-5);
}

.foot__mark { font-family: var(--serif); font-size: 1.125rem; }

.foot__tag {
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--muted);
  max-width: 34rem;
  line-height: 1.6;
}

.foot__links { margin-left: auto; display: flex; gap: var(--sp-4); }
.foot__links a {
  font-family: var(--mono);
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--muted);
  text-decoration: none;
}
.foot__links a:hover { color: var(--ink); }

/* ⛔ THE VENDOR LINKBACKS, AND THEY ARE AN OBLIGATION RATHER THAN A CREDIT LINE.
   Navigraph asked for linkbacks to navigraph.com and simbrief.com and we wrote back committing to
   them; docs/vendor/NAVIGRAPH_DISPLAY_CONTRACT.md carries the quote. Styled like the footer's own
   quiet furniture on purpose — this is attribution, not a partner badge. */
.foot__credit {
  width: 100%;
  margin-top: var(--sp-5);
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--muted);
}
.foot__credit a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
.foot__credit a:hover { color: var(--ink); }

.foot__sig {
  width: 100%;
  margin-top: var(--sp-5);
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--faint);
}
