/* Skip-to-content link */
.skip-link {
  position: fixed;
  top: -100%;
  left: var(--space-m, 1rem);
  z-index: 9999;
  padding: 0.5rem 1rem;
  background: var(--c-blue, #045cfa);
  color: #fff;
  font-family: var(--f-display, sans-serif);
  font-size: 0.875rem;
  font-weight: 600;
  border-radius: 2px;
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus {
  top: var(--space-m, 1rem);
  outline: 2px solid #fff;
  outline-offset: 2px;
}

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  background: var(--c-canvas); color: var(--c-text);
  font-family: var(--f-body); font-size: var(--fs-step-0); line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
h1,h2,h3 { font-family: var(--f-display); line-height: 1.05; font-weight: 700; }
a { color: inherit; text-decoration: none; }
img, video { display: block; max-width: 100%; height: auto; }
.container { max-width: var(--content-max); margin-inline: auto; padding-inline: var(--space-m); }
/* Anchor targets (rail/nav #section jumps) clear the sticky header */
:is(section, main)[id] { scroll-margin-top: var(--anchor-offset); }
.mono-label { font-family: var(--f-mono); font-size: var(--fs-step--1); letter-spacing: .12em; text-transform: uppercase; color: var(--c-blue-text); }
/* Terminal-type caret while a [data-type] label types itself in (mono-type.js) */
.mono-label.is-typing::after { content: "▍"; margin-left: 1px; color: var(--c-blue); animation: mono-caret 1s steps(1) infinite; }
@keyframes mono-caret { 50% { opacity: 0; } }
/* CSS-animation kill-switch: gated on the ABSENCE of html.motion-ready (the JS gate, which
   honors the in-UI toggle), NEVER on the raw media query. The old @media !important rule
   silently nuked every CSS animation on machines whose OS reports reduced-motion even with
   the toggle forcing motion ON (found live 2026-08-01: computed animation-duration 1e-06s —
   the invisible teaser hover-draws and static marching dashes were exactly this). Same class
   of defect as the marquee gap; the gotcha now applies to the entire CSS layer. */
html:not(.motion-ready) .mono-label.is-typing::after { display: none; animation: none; }
html:not(.motion-ready) *,
html:not(.motion-ready) *::before,
html:not(.motion-ready) *::after {
  animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important;
}
/* [data-rv] hidden/reveal states live in motion.css, gated on html.motion-ready — an earlier
   UNCONDITIONAL copy here made every data-rv section invisible for no-JS visitors (motion.css's
   own comment documents the intended no-JS/SSR-visible design). Removed 2026-07-15. */
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
/* Programmatic focus targets (e.g. Blazor FocusOnNavigate's h1, tabindex=-1) aren't keyboard-tabbable, so they need no focus ring; keyboard :focus-visible elsewhere is unaffected. */
[tabindex="-1"]:focus { outline: none; }

/* View Transitions: snappy cross-fade; disabled under reduced-motion */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.28s; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* ── Atmosphere layer (atmosphere.js) ───────────────────────────────────────────────────────
   [data-atmo] sections get an injected ambient canvas. The canvas is absolutely positioned so
   it never participates in grid/flex flow; content is lifted above it. Canvas exists only when
   motion is on — without JS or with reduced motion the section renders exactly as before. */
[data-atmo] { position: relative; }
.atmo-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; }
[data-atmo] > :not(.atmo-canvas) { position: relative; z-index: 1; }

/* ── AME.Blazor.Controls token bridge ───────────────────────────────────────────────────────
   The controls' stylesheet defines --ame-* as var(--rz-*, <light fallback>): the --rz-* layer
   is the theming seam and NOTHING else defines it, so mapping it onto the site's own dark
   tokens themes every control (and the JS chart engine, which reads the computed values)
   without AMEThemeProvider. Semantics per the controls' dark palette: base-50 = page bg,
   base-100 = raised surface (lighter than the page in dark themes). */
:root {
  --rz-base-50: var(--c-canvas);
  --rz-base-100: var(--c-surface);
  --rz-base-200: var(--c-line);
  --rz-border: var(--c-line);
  --rz-text-color: var(--c-text);
  --rz-text-tertiary-color: var(--c-muted);
  --rz-text-disabled-color: var(--c-muted);
  --rz-primary: var(--c-blue);
  --rz-secondary: var(--c-muted);
  --rz-success: var(--c-brand-green-text);
  --rz-warning: var(--c-amber);
  --rz-danger: #FF7A6B;
  --rz-info: var(--c-blue);
}

/* ── Dew-point calculator (WASM island) ─────────────────────────────────────────────────────
   Global, not scoped: the component lives in the client project, so it carries no server-side
   scope attribute. Visual language mirrors the AME Portal page's panels. --c-gas is the
   product accent (brand green); redeclared here because .portal's scope does not reach a
   client-rendered subtree. */
.calc { --c-gas: var(--c-brand-green-text); margin-top: var(--space-m); }
.calc-loading { font-family: var(--f-mono); font-size: var(--fs-step--1); color: var(--c-muted); margin-bottom: var(--space-s); }
.calc-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-m); }
@media (min-width: 900px) { .calc-grid { grid-template-columns: 1.1fr 1fr 1fr; } }

.calc-panel {
  border: 1px solid var(--c-line); border-radius: 6px; background: var(--c-surface);
  padding: var(--space-m); display: flex; flex-direction: column; gap: .6rem;
}
.calc-panel-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1rem; margin-bottom: .2rem; }
.calc-panel-title {
  font-family: var(--f-mono); font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--c-blue-text); font-weight: 500;
}
.calc-panel-note { font-family: var(--f-mono); font-size: .62rem; color: var(--c-muted); }

.calc-row-controls { display: flex; gap: .5rem; flex-wrap: wrap; }
.calc-select, .calc-num {
  font-family: var(--f-mono); font-size: var(--fs-step--1); color: var(--c-text);
  background: var(--c-canvas); border: 1px solid var(--c-line); border-radius: 4px; padding: .45em .6em;
}
.calc-select { flex: 1 1 10rem; }
/* Inside a .calc-field (COLUMN flex) the 10rem flex-basis above becomes HEIGHT — the giant
   reference-conditions dropdown. Neutralize the basis in the column context. */
.calc-field .calc-select { flex: 0 0 auto; }
.calc-select:focus-visible { outline: 2px solid var(--c-blue); outline-offset: 1px; }
.calc-select:disabled { opacity: .5; }
/* Numeric inputs are AMENumeric (the portal's own control): size the WRAPPER, not the control's
   internals, so this survives any internal markup change in the library. */
.calc-numwrap { display: inline-block; width: 7.5rem; }
.calc-numwrap input { width: 100%; text-align: right; }
/* Warnings stack (AMEAlert, severity-mapped, worst first) */
.calc-alerts { display: flex; flex-direction: column; gap: .5rem; width: 100%; }

.calc-comp { display: flex; align-items: center; justify-content: space-between; gap: var(--space-s); }
.calc-comp-name { font-size: var(--fs-step--1); color: var(--c-text); }
.calc-total {
  display: flex; justify-content: space-between; margin-top: .3rem; padding-top: .6rem;
  border-top: 1px solid var(--c-line); font-family: var(--f-mono); font-size: var(--fs-step--1);
  color: var(--c-muted);
}
.calc-total-ok { color: var(--c-gas); }
.calc-total-off { color: var(--c-amber); }

/* ── Mode choice: two-tier (2026-08-06 redesign — physics first, direction second) ────────── */
.calc-mode-q {
  font-family: var(--f-mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--c-muted);
}
.calc-modes { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.calc-seg {
  display: flex; flex-direction: column; gap: .2rem; align-items: flex-start; text-align: left;
  padding: .7em .9em; cursor: pointer; border-radius: 6px;
  background: var(--c-canvas); border: 1px solid var(--c-line); color: var(--c-muted);
  transition: border-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out),
              box-shadow var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.calc-seg:hover:not(:disabled) { transform: translateY(-1px); border-color: rgba(255,255,255,.28); }
.calc-seg:disabled { opacity: .5; cursor: default; }
.calc-seg:focus-visible { outline: 2px solid var(--c-blue); outline-offset: 2px; }
.calc-seg-name { font-weight: 600; font-size: var(--fs-step--1); color: var(--c-text); line-height: 1.3; }
.calc-seg-sub { font-family: var(--f-mono); font-size: .58rem; letter-spacing: .08em; color: var(--c-muted); }
.calc-seg.is-on {
  border-color: var(--c-blue);
  box-shadow: 0 0 0 1px var(--c-blue), 0 6px 18px rgba(4,92,250,.18);
}
.calc-seg.is-on .calc-seg-name { color: var(--c-blue-text); }
.calc-seg-hc.is-on {
  border-color: var(--c-brand-green-text);
  box-shadow: 0 0 0 1px var(--c-brand-green-text), 0 6px 18px rgba(143,203,58,.16);
}
.calc-seg-hc.is-on .calc-seg-name { color: var(--c-brand-green-text); }
.calc-submodes { display: flex; gap: .4rem; flex-wrap: wrap; }
html.motion-ready .calc-submodes { animation: calc-fields-in .32s var(--ease-out); }

/* State change you can SEE: mode switches recreate .calc-fields (@key), which re-arrives;
   each fresh result pops (@key on .calc-big). Both gated on motion-ready. */
html.motion-ready .calc-fields { animation: calc-fields-in .32s var(--ease-out); }
@keyframes calc-fields-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.calc-fields { display: flex; flex-direction: column; gap: .6rem; }
html.motion-ready .calc-big { animation: calc-big-in .28s var(--ease-out); }
@keyframes calc-big-in {
  from { opacity: 0; transform: scale(.96); }
  to   { opacity: 1; transform: none; }
}

.calc-field { display: flex; flex-direction: column; gap: .3rem; }
.calc-label { font-size: var(--fs-step--1); color: var(--c-muted); }
.calc-input-wrap { display: flex; align-items: center; gap: .5rem; }
.calc-unit { font-family: var(--f-mono); font-size: .66rem; color: var(--c-muted); }

.calc-privacy { font-family: var(--f-mono); font-size: .62rem; color: var(--c-muted); line-height: 1.5; }

.calc-empty { font-family: var(--f-mono); color: var(--c-muted); font-size: 1.5rem; }
.calc-big { font-family: var(--f-display); font-size: clamp(2.2rem, 1.8rem + 2vw, 3.2rem); font-weight: 700; line-height: 1; color: var(--c-text); }
.calc-big-unit { font-size: .42em; color: var(--c-muted); margin-left: .15em; }
.calc-secondary { font-family: var(--f-mono); font-size: var(--fs-step--1); color: var(--c-muted); }
.calc-nosolution { font-size: var(--fs-step--1); color: var(--c-amber); line-height: 1.6; }

/* Converged badge is AMEStatusPill; warnings are AMEAlert — both style themselves. */

.calc-diag { display: grid; grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr)); gap: .5rem; margin: 0; }
.calc-diag dt { font-family: var(--f-mono); font-size: .58rem; letter-spacing: .1em; text-transform: uppercase; color: var(--c-muted); }
.calc-diag dd { font-family: var(--f-mono); font-size: var(--fs-step--1); color: var(--c-text); margin: .15rem 0 0; }

.calc-loading { display: flex; align-items: center; gap: .6rem; }
.calc-nosolution { font-size: var(--fs-step--1); color: var(--c-amber); line-height: 1.6; }

/* ── Live monitor island — THE AME PORTAL UI, on a simulated plant ──────────────────────────
   Rebuilt 2026-08-07 (Alberto: "the live demo should have the same ui/ux like the real one").
   The site ships the same control library the portal pins, so the controls style themselves
   through the --rz-* bridge above; everything here is the portal's APP-LEVEL chrome, ported
   from Ame.Portal.Ui/wwwroot/app.css and mapped onto the site's dark tokens.

   Rules this block is written to (docs/hmi-principles.md, via the portal's own stylesheet):
     §1  colour is scarce — nothing here is coloured unless it is abnormal or actionable;
     §4b the ONE animation in the product is the unacknowledged-alarm flash, and reduced
         motion replaces it with a persistent outline rather than dropping the distinction;
     R2  brightness encodes on/off — mixed toward absolute white/black, not toward a theme
         token, so the direction is structural and cannot invert with the palette;
     R7  every number is tabular-nums, so columns of figures stay decimal-aligned;
     R11 selection is a WHITE outline: white is outside the alarm vocabulary.
   No drop shadows, and exactly ONE filter: `.lmon-grey`, the operator-toggled greyscale proof
   (off by default, applied to the process area only while the switch is on). It is the one place
   a filter earns its cost, because draining the colour is how the redundant-encoding claim gets
   checked rather than believed. Everything else runs unfiltered on modest hardware. */
.lmon {
  margin-top: var(--space-m);
  border: 1px solid var(--c-line); border-radius: 8px; overflow: hidden;
  background: var(--c-canvas);

  /* AMENavRail's three sidebar tokens, rebound INSIDE the island exactly as the portal rebinds
     them in its own app.css. They default to var(--rz-success, …), which this site maps to the
     brand green — correct for the accent and the tick, and wrong for --ame-nav-active-bg, which
     is a BACKGROUND: it would paint a solid #8FCB3A block behind the hovered/active item, i.e.
     saturated colour spent on a normal state (§1) in the one place PAS reserves for nothing at
     all. Custom properties inherit, so scoping them here leaves the rest of the page alone. */
  --ame-sidebar-accent: var(--c-brand-green-text);
  --ame-nav-active-bg: color-mix(in srgb, var(--c-brand-green-text) 14%, transparent);
  --ame-sidebar-border: var(--c-line);
}

/* ---- app shell: topbar over (nav rail + content pane) ---- */
.lmon-topbar {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .6rem .9rem;
  background: var(--c-surface); border-bottom: 1px solid var(--c-line);
}
.lmon-brand { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.lmon-brand-mark { font-weight: 800; letter-spacing: .06em; color: var(--c-blue); font-size: 1rem; }
.lmon-brand-name { font-weight: 600; font-size: 1rem; color: var(--c-text); }
.lmon-brand-sub { font-family: var(--f-mono); font-size: .58rem; letter-spacing: .06em; color: var(--c-muted); }
.lmon-topbar-status { display: flex; gap: .4rem; margin-left: auto; flex-wrap: wrap; }

.lmon-shell { display: flex; align-items: stretch; min-height: 30rem; }

/* ---- the demo's nav rail: the PRODUCT's control, styled here because the site links only
   ame-controls.css and the rail's own stylesheet (ame-nav.css) is not in that bundle at the
   pinned rc.1 — unstyled, the buttons wrapped into a box-cluster with a dead column below
   (Alberto's 2026-08-21 screenshot). Scoped under .lmon so a future package stylesheet cannot
   fight it. Modeled on the real portal's left rail: a narrow vertical column, icon over a small
   label, active = accent tint + left bar. */
.lmon .ame-nav-rail {
  position: relative; display: flex; flex-direction: column; align-items: stretch; gap: .3rem;
  flex: 0 0 5.4rem; width: 5.4rem; padding: .7rem .4rem;
  background: color-mix(in srgb, var(--c-text) 3%, transparent);
  border-right: 1px solid color-mix(in srgb, var(--c-text) 10%, transparent);
}
/* The sliding tick indicator belongs to the unshipped stylesheet; our active mark is the bar. */
.lmon .ame-nav-rail__tick { display: none; }
.lmon .ame-nav-rail__item {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: .3rem;
  padding: .55rem .15rem .5rem; margin: 0; border: 0; border-radius: .5rem;
  background: transparent; color: var(--c-muted); font: inherit; cursor: pointer;
  transition: background .18s ease, color .18s ease;
}
.lmon .ame-nav-rail__item:hover { background: color-mix(in srgb, var(--c-text) 7%, transparent); color: var(--c-text); }
.lmon .ame-nav-rail__item.is-active {
  background: color-mix(in srgb, var(--c-brand-green-text, #8FCB3A) 12%, transparent);
  color: var(--c-text);
}
.lmon .ame-nav-rail__item.is-active::before {
  content: ""; position: absolute; left: -.4rem; top: .55rem; bottom: .5rem; width: 2px;
  border-radius: 1px; background: var(--c-brand-green-text, #8FCB3A);
}
.lmon .ame-nav-rail__item:focus-visible { outline: 2px solid var(--c-blue, #6AA9FF); outline-offset: 1px; }
.lmon .ame-nav-rail__icon { font-size: 1.2rem; line-height: 1; }
.lmon .ame-nav-rail__label { font-size: .56rem; letter-spacing: .05em; line-height: 1.15; text-align: center; }
/* Narrow: the rail slims to icons — a title attribute still names each screen. A CONTAINER
   query, not a media query: the island lives inside a padded section, so its own width is the
   fact that matters (a 900px viewport can still hand the island 600px). */
/* ⚠ container-type applies layout containment: any FUTURE position:fixed element rendered
   inside the demo (library tooltips, dialogs, notification hosts are all fixed) would be
   re-rooted to .lmon and misplaced by its own viewport math. None of the controls the demo
   uses today emits one (verified against the shipped control CSS, 2026-08-21). */
.lmon { container-type: inline-size; }
@container (max-width: 640px) {
  .lmon .ame-nav-rail { flex-basis: 3.2rem; width: 3.2rem; padding: .55rem .25rem; }
  .lmon .ame-nav-rail__label { display: none; }
  .lmon .ame-nav-rail__item.is-active::before { left: -.25rem; }
}

/* ---- exhibit arrival ceremonies (wave 3, state-based one-shots) ----
   The marker classes land on the STATIC section wrappers (ame-portal.js), so an island
   re-render can never strip them. Without motion (no motion-ready), nothing here applies and
   both islands are simply visible. */
html.motion-ready .ap-live-on .lmon { position: relative; overflow: hidden; }
html.motion-ready .ap-live-on .lmon::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 3.5rem; left: -20%;
  background: linear-gradient(100deg, transparent,
    color-mix(in srgb, var(--c-brand-green-text, #8FCB3A) 22%, transparent), transparent);
  animation: lmon-scan 1.1s cubic-bezier(.5, .1, .3, 1) .15s both;
  pointer-events: none;
}
@keyframes lmon-scan {
  from { left: -20%; opacity: 1; }
  85%  { opacity: 1; }
  to   { left: 115%; opacity: 0; }
}
html.motion-ready .ap-calc-host:not(.ap-calc-on) .calc-panel { opacity: 0; transform: translateY(14px); }
.ap-calc-host .calc-panel { transition: opacity .6s ease, transform .6s cubic-bezier(.22, .9, .28, 1); }
.ap-calc-host.ap-calc-on .calc-panel:nth-of-type(2) { transition-delay: .14s; }
.ap-calc-host.ap-calc-on .calc-panel:nth-of-type(3) { transition-delay: .28s; }

/* ---- site-wide CTA sheen (wave 3): a light band crosses a primary button once per hover.
   STATE-BASED (:hover), per the 2026-07-24 no-pointer-tracking rule; gated on motion-ready so
   reduced-motion users get the ordinary hover state.
   ⚠ Membership is deliberate, per the dispatched review (2026-08-21):
   - .btn-send is EXCLUDED — its ::after is the form's SENDING SPINNER (Contact.razor.css,
     scoped), which this pseudo-element would have silently destroyed for motion users.
   - the target is .projects-all-link, not .projects-cta — the latter is a full-width layout
     wrapper, and the sheen would have swept bare page background. */
html.motion-ready .cta, html.motion-ready .ap-cta, html.motion-ready .ari-cta,
html.motion-ready .projects-all-link {
  position: relative; overflow: hidden;
}
html.motion-ready .cta::after, html.motion-ready .ap-cta::after, html.motion-ready .ari-cta::after,
html.motion-ready .projects-all-link::after {
  content: ""; position: absolute; top: -20%; bottom: -20%; width: 40%; left: -60%;
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, .28), transparent);
  transform: skewX(-12deg); pointer-events: none; transition: none; opacity: 0;
}
html.motion-ready .cta:hover::after, html.motion-ready .ap-cta:hover::after,
html.motion-ready .ari-cta:hover::after, html.motion-ready .projects-all-link:hover::after {
  animation: cta-sheen .7s ease .05s 1 both;
}
@keyframes cta-sheen {
  from { left: -60%; opacity: 1; }
  to   { left: 130%; opacity: 0; }
}
.lmon-main { flex: 1 1 auto; min-width: 0; padding: 1rem 1.1rem; overflow-x: auto; }

/* ---- page chrome ---- */
.lmon-page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: .8rem; flex-wrap: wrap; margin: 0 0 .9rem;
}
.lmon-page-title { margin: 0; font-size: 1.05rem; font-weight: 650; color: var(--c-text); }
.lmon-page-sub { font-size: .72rem; color: var(--c-muted); }
.lmon-pill-row { display: flex; gap: .4rem; flex-wrap: wrap; }
.lmon-unit { font-size: .68rem; color: var(--c-muted); }
.lmon-mono { font-family: var(--f-mono); font-variant-numeric: tabular-nums; }
.lmon-controls { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .7rem; }
.lmon-spacer { flex: 1; }
.lmon-chip-label { margin-left: .3rem; }
.lmon-footnote { margin: .9rem .1rem 0; font-size: .66rem; line-height: 1.6; color: var(--c-muted); }
.lmon-legend { margin: .7rem 0 0; font-size: .66rem; line-height: 1.6; color: var(--c-muted); }
.lmon-note {
  margin: 0; padding: .5rem .9rem;
  border-top: 1px solid var(--c-line); background: var(--c-surface);
  font-family: var(--f-mono); font-size: .6rem; letter-spacing: .04em; color: var(--c-muted);
}

/* ---- KPI tiles: a caption is AMECard's <Header>, styled as a LABEL (quiet, no rule) ---- */
.lmon-kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .6rem; margin-bottom: .9rem;
}
.lmon-kpis > .ame-card > .ame-card__header {
  padding: .55rem .8rem .1rem; border-bottom: none;
  font-size: .66rem; font-weight: 600; color: var(--c-muted);
}
.lmon-kpis > .ame-card > .ame-card__body { padding: 0 .8rem .6rem; }
.lmon-kpi-value {
  font-size: 1.6rem; font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums; color: var(--c-text);
}
/* A count wears colour only when it demands action. */
.lmon-kpi-value--crit { color: var(--rz-danger, #FF7A6B); }
.lmon-kpi-value--warn { color: var(--c-amber); }
/* "Measuring" is a state, not a number — quieter than a value, never disguised as one. */
.lmon-kpi-value--nodata { font-size: 1.05rem; font-weight: 600; color: var(--c-muted); padding-top: .35rem; }
.lmon-kpi-pct { font-size: .8em; font-weight: 600; margin-left: .1em; }

/* ---- alarm KPI screen: distribution + Pareto rows ----
   Bars are greyscale by design (HMI law: colour is for abnormal); the priority glyph beside
   each row already carries colour + shape + numeral where it is earned. */
.lmon-dist, .lmon-pareto { display: flex; flex-direction: column; gap: .45rem; margin: .2rem 0 .3rem; }
.lmon-dist-row { display: flex; align-items: center; gap: .55rem; min-width: 0; }
.lmon-dist-name { flex: 0 0 7.5rem; font-size: .74rem; color: var(--c-text); }
.lmon-pareto-name { flex: 0 0 16rem; font-size: .74rem; color: var(--c-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lmon-dist-track { flex: 1 1 auto; height: .55rem; border-radius: .3rem;
  background: color-mix(in srgb, var(--c-text) 8%, transparent); overflow: hidden; }
.lmon-dist-bar { display: block; height: 100%; border-radius: .3rem;
  background: color-mix(in srgb, var(--c-text) 45%, transparent);
  transition: width .6s ease; }
.lmon-dist-bar--pareto { background: color-mix(in srgb, var(--c-text) 60%, transparent); }
.lmon-dist-num { flex: 0 0 auto; font-size: .74rem; font-variant-numeric: tabular-nums; color: var(--c-text); }
.lmon-dist-count { flex: 0 0 auto; font-size: .66rem; color: var(--c-muted); font-variant-numeric: tabular-nums; }
@media (max-width: 760px) {
  .lmon-pareto-name { flex-basis: 9rem; }
  .lmon-dist-name { flex-basis: 5.5rem; }
}

/* ---- the reserved faceplate zone: SIBLING COLUMNS, never a layer ----
   No position, no z-index, no backdrop — so "the rail never covers the process area" is a
   fact about the layout, not a promise about the code. The column exists whether or not
   something is selected, because a rail that appeared on selection would reflow the process
   area under the operator's pointer. */
.lmon-depict { display: grid; grid-template-columns: minmax(0, 1fr) 15rem; gap: .8rem; align-items: start; }
.lmon-depict__process { min-width: 0; }
@media (max-width: 1100px) {
  /* Narrow: the rail goes BELOW rather than shrinking — squeezing the process area is the one
     thing the reserved zone exists to prevent. */
  .lmon-depict { grid-template-columns: minmax(0, 1fr); }
  .lmon-faceplate { border-left: none; border-top: 1px solid var(--c-line); }
}

.lmon-faceplate {
  border-left: 1px solid var(--c-line); background: var(--c-surface);
  padding: .7rem .8rem; font-size: .72rem;
}
.lmon-fp__hint-title { font-weight: 600; margin-bottom: .35rem; color: var(--c-text); }
.lmon-fp__hint { margin: 0; color: var(--c-muted); line-height: 1.55; }
.lmon-fp__name { margin: 0 0 .1rem; font-size: .8rem; font-weight: 600; color: var(--c-text); }
.lmon-fp__id { font-family: var(--f-mono); font-size: .6rem; color: var(--c-muted); }
.lmon-fp__section {
  margin-top: .7rem; padding-top: .45rem; border-top: 1px solid var(--c-line);
  font-size: .56rem; letter-spacing: .12em; color: var(--c-muted);
}
.lmon-fp__kv { display: flex; justify-content: space-between; gap: .6rem; padding: .15rem 0; flex-wrap: wrap; }
.lmon-fp__k { color: var(--c-muted); display: inline-flex; align-items: center; gap: .3rem; }
/* A declaration row's LABEL never wraps around its value ("Heavy / C6+ bound / end",
   2026-08-21 screenshot) — keep the label whole; a tight cell drops the VALUE to its own line. */
.lmon-decl-kv .lmon-fp__k { white-space: nowrap; }
.lmon-fp__v { font-family: var(--f-mono); font-variant-numeric: tabular-nums; text-align: right; color: var(--c-text); }
.lmon-fp__v--live { color: var(--c-blue); }
.lmon-fp__v--bad { color: var(--rz-danger, #FF7A6B); }
.lmon-fp__glyph-label { font-size: .68rem; }

/* ---- device band: BRIGHTNESS carries on/off, colour carries abnormality ----
   Three fills against the pane, not two. The mix partners are absolute WHITE and BLACK, not
   the theme's text colour: mixing toward a token looks theme-aware and INVERTS on a light
   palette, which is the opposite of the rule. */
.lmon-nodes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
@media (min-width: 1000px) { .lmon-nodes { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.lmon-node {
  display: flex; flex-direction: column; gap: .3rem; align-items: flex-start; text-align: left;
  padding: .55rem .6rem; cursor: pointer; border-radius: 6px;
  background: var(--c-canvas); border: 1px solid var(--c-line); color: var(--c-text);
  transition: border-color var(--dur-micro) var(--ease-out);
}
.lmon-node--on { background: color-mix(in srgb, #FFFFFF 7%, var(--c-canvas)); }
.lmon-node--off { background: color-mix(in srgb, #000000 22%, var(--c-canvas)); }
.lmon-node:hover { border-color: rgba(255, 255, 255, .3); }
.lmon-node:focus-visible { outline: 2px solid var(--c-blue); outline-offset: 2px; }
/* R11: selection is a white outline — white is outside the alarm vocabulary, so it can never
   be mistaken for a priority. */
.lmon-node--sel { outline: 2px solid rgba(255, 255, 255, .85); outline-offset: -1px; }
.lmon-node__head { display: flex; align-items: center; gap: .4rem; width: 100%; }
.lmon-node__name { font-size: .72rem; font-weight: 600; color: var(--c-text); }
/* R3: a status WORD, small and quiet — it is a label, not a value. */
.lmon-node__status { font-family: var(--f-mono); font-size: .55rem; letter-spacing: .1em; color: var(--c-muted); }
.lmon-node__row { display: flex; align-items: baseline; gap: .2rem; }
.lmon-node__value {
  font-family: var(--f-mono); font-variant-numeric: tabular-nums;
  font-size: 1rem; font-weight: 600; color: var(--c-text);
}
/* R8: units small, non-bold, adjacent. Large bright units are an explicit anti-pattern. */
.lmon-node__unit { font-size: .58rem; color: var(--c-muted); }
/* R9: absent is an explicit token — never a blank, never a number at the scale floor, and
   deliberately NOT coloured, because "no data" is not an alarm. */
.lmon-node__bad { font-family: var(--f-mono); font-size: .72rem; color: var(--c-muted); }
.lmon-node__foot { font-family: var(--f-mono); font-size: .55rem; color: var(--c-muted); }

/* ---- BAND 2: like-for-like comparison. One row per measurement, one column per device,
   shared baseline and shared scale — a column that breaks the pattern is the odd one out,
   and that only works if every column is measured the same way. ---- */
.lmon-cmp-wrap { margin-top: .8rem; overflow-x: auto; }
.lmon-cmp-title { font-size: .6rem; letter-spacing: .08em; color: var(--c-muted); margin-bottom: .3rem; }
.lmon-cmp { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.lmon-cmp th, .lmon-cmp td { border-bottom: 1px solid var(--c-line); padding: .28rem .2rem; }
.lmon-cmp thead th:not(.lmon-cmp__rowlbl) {
  font-size: .58rem; font-weight: 500; color: var(--c-muted);
  text-align: center; letter-spacing: .04em; white-space: nowrap;
}
.lmon-cmp__rowlbl {
  text-align: left; width: 12rem; font-size: .62rem; font-weight: 400;
  color: var(--c-muted); white-space: nowrap; cursor: default;
}
.lmon-cmp__unit { font-size: .56rem; color: var(--c-muted); }
.lmon-cmp td { text-align: center; vertical-align: middle; }
/* Zebra by ROW: the eye reads across a row here, because the comparison is horizontal. */
.lmon-cmp__alt td, .lmon-cmp__alt th { background: color-mix(in srgb, #FFFFFF 3%, transparent); }
.lmon-cmp__cell { cursor: pointer; }
.lmon-cmp__cell--sel { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .5); }
.lmon-cmp__col--sel { color: var(--c-text); }
/* The column header is a keyboard route into the faceplate that must not LOOK like a button:
   five pieces of chrome in a band whose whole job is that nothing competes with the data. */
.lmon-cmp__colbtn {
  all: unset; display: block; width: 100%; cursor: pointer;
  font: inherit; color: inherit; text-align: center;
}
.lmon-cmp__colbtn:focus-visible { outline: 2px solid #FFFFFF; outline-offset: 1px; }
.lmon-cmp__num { font-family: var(--f-mono); font-size: .68rem; color: var(--c-text); }
.lmon-cmp__num--bad { color: var(--rz-danger, #FF7A6B); }
.lmon-cmp__bad { font-family: var(--f-mono); font-size: .62rem; color: var(--c-muted); }
.lmon-cmp__word { font-family: var(--f-mono); font-size: .58rem; letter-spacing: .08em; color: var(--c-muted); }
.lmon-cmp__svg { display: block; margin: .15rem auto 0; }
/* R5: a TRACK with a moving POINTER, never a bar that fills — a fill disappears exactly as
   the value gets low, and low margin is the dangerous case. */
.lmon-cmp__track { fill: none; stroke: var(--c-line); }
.lmon-cmp__zero { fill: rgba(125, 190, 214, .34); }
.lmon-cmp__pointer { fill: var(--c-blue); }
.lmon-cmp__pointer--bad { fill: var(--rz-danger, #FF7A6B); }

/* ---- trends: chart column + the point rail ---- */
.lmon-trend-layout { display: grid; grid-template-columns: minmax(0, 1fr) 16rem; gap: .8rem; align-items: start; }
@media (max-width: 1000px) { .lmon-trend-layout { grid-template-columns: minmax(0, 1fr); } }
.lmon-legend-row { display: flex; gap: .8rem; flex-wrap: wrap; align-items: center; margin-top: .5rem; font-size: .68rem; }
.lmon-pen { display: inline-flex; align-items: center; gap: .35rem; color: var(--c-text); }
.lmon-pen--off { opacity: .45; }
.lmon-swatch { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }
.lmon-rail { display: flex; flex-direction: column; gap: .5rem; }
.lmon-rail-card { background: var(--c-surface); border: 1px solid var(--c-line); border-radius: 6px; padding: .5rem .6rem; }
.lmon-rail-name { font-size: .72rem; font-weight: 600; color: var(--c-text); }
.lmon-rail-id { font-family: var(--f-mono); font-size: .55rem; color: var(--c-muted); }
.lmon-rail-row { display: flex; align-items: flex-end; justify-content: space-between; gap: .5rem; margin-top: .3rem; }
.lmon-rail-value { font-family: var(--f-mono); font-size: 1.1rem; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--c-text); }
.lmon-rail-value--warn { color: var(--c-amber); }
.lmon-rail-unit { font-size: .58rem; color: var(--c-muted); margin-left: .15rem; }
.lmon-rail-dev { font-size: .6rem; color: var(--c-muted); margin-top: .2rem; }

/* ---- alarms ---- */
.lmon-alarm-state { font-family: var(--f-mono); font-size: .62rem; font-weight: 700; letter-spacing: .06em; color: var(--c-text); }
/* FIRST OUT — the first alarm of a cascade. A WORD, not a colour: it is a different axis from
   priority and must not compete with it. */
.lmon-firstout {
  font-size: .52rem; letter-spacing: .07em; color: var(--c-amber);
  border: 1px solid color-mix(in srgb, var(--c-amber) 45%, transparent);
  border-radius: 2px; padding: 0 .25rem; margin-left: .35rem; white-space: nowrap;
}
.lmon-events { display: flex; flex-direction: column; gap: .3rem; }
.lmon-event-row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; font-family: var(--f-mono); font-size: .62rem; }
.lmon-event-time { color: var(--c-muted); font-variant-numeric: tabular-nums; }
.lmon-event-edge { color: var(--c-text); }
.lmon-event-kind { color: var(--c-muted); }
.lmon-event-val { color: var(--c-muted); margin-left: auto; font-variant-numeric: tabular-nums; }

/* ---- system ---- */
.lmon-sys-meta { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .6rem; }

/* ---- THE PRIORITY GLYPH: colour + SHAPE + NUMERAL ----
   Three redundant channels, so no single one carries the meaning alone. The shape arrives as
   configuration and is applied here through clip-path; the numeral is the element's own text. */
.lmon-pri {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; flex: 0 0 auto; vertical-align: middle;
  font: 600 9px/1 var(--f-mono); font-variant-numeric: tabular-nums;
  /* ONE fixed dark ink over every priority colour, so a site recolouring the scale changes
     one variable and not two. */
  color: #12161C; border: 1px solid rgba(0, 0, 0, .55);
}
.lmon-pri--square { /* P1 — no clip: the full block reads heaviest, which is the point */ }
.lmon-pri--triangle-up { clip-path: polygon(50% 0, 100% 100%, 0 100%); padding-top: 3px; font-size: 8px; }
/* padding-bottom, because the clip removes the bottom corners and an un-nudged numeral would
   sit in the narrow apex and get cut. */
.lmon-pri--triangle-down { clip-path: polygon(0 0, 100% 0, 50% 100%); padding-bottom: 3px; font-size: 8px; }
.lmon-pri--diamond { clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); font-size: 8px; }

/* The ONLY perpetual motion in the product, and only while the condition is PRESENT and
   unseen. steps(1,end) is a hard on/off rather than a fade: a pulse that eases is decorative,
   and this one is meant to be caught in peripheral vision. */
@keyframes lmon-unack-flash {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: .18; }
}
.lmon-pri--unack { animation: lmon-unack-flash 1s steps(1, end) infinite; }

/* Reduced motion does NOT mean "lose the distinction" — it means carry it another way. The
   outline uses the theme's TEXT colour, not currentColor: currentColor here is the glyph's
   near-black numeral ink, which on a dark page is the page's own background, i.e. an
   invisible outline that silently removes exactly the distinction this rule preserves. */
@media (prefers-reduced-motion: reduce) {
  .lmon-pri--unack {
    animation: none;
    outline: 2px solid var(--c-text);
    outline-offset: 2px;
  }
}

/* ---- THE GREYSCALE PROOF ----
   One filter, opt-in, on the process area only. The toolbar carrying the switch is deliberately
   OUTSIDE it so the control that turns it off stays legible. Nothing on the filtered area loses
   its meaning: every state under here also carries a word and a mark. */
.lmon-grey { filter: grayscale(1); }
/* display:contents so the one-control EditForm (AMESwitch needs a cascading EditContext) does not
   introduce a block box in the middle of the flex toolbar. */
.lmon-greyform { display: contents; }

/* ---- STATUS IS NOT BINARY ----
   The engine's terminal status in words, wherever the number itself cannot carry it. Neutral ink:
   "not converged" is a caveat on a number, not an alarm, and it must not compete with one. */
.lmon-node__estat {
  font-family: var(--f-mono); font-size: .55rem; letter-spacing: .04em;
  color: var(--c-amber);
}
.lmon-rail-stat { font-family: var(--f-mono); font-size: .58rem; color: var(--c-amber); margin-top: .2rem; }

/* ---- THE ENGINE'S OWN VALIDITY FINDINGS ----
   Severity + code + message, worst first. The severity WORD is the channel; the left border only
   reinforces it, and INFORMATION deliberately gets no colour at all — an extended-range note is
   not an abnormal condition, it is a statement about how much the number is worth. */
.lmon-warn-list { margin-top: .7rem; }
.lmon-warn-title { font-size: .6rem; letter-spacing: .08em; color: var(--c-muted); margin-bottom: .35rem; }
.lmon-warn {
  display: grid; grid-template-columns: auto 1fr; gap: .1rem .5rem;
  padding: .3rem .5rem; margin-top: .3rem;
  border-left: 2px solid var(--c-line); background: color-mix(in srgb, #FFFFFF 3%, transparent);
  font-size: .62rem; line-height: 1.5;
}
.lmon-warn__sev {
  font-family: var(--f-mono); font-size: .55rem; font-weight: 700; letter-spacing: .08em;
  color: var(--c-text); white-space: nowrap;
}
.lmon-warn__code { font-family: var(--f-mono); font-size: .55rem; color: var(--c-muted); word-break: break-all; }
.lmon-warn__msg { grid-column: 1 / -1; color: var(--c-muted); }
.lmon-warn--caution { border-left-color: var(--c-amber); }
.lmon-warn--high { border-left-color: var(--c-amber); }
.lmon-warn--severe { border-left-color: var(--rz-danger, #FF7A6B); }
.lmon-warn-none { margin: .4rem 0 0; font-size: .62rem; color: var(--c-muted); line-height: 1.5; }
.lmon-warn-note { margin: .45rem 0 0; font-size: .58rem; color: var(--c-muted); line-height: 1.55; }

/* ---- what a fixed axis owes the reader ---- */
.lmon-notdrawn { margin: .5rem 0 0; font-size: .64rem; line-height: 1.6; color: var(--c-muted); }

/* ---- the un-rationalised priority: a MARK, not a colour ----
   A hollow question mark riding beside the glyph. It carries no priority colour on purpose: the
   claim is about the provenance of the priority, not about the severity of the condition. */
.lmon-unrat {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; flex: 0 0 auto; margin-left: .2rem; vertical-align: middle;
  border: 1px solid var(--c-muted); border-radius: 50%;
  font: 700 9px/1 var(--f-mono); color: var(--c-muted); cursor: help;
}

/* ---- the shelf: off the outstanding list, still on the screen ---- */
.lmon-shelf { display: flex; flex-direction: column; gap: .35rem; }
.lmon-shelf-row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; font-size: .66rem; }
.lmon-shelf-dev { color: var(--c-text); }
.lmon-shelf-msg { color: var(--c-muted); }
.lmon-shelf-detail {
  margin-left: auto; font-family: var(--f-mono); font-size: .6rem;
  font-variant-numeric: tabular-nums; color: var(--c-muted);
}

/* ---- the flood banner: conditional, and it names the offender ---- */
.lmon-flood-top { display: block; margin-top: .3rem; font-size: .66rem; }
.lmon-flood-fix { display: block; margin-top: .3rem; font-weight: 600; }

/* ---- the DEVICE screen: the analysis declaration and what came of it ----
   A device page is dense config, so everything here is quiet by construction: no fills, no
   status colour of its own. The only colour on the screen arrives through AMEStatusPill, which
   already carries a WORD, and through the refusal block below. */
.lmon-decl-head {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin: .2rem 0 .6rem;
}
.lmon-decl-ver {
  font-family: var(--f-mono); font-size: .7rem; font-weight: 600; color: var(--c-text);
}
.lmon-decl-wrap { overflow-x: auto; }
.lmon-decl { width: 100%; border-collapse: collapse; font-size: .68rem; }
.lmon-decl th, .lmon-decl td {
  border-bottom: 1px solid var(--c-line); padding: .26rem .4rem; text-align: left;
  vertical-align: baseline;
}
.lmon-decl thead th {
  font-size: .58rem; letter-spacing: .07em; text-transform: uppercase; color: var(--c-muted);
  font-weight: 500;
}
/* The ordinal is the CYCLE POSITION, not a rank — muted, narrow, and never sortable. */
.lmon-decl__ix {
  width: 2.2rem; font-family: var(--f-mono); font-variant-numeric: tabular-nums;
  color: var(--c-muted);
}
.lmon-decl__tag { color: var(--c-muted); }
.lmon-decl__role { color: var(--c-muted); }
.lmon-decl-kv { max-width: 46rem; }
.lmon-policy-row { margin-top: .7rem; }

/* The severity mark is aria-hidden decoration beside the WORD — it must never be the only
   channel, so it is drawn quietly and the pill beside it carries the meaning. */
.lmon-sevmark {
  font-family: var(--f-mono); font-size: .7rem; color: var(--c-muted); margin-right: .35rem;
}
.lmon-findcode { font-size: .6rem; word-break: break-all; }

/* ---- the disclosure link: a plain button, never a link that navigates nowhere ---- */
.lmon-linkbtn {
  background: none; border: 0; padding: .2rem 0; margin-top: .4rem; cursor: pointer;
  font: inherit; font-size: .66rem; color: var(--c-blue); text-decoration: underline;
}
.lmon-linkbtn:focus-visible { outline: 2px solid var(--c-blue); outline-offset: 2px; }
.lmon-help {
  margin-top: .5rem; padding: .6rem .7rem; border-radius: 6px;
  border: 1px solid var(--c-line); background: var(--c-surface);
}
.lmon-help__title { font-size: .7rem; font-weight: 600; color: var(--c-text); margin-bottom: .3rem; }
.lmon-help__body { margin: 0; font-size: .66rem; line-height: 1.65; color: var(--c-muted); }

/* ---- THE REFUSAL: a result, drawn as one ----
   Not an AMEAlert: an alert says something has gone wrong on the plant. Nothing has — the
   engine was asked a question about a composition it holds no data for, and it answered. The
   block is bordered rather than filled so it reads as a stated absence, not as an incident. */
.lmon-refusal {
  margin: .3rem 0 0; padding: .55rem .7rem; border-radius: 6px;
  border: 1px solid var(--c-line); border-left: 3px solid var(--c-amber);
  background: var(--c-surface);
}
.lmon-refusal__title {
  font-size: .68rem; font-weight: 600; color: var(--c-text); margin-bottom: .3rem;
}
.lmon-refusal__msg {
  margin: 0; font-size: .66rem; line-height: 1.6; color: var(--c-text);
}
.lmon-refusal__note {
  margin: .45rem 0 0; font-size: .6rem; line-height: 1.6; color: var(--c-muted);
}

.calc-chart-hint {
  font-family: var(--f-mono); font-size: .6rem; letter-spacing: .08em; color: var(--c-muted);
  margin-top: .5rem; text-align: center;
}
/* The envelope's closure line — the library's honesty about how the trace ended, one step
   brighter than the hint because it is a statement about the data, not about the controls. */
.calc-env-closure { color: var(--c-text); font-size: .64rem; }

/* Chart host panel — the chart itself is AMEChart (the product's own control); its axes,
   crosshair, tooltip, zoom and export are the library's. SVG projection styles removed
   2026-07-31 with the hand-rolled chart. */
.calc-chart {
  margin: var(--space-m) 0 0; padding: var(--space-m);
  border: 1px solid var(--c-line); border-radius: 6px; background: var(--c-surface);
}

/* ── Contact success ceremony (markup injected by js/contact-form.js — JS-created nodes carry
      no Blazor scope attribute, so these rules must live globally, like the progress rail) ── */
.contact-success {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-m);
  padding-block: var(--space-l);
}
.contact-success-icon {
  width: 64px;
  height: 64px;
  color: var(--c-brand-green-text);
}
.cs-ring, .cs-check {
  stroke-dasharray: 60;
  stroke-dashoffset: 60;
}
.contact-success.is-in .cs-ring {
  transition: stroke-dashoffset 0.7s var(--ease-out);
  stroke-dashoffset: 0;
}
.contact-success.is-in .cs-check {
  transition: stroke-dashoffset 0.45s var(--ease-out) 0.55s;
  stroke-dashoffset: 0;
}
@media (prefers-reduced-motion: reduce) {
  .cs-ring, .cs-check { stroke-dashoffset: 0; }
  .contact-success.is-in .cs-ring, .contact-success.is-in .cs-check { transition: none; }
}

/* ── Loop-check progress rail (built by js/motion/progress-rail.js; desktop only) ────────── */
.progress-rail {
  position: fixed; right: 20px; top: 50%; transform: translateY(-50%);
  z-index: 40; display: flex; flex-direction: column; align-items: center;
  justify-content: space-between; gap: .35rem; min-height: 42vh; max-height: 62vh;
  opacity: 0; transition: opacity .5s var(--ease-out);
  padding-block: 6px;
}
.progress-rail.is-ready { opacity: 1; }
/* JS only builds the rail ≥1380px; this guards a post-build viewport shrink (enhanced nav). */
@media (max-width: 1379px) { .progress-rail { display: none; } }

.progress-rail-track {
  position: absolute; top: 10px; bottom: 10px; left: 50%; width: 1px;
  transform: translateX(-50%); background: var(--c-line); border-radius: 1px;
}
.progress-rail-fill {
  position: absolute; left: 0; top: 0; width: 1px; height: 0;
  background: var(--c-blue); box-shadow: 0 0 6px var(--c-blue);
  transition: height .12s linear;
}

.progress-rail-tick {
  position: relative; z-index: 1; width: 22px; height: 22px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer; color: var(--c-muted);
}
.progress-rail-tick::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  border: 1px solid var(--c-muted); background: var(--c-canvas);
  transition: background var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.progress-rail-tick:hover::before { border-color: var(--c-blue-text); }
.progress-rail-tick.is-active::before {
  border-color: var(--c-blue); background: var(--c-blue);
  box-shadow: 0 0 0 3px rgba(4, 92, 250, .25); transform: scale(1.15);
}
/* Done ticks swap the dot for a drawn check — the "loop-check" completing itself */
.progress-rail-tick.is-done::before { opacity: 0; }
.progress-rail-tick svg {
  position: absolute; width: 15px; height: 15px; color: var(--c-blue);
  opacity: 0; transition: opacity var(--dur-micro) var(--ease-out);
}
.rail-check-path { stroke-dasharray: 26; stroke-dashoffset: 26; transition: stroke-dashoffset .4s var(--ease-out); }
.progress-rail-tick.is-done svg { opacity: 1; }
.progress-rail-tick.is-done .rail-check-path { stroke-dashoffset: 0; }

.progress-rail-label {
  position: absolute; right: 26px; top: 50%; transform: translateY(-50%) translateX(4px);
  white-space: nowrap; font-family: var(--f-mono); font-size: var(--fs-step--1);
  letter-spacing: .04em; color: var(--c-text); background: rgba(12, 20, 31, .9);
  padding: .15em .5em; border: 1px solid var(--c-line); border-radius: 3px;
  opacity: 0; pointer-events: none; transition: opacity var(--dur-micro) var(--ease-out),
              transform var(--dur-micro) var(--ease-out);
}
.progress-rail-tick:hover .progress-rail-label,
.progress-rail-tick:focus-visible .progress-rail-label,
.progress-rail-tick.is-active .progress-rail-label { opacity: 1; transform: translateY(-50%) translateX(0); }
.progress-rail-tick:focus-visible { outline: 2px solid var(--c-blue); outline-offset: 2px; border-radius: 4px; }

@media (prefers-reduced-motion: reduce) {
  .progress-rail, .progress-rail-fill, .progress-rail-tick::before,
  .progress-rail-tick svg, .rail-check-path, .progress-rail-label { transition: none; }
}
