/* ==========================================================================
   THE FIGURES — dials, meters, stacked bars, and the switches.
   ==========================================================================

   Franklin, 2026-09-22: "much more engaging graphs and dials and toggles."
   ADVANCED ONLY, his call, so this file has exactly one unscoped rule — the
   one that hides every figure — and everything that draws is behind
   [data-skin="advanced"]. The markup is emitted on both looks; Original
   simply never paints it, which is why changing the skin in Settings redraws
   the screen with no reload and no re-render hook.
   scripts/test-figures.mjs fails if a second unscoped rule ever appears.

   WHY THE FIGURES BELONG TO THIS SKIN AND NOT TO THE APP. A neumorphic
   surface has one genuinely useful trick: an inset shadow draws a CHANNEL, and
   a channel is what a gauge actually is. Every track here is carved and every
   value rides in it, so a dial reads as an instrument set into the panel
   rather than a picture of one. On the flat theme the same figure would be two
   rectangles.

   THE COST COLOURS ARE MEASURED, NOT CHOSEN. Three hues, validated in each
   mode separately — a lightness band, a chroma floor, protanopia and
   deuteranopia separation, a normal-vision floor, and 3:1 against this skin's
   own base:

     slot        light      dark       what it is
     --nm-slot-a #2a7d4e    #3f9c68    labour
     --nm-slot-b #4066a8    #5c85cc    materials
     --nm-slot-c #8e610d    #bb8a22    overhead

   THE ORDER IS THE SAFETY MECHANISM. Green and amber beside each other
   collapse under protanopia (ΔE 5.3, against a floor of 8); with the blue
   between them the worst touching pair is 17.4 light and 16.3 dark. So the
   stack is always drawn a, b, c — never reordered to suit a screen.

   And no figure encodes anything in colour alone: every number is printed, and
   every status carries a word ("over", "under") beside the hue.
   ========================================================================== */

/* THE ONE UNSCOPED RULE. */
.nm-fig, .nm-figrow, .nm-figrow-note, .nm-meter, .nm-stack, .nm-bars, .nm-div { display: none; }

html[data-skin="advanced"] .nm-fig { display: flex; }
html[data-skin="advanced"] .nm-figrow { display: grid; }
html[data-skin="advanced"] .nm-figrow-note { display: block; }
html[data-skin="advanced"] .nm-meter,
html[data-skin="advanced"] .nm-stack,
html[data-skin="advanced"] .nm-bars,
html[data-skin="advanced"] .nm-div { display: block; }

html[data-skin="advanced"] {
  /* RE-DERIVED FOR RELIEF (2026-09-26). The old three were validated against a
     single #e6e2d6 base. Relief has three surfaces and the darkest a mark can
     land on is #d2ccba — the top of a carved channel, which is exactly where
     these bars sit. Against that, green fell to 2.65:1 and amber to 2.92:1,
     both under the 3:1 a mark needs. Each is one step darker and the trio was
     re-run through the full gate: lightness band, chroma floor, protanopia and
     deuteranopia separation, normal-vision floor, and 3:1 on that surface. */
  --nm-slot-a: #2a7d4e;
  --nm-slot-b: #4066a8;
  --nm-slot-c: #8e610d;
  /* The gap between touching segments is the PAGE showing through, which is
     what keeps two neighbouring fills apart without a border's extra ink. */
  --nm-gap: 2px;
  /* THE EMPTY PART OF A GAUGE, which is not the same colour as a shadow. In
     the light skin --nm-dark (#c2bdaf on #e6e2d6) is exactly the quiet step
     off the surface a track wants. In the dark skin it is #0d110d on #232b24 —
     near-black, and at 9px thick it stops reading as a channel cut into the
     card and starts reading as a drawn ring louder than the data inside it.
     So the track is its own token, and dark picks its own value. */
  --nm-track: var(--nm-dark);
}
html[data-mode="dark"][data-skin="advanced"] { --nm-track: #3a453b; }

/* ==========================================================================
   DIAL
   ========================================================================== */
html[data-skin="advanced"] .nm-fig {
  margin: 0;
  align-items: center;
  gap: var(--space-4);
}
html[data-skin="advanced"] .nm-fig svg { width: 76px; height: 76px; flex: none; }
/* The track is the channel the value sits in. Carved, so it reads as cut into
   the card rather than drawn on it. */
html[data-skin="advanced"] .nm-dial-track {
  fill: none;
  stroke: var(--nm-track);
  stroke-width: 9;
  stroke-linecap: round;
}
html[data-skin="advanced"] .nm-dial-fill {
  fill: none;
  stroke: var(--accent-ink);
  stroke-width: 9;
  stroke-linecap: round;
}
html[data-skin="advanced"] .nm-dial.is-good .nm-dial-fill { stroke: var(--ok); }
html[data-skin="advanced"] .nm-dial.is-bad .nm-dial-fill { stroke: var(--danger); }
html[data-skin="advanced"] .nm-fig figcaption { display: block; min-width: 0; }
/* Proportional figures, not tabular: at this size equal-width digits make a
   number like 121 look gappy, and nothing here has to line up in a column. */
html[data-skin="advanced"] .nm-fig-v {
  display: block;
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--ink);
}
html[data-skin="advanced"] .nm-fig-k {
  display: block;
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 2px;
}
/* The scale the arc is drawn to. Quiet, but present — see the note on `max` in
   js/figures.js for why an unstated range is the thing that makes a dial lie. */
/* Said ONCE under the row rather than three times inside it. */
html[data-skin="advanced"] .nm-figrow-note {
  margin: calc(var(--space-2) * -1) 0 0;
  font-size: 0.8125rem;
  color: var(--muted);
}
html[data-skin="advanced"] .nm-fig-n {
  display: block;
  font-size: 0.8125rem;
  color: var(--muted);
  margin-top: 2px;
}

/* A row of dials. auto-fit rather than three fixed columns: on a tablet in
   portrait these wrap to two and then to one, and a dial that has to shrink to
   fit a third of a phone is a dial nobody can read. */
html[data-skin="advanced"] .nm-figrow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-4) var(--space-6);
  margin-block-end: var(--nm-clear);
}

/* ==========================================================================
   METER — a ratio against a limit, with the limit MARKED.
   ========================================================================== */
html[data-skin="advanced"] .nm-meter { margin-block: var(--nm-clear-sm); }
html[data-skin="advanced"] .nm-meter-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: 5px;
}
html[data-skin="advanced"] .nm-meter-k { color: var(--muted); font-size: 0.875rem; }
html[data-skin="advanced"] .nm-meter-v { color: var(--ink); font-weight: 600; font-size: 0.9375rem; }
html[data-skin="advanced"] .nm-meter-track {
  position: relative;
  height: 14px;
  border-radius: 999px;
  background: var(--nm-sunk);
  box-shadow: var(--nm-in-sm);
  overflow: hidden;
}
html[data-skin="advanced"] .nm-meter-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 1px 1px 3px rgba(19, 32, 24, 0.22);
}
html[data-skin="advanced"] .nm-meter.is-over .nm-meter-fill { background: var(--danger); }
/* Where the budget was. A bar past this line is over, and the WORD "over" is
   printed beside the number — the line is the picture, not the proof. */
html[data-skin="advanced"] .nm-meter-limit {
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  background: var(--ink);
  opacity: 0.55;
}
html[data-skin="advanced"] .nm-meter-n {
  margin: 5px 0 0;
  font-size: 0.8125rem;
  color: var(--muted);
}

/* ==========================================================================
   STACK — part-to-whole on a track whose full width is the total.
   ========================================================================== */
html[data-skin="advanced"] .nm-stack { margin-block: var(--nm-clear); }
html[data-skin="advanced"] .nm-stack-k,
html[data-skin="advanced"] .nm-div .nm-stack-k {
  margin: 0 0 var(--space-2);
  font-size: 0.875rem;
  color: var(--muted);
}
html[data-skin="advanced"] .nm-stack-track {
  position: relative;
  display: flex;
  gap: var(--nm-gap);
  height: 22px;
  border-radius: 999px;
  background: var(--nm-sunk);
  box-shadow: var(--nm-in-sm);
  padding: 0;
  overflow: hidden;
}
html[data-skin="advanced"] .nm-stack-seg { display: block; height: 100%; min-width: 0; }
html[data-skin="advanced"] .nm-stack-limit {
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  background: var(--ink);
  opacity: 0.55;
}
html[data-skin="advanced"] .nm-slot-a { background: var(--nm-slot-a); }
html[data-skin="advanced"] .nm-slot-b { background: var(--nm-slot-b); }
html[data-skin="advanced"] .nm-slot-c { background: var(--nm-slot-c); }
html[data-skin="advanced"] .nm-slot-rest { background: var(--nm-track); }

/* The legend. Present whenever there is more than one series — the dependable
   identity channel, so nobody has to match a colour to a memory. The value
   rides beside its own key, which is also the table view: no number in any
   figure here is reachable only by hovering it. */
html[data-skin="advanced"] .nm-keys {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
}
html[data-skin="advanced"] .nm-key {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 0.875rem;
}
html[data-skin="advanced"] .nm-key-dot {
  width: 10px; height: 10px;
  border-radius: 3px;
  flex: none;
  transform: translateY(1px);
}
/* Text wears text tokens. A mark beside the words carries the identity — a
   label painted in the series colour is a label nobody can read. */
html[data-skin="advanced"] .nm-key-k { color: var(--muted); }
html[data-skin="advanced"] .nm-key-v { color: var(--ink); font-weight: 600; }
html[data-skin="advanced"] .nm-stack.is-over .nm-key-rest .nm-key-v { color: var(--danger); }

/* ==========================================================================
   BARS — magnitude down a list, optionally a part inside its whole.
   ========================================================================== */
html[data-skin="advanced"] .nm-bars { margin-block: var(--nm-clear); }
html[data-skin="advanced"] .nm-bar-rows { list-style: none; margin: 0; padding: 0; }
html[data-skin="advanced"] .nm-bar-row {
  display: grid;
  grid-template-columns: minmax(6rem, 11rem) 1fr minmax(5rem, auto);
  align-items: center;
  gap: var(--space-3);
  padding: 3px 0;
}
html[data-skin="advanced"] .nm-bar-k {
  color: var(--muted);
  font-size: 0.875rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html[data-skin="advanced"] .nm-bar-plot {
  position: relative;
  height: 16px;
  border-radius: 999px;
  background: var(--nm-sunk);
  box-shadow: var(--nm-in-sm);
}
/* The WHOLE the bar came out of — what was quoted, every estimate at that size.
   It is context rather than data, so it is the track's own colour a step up
   from the channel, never a second hue competing with the value inside it. */
html[data-skin="advanced"] .nm-bar-of,
html[data-skin="advanced"] .nm-slot-of {
  position: absolute;
  left: 0; top: 3px; bottom: 3px;
  border-radius: 999px;
  background: var(--nm-track);
}
html[data-skin="advanced"] .nm-slot-of { position: static; }
html[data-skin="advanced"] .nm-bar-v {
  position: absolute;
  left: 0; top: 3px; bottom: 3px;
  border-radius: 999px;
  background: var(--nm-slot-a);
}
html[data-skin="advanced"] .nm-bar-t {
  text-align: right;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   DIVERGING — above or below a line, worst first.
   ========================================================================== */
html[data-skin="advanced"] .nm-div { margin-block: var(--nm-clear); }
html[data-skin="advanced"] .nm-div-rows { list-style: none; margin: 0; padding: 0; }
html[data-skin="advanced"] .nm-div-row {
  display: grid;
  grid-template-columns: minmax(6rem, 11rem) 1fr minmax(5rem, auto);
  align-items: center;
  gap: var(--space-3);
  padding: 3px 0;
}
html[data-skin="advanced"] .nm-div-k {
  color: var(--muted);
  font-size: 0.875rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The channel, with the zero rule cut down its middle. */
html[data-skin="advanced"] .nm-div-plot {
  position: relative;
  height: 16px;
  border-radius: 999px;
  background: var(--nm-sunk);
  box-shadow: var(--nm-in-sm);
}
/* THE ZERO RULE, and it has to be visible or the chart has no grammar: the
   caption says "left of the line", so there had better be a line. --line is a
   hairline meant to divide table rows and disappeared into the carved channel
   entirely; this is the control edge, which is measured at 4.4:1 on the base. */
html[data-skin="advanced"] .nm-div-plot::before {
  content: '';
  position: absolute;
  left: calc(50% - 1px); top: 0; bottom: 0;
  width: 2px;
  background: var(--nm-edge);
  border-radius: 1px;
  z-index: 1;
}
html[data-skin="advanced"] .nm-div-bar {
  position: absolute;
  top: 3px; bottom: 3px;
  border-radius: 999px;
  background: var(--ok);
}
html[data-skin="advanced"] .nm-div-row.is-bad .nm-div-bar { background: var(--danger); }
html[data-skin="advanced"] .nm-div-v {
  text-align: right;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink);
  /* A column of numbers DOES line up, so this is where tabular figures belong. */
  font-variant-numeric: tabular-nums;
}
html[data-skin="advanced"] .nm-tag {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: 999px;
  vertical-align: 1px;
}
html[data-skin="advanced"] .nm-tag-bad { background: var(--wash-danger); color: var(--danger); }

/* A phone has no room for a name, a plot and a number on one line. */
@media (max-width: 40rem) {
  html[data-skin="advanced"] .nm-div-row {
    grid-template-columns: 1fr auto;
    grid-template-areas: 'k v' 'p p';
    row-gap: 2px;
  }
  html[data-skin="advanced"] .nm-div-k { grid-area: k; }
  html[data-skin="advanced"] .nm-div-v { grid-area: v; }
  html[data-skin="advanced"] .nm-div-plot { grid-area: p; }
  html[data-skin="advanced"] .nm-bar-row {
    grid-template-columns: 1fr auto;
    grid-template-areas: 'k v' 'p p';
    row-gap: 2px;
  }
  html[data-skin="advanced"] .nm-bar-k { grid-area: k; }
  html[data-skin="advanced"] .nm-bar-t { grid-area: v; }
  html[data-skin="advanced"] .nm-bar-plot { grid-area: p; }
  html[data-skin="advanced"] .nm-fig { gap: var(--space-3); }
  html[data-skin="advanced"] .nm-fig svg { width: 60px; height: 60px; }
}

/* ==========================================================================
   THE ESCAPE HATCHES, the same two the skin itself keeps.
   ========================================================================== */

/* Somebody who asked their OS for more contrast has told us the soft channel
   does not work for them: the carved tracks become outlined ones. */
@media (prefers-contrast: more) {
  html[data-skin="advanced"] .nm-meter-track,
  html[data-skin="advanced"] .nm-stack-track,
  html[data-skin="advanced"] .nm-bar-plot,
  html[data-skin="advanced"] .nm-div-plot {
    box-shadow: none;
    border: 1px solid var(--nm-edge);
  }
  html[data-skin="advanced"] .nm-dial-track { stroke: var(--nm-edge); }
}

/* On paper a carved channel is a grey smear. The bars stay — they are the
   information — and the channel becomes a rule. */
@media print {
  html[data-skin="advanced"] .nm-meter-track,
  html[data-skin="advanced"] .nm-stack-track,
  html[data-skin="advanced"] .nm-bar-plot,
  html[data-skin="advanced"] .nm-div-plot {
    box-shadow: none;
    border: 1px solid #999;
  }
  html[data-skin="advanced"] .nm-dial-track { stroke: #ccc; }
}
