/* ============================================================
   THE SOFTWARE — the section that says Nivio is a screen a dispatcher
   signs into, not only an endpoint. Section-scoped; every selector .sw-.

   Bone ground (it sits under #fx, which is Carbon), so every accent that
   is TEXT routes through --signal-ink-bone. Signal itself is 1.19:1 on
   Bone and appears here only inside the Carbon app frame, where it is
   13.07:1 and legal.

   The frame is deliberately a SCHEMATIC, not a screenshot: drawn in CSS
   and SVG, captioned "Illustrative", so it shows the shape of the product
   without standing in for a picture of it. When a real screen capture
   exists it replaces the .sw-app block and nothing else changes.

   No RUNNING animation. The page already carries four running instruments
   (hero, board solve, desks rotator, API scroll); this section is the still
   frame between the calculator and the code, and it earns its attention by
   being the only thing on the page that holds completely still.

   The one exception, added 2026-09-18: the schematic solves itself once when
   the section is first scrolled into view, and again when a reader presses
   Re-solve. It is not a fifth instrument — it has no loop and no timer, it
   resolves to the still frame above within two seconds, and it only repeats
   when a person asks. The point is to show that the plan is COMPUTED rather
   than drawn, which a still picture of a finished board cannot say.

   Every rule below is scoped to .sw-app.is-solving, a class only JS adds.
   The resting state of the markup is the finished board, so with JS off, or
   under prefers-reduced-motion, the section is exactly what it was before.
   ============================================================ */

.sw{padding:96px 0;background:var(--nv-bone,#F2F0E9);color:var(--nv-carbon,#171918);
  border-top:1px solid var(--nv-border-alt,#C8C6BE)}
.sw *,.sw *::before,.sw *::after{box-sizing:border-box}

/* ---- head ---------------------------------------------------------- */
.sw-head{max-width:1060px;display:flex;flex-direction:column;gap:20px}
.sw-k{font-family:var(--nv-font-mono,"Geist Mono",ui-monospace,monospace);
  font-weight:500;font-size:.6875rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--nv-slate,#3A3D3B)}
.sw-head h2{margin:0;font-weight:600;font-size:1.6875rem;line-height:1.15;
  letter-spacing:-.02em;color:var(--nv-carbon,#171918)}
@media(min-width:769px){ .sw-head h2{font-size:2.375rem} }
@media(min-width:1240px){ .sw-head h2{font-size:2.75rem} }
.sw-head h2 .sig{color:var(--signal-ink-bone,#41630F)}
.sw-head p{margin:0;font-size:1.0625rem;line-height:1.65;
  color:var(--nv-ash,#5F6461);max-width:64ch;text-wrap:pretty}
/* the control beat is the one a reader should leave with, so it is set off
   above a hairline the way #whats-it-worth sets off its closing line */
.sw-close{margin-top:6px;padding-top:18px;border-top:1px solid var(--nv-border-alt,#C8C6BE);
  max-width:66ch}
.sw-head b{color:var(--nv-carbon,#171918);font-weight:600}

/* ---- the app frame -------------------------------------------------- */
.sw-app{margin-top:46px;border-radius:var(--nv-radius,4px);overflow:hidden;
  background:var(--nv-carbon,#171918);border:1px solid var(--nv-border-alt,#C8C6BE);
  box-shadow:0 18px 44px -28px rgba(23,25,24,.55)}

.sw-chrome{display:flex;align-items:center;justify-content:space-between;
  gap:16px;flex-wrap:wrap;padding:11px 14px;
  background:var(--nv-graphite,#222523);
  border-bottom:1px solid rgba(242,240,233,.10)}
.sw-tabs{display:flex;gap:6px;flex-wrap:wrap}
.sw-tab{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-size:.6875rem;
  letter-spacing:.04em;padding:6px 11px;border-radius:var(--nv-radius-sm,3px);
  color:var(--nv-steel,#9EA29F);border:1px solid transparent;white-space:nowrap}
.sw-tab.on{color:var(--nv-bone,#F2F0E9);background:rgba(242,240,233,.07);
  border-color:rgba(242,240,233,.16)}
.sw-tools{display:flex;gap:6px;flex-wrap:wrap}
.sw-tool{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-size:.6875rem;
  letter-spacing:.06em;text-transform:uppercase;padding:6px 10px;
  border-radius:var(--nv-radius-sm,3px);border:1px solid rgba(242,240,233,.18);
  color:var(--nv-fog,#C8C6BE);white-space:nowrap}
.sw-tool.p{background:var(--nv-signal,#D7E84A);border-color:var(--nv-signal,#D7E84A);
  color:var(--nv-carbon,#171918);font-weight:600}

/* rail + map. One column below 900 so the map never gets squeezed to a smear. */
.sw-body{display:grid;grid-template-columns:1fr}
@media(min-width:900px){ .sw-body{grid-template-columns:320px 1fr} }

.sw-rail{padding:16px 14px;border-bottom:1px solid rgba(242,240,233,.10)}
@media(min-width:900px){
  .sw-rail{border-bottom:0;border-right:1px solid rgba(242,240,233,.10)} }
.sw-rail-h{display:flex;justify-content:space-between;align-items:baseline;
  font-family:var(--nv-font-mono,"Geist Mono",monospace);font-size:.625rem;
  letter-spacing:.12em;text-transform:uppercase;color:var(--nv-steel,#9EA29F);
  padding-bottom:11px;margin-bottom:11px;border-bottom:1px solid rgba(242,240,233,.08)}
.sw-drv{display:flex;flex-direction:column;gap:6px;padding:9px 0}
/* the row whose route is drawn in Signal on the map */
.sw-drv.on{border-left:2px solid var(--nv-signal,#D7E84A);padding-left:9px;
  margin-left:-11px;padding-right:0}
.sw-drv + .sw-drv{border-top:1px solid rgba(242,240,233,.06)}
.sw-drv-n{display:flex;justify-content:space-between;gap:10px;
  font-family:var(--nv-font-mono,"Geist Mono",monospace);font-size:.6875rem;
  letter-spacing:.04em;color:var(--nv-fog,#C8C6BE)}
.sw-drv-n i{font-style:normal;color:var(--nv-steel,#9EA29F)}
.sw-chips{display:flex;gap:5px;flex-wrap:wrap}
.sw-chip{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-size:.625rem;
  letter-spacing:.03em;padding:3px 7px;border-radius:2px;
  background:rgba(215,232,74,.13);border:1px solid rgba(215,232,74,.32);
  color:var(--nv-signal,#D7E84A);white-space:nowrap}
/* a locked load reads as held, not as another optimized result */
.sw-chip.lk{background:rgba(242,240,233,.09);border-color:rgba(242,240,233,.30);
  color:var(--nv-bone,#F2F0E9)}
/* the one mid-swap: outlined, waiting to land */
.sw-chip.sw-mv{background:transparent;border-style:dashed;
  border-color:rgba(242,240,233,.34);color:var(--nv-fog,#C8C6BE)}

.sw-map{display:flex;flex-direction:column;min-height:300px;padding:14px 14px 0}
.sw-map svg{display:block;width:100%;flex:1 1 auto;min-height:262px}
/* Map labels take their colour from `color` and paint with currentColor, so a
   contrast audit measures the value that actually renders rather than the
   inherited CSS colour an SVG fill attribute silently bypasses. Steel on
   Carbon is 7.90:1. */
.sw-map .sw-lbl{color:var(--nv-steel,#9EA29F);fill:currentColor;
  stroke:var(--nv-carbon,#171918);stroke-width:3px;stroke-linejoin:round;
  paint-order:stroke fill}

/* The legend used to float over the bottom-left of the map. Now that every
   node carries a place name there is nowhere on the surface it can sit
   without covering one, so it becomes a footer strip under the drawing. */
.sw-legend{display:flex;gap:18px;flex-wrap:wrap;align-items:center;
  margin-top:10px;padding:9px 2px 11px;
  border-top:1px solid rgba(242,240,233,.08);
  font-family:var(--nv-font-mono,"Geist Mono",monospace);
  font-size:.625rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--nv-steel,#9EA29F)}
.sw-legend-n{margin-left:auto}
.sw-legend span{display:flex;align-items:center;gap:6px}
.sw-legend i{width:14px;height:0;border-top:2px solid var(--nv-signal,#D7E84A)}
.sw-legend i.d{border-top-style:dashed;border-top-color:var(--nv-steel,#9EA29F)}

.sw-kpis{display:grid;grid-template-columns:repeat(2,1fr);
  border-top:1px solid rgba(242,240,233,.10)}
@media(min-width:760px){ .sw-kpis{grid-template-columns:repeat(4,1fr)} }
.sw-kpi{padding:15px 16px 17px;border-top:1px solid rgba(242,240,233,.06)}
.sw-kpi + .sw-kpi{border-left:1px solid rgba(242,240,233,.10)}
@media(max-width:759px){ .sw-kpi:nth-child(odd){border-left:0} }
@media(min-width:760px){ .sw-kpi{border-top:0} }
.sw-kpi .k{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-size:.625rem;
  letter-spacing:.12em;text-transform:uppercase;color:var(--nv-steel,#9EA29F)}
.sw-kpi .v{font-size:1.375rem;font-weight:600;font-variant-numeric:tabular-nums;
  color:var(--nv-bone,#F2F0E9);margin-top:7px;letter-spacing:-.01em}
.sw-kpi .v em{font-style:normal;color:var(--nv-signal,#D7E84A);font-size:.8125rem;
  font-weight:500;margin-left:7px;letter-spacing:0}

.sw-cap{margin-top:12px;font-family:var(--nv-font-mono,"Geist Mono",monospace);
  font-size:.6875rem;letter-spacing:.06em;color:var(--nv-slate,#3A3D3B)}

/* ---- the capability deck ---------------------------------------------
   Six blocks, three at a time. Below 1080 the deck is not a deck: both pages
   render in the ordinary flow and the arrows are gone, because a carousel on
   a phone hides content behind a gesture nobody makes. The rotation is a
   wide-screen affordance only, and sw.js checks the same breakpoint before
   it starts anything.

   The two pages stack in one grid cell so the container is as tall as the
   taller page. That costs a little whitespace under the shorter one and buys
   a deck that never jumps height mid-turn. */
/* ---- the capability rail --------------------------------------------
   Six cards on one track, three and a bit in view, paged by ticks. Ported
   from the client's component with four things changed, all of them the
   standards rather than taste:

   1. The dim is gone. The component faded inactive cards to opacity .34,
      which puts Ash at 1.60:1 and the heading at 1.67:1 on Bone — text you
      cannot read is not a de-emphasis, it is content removed. The active
      card is now marked by a 2px Moss cap over the Fog rule, a Moss eyebrow
      and index, and a Carbon heading; the others keep Slate headings and Ash
      body at full strength. Three signals, all of them legible.
   2. Headings are Carbon, not Moss. Six green headings plus six green
      eyebrows plus the ticks is far past the two or three accents per view
      the standards allow; the accent marks the ONE card in play.
   3. The card index was Steel, 2.27:1 on Bone. Ash inactive, Moss active.
   4. Card content lives in the HTML, not in a JS array. The component built
      all six from script, which means no crawler and no reader without JS
      ever sees a word of it. On a marketing page that is a real cost.

   Below 1080 there is no rail at all: all six stack in the ordinary grid,
   which is what v3 asks for — a card set steps 3-up, 2-up, 1-up and is
   never a horizontal scroller on a phone. */

.sw-railhead{display:none}
.sw-clip{margin-top:22px}
.sw-track{display:grid;grid-template-columns:1fr;gap:0}

.sw-card{padding:24px 0 28px;border-top:1px solid var(--nv-border-alt,#C8C6BE);min-width:0;
  display:flex;flex-direction:column;gap:10px}
.sw-cap{border-top:2px solid transparent;margin-top:-25px;padding-top:23px}
.sw-card .num{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-weight:400;
  font-size:.6875rem;line-height:1;letter-spacing:.14em;color:var(--nv-ash,#5F6461)}
.sw-b-k{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-weight:500;
  font-size:.625rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--signal-ink-bone,#41630F);margin:0}
.sw-card h3{margin:0;font-size:1.1875rem;line-height:1.32;font-weight:600;
  letter-spacing:-.012em;color:var(--nv-carbon,#171918);text-wrap:balance}
.sw-card p{margin:0;font-size:.9375rem;line-height:1.62;
  color:var(--nv-ash,#5F6461);text-wrap:pretty}

@media(min-width:700px){
  .sw-track{grid-template-columns:repeat(2,1fr)}
  .sw-card{padding:24px 28px 28px}
  .sw-card:nth-child(2n+1){padding-left:0}
  .sw-card:nth-child(2n){border-left:1px solid var(--nv-border-alt,#C8C6BE);padding-right:0}
}

/* ---- rail mode ---- */
@media(min-width:1080px){
  .sw-railhead{display:flex;align-items:flex-end;justify-content:space-between;
    gap:32px;margin-top:44px}
  .sw-k2{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-weight:500;
    font-size:.6875rem;letter-spacing:.15em;text-transform:uppercase;
    color:var(--signal-ink-bone,#41630F)}
  .sw-nav2{display:flex;align-items:center;gap:20px;flex:0 0 340px}
  .sw-count{font-family:var(--nv-font-mono,"Geist Mono",monospace);font-weight:400;
    font-size:.6875rem;line-height:1;letter-spacing:.14em;
    color:var(--nv-ash,#5F6461);font-variant-numeric:tabular-nums}
  .sw-ticks{display:flex;gap:4px;flex:1}
  .sw-ticks button{flex:1;height:22px;background:none;border:0;padding:0;
    cursor:pointer;display:grid;align-items:center}
  .sw-ticks span{display:block;height:3px;background:var(--nv-fog,#C8C6BE);
    transition:background 120ms ease-out}
  .sw-ticks button[data-on="1"] span{background:var(--signal-ink-bone,#41630F)}
  .sw-ticks button:hover span{background:var(--nv-slate,#3A3D3B)}
  .sw-ticks button[data-on="1"]:hover span{background:var(--signal-ink-bone,#41630F)}
  .sw-ticks button:focus-visible{outline:2px solid var(--signal-ink-bone,#41630F);
    outline-offset:3px}

  .sw-clip{overflow:hidden;margin-top:20px;position:relative}
  /* the card at the right edge is meant to be half-seen — that is the signal
     that the rail carries on. A hard cut through a word reads as a bug, so
     it dissolves into the ground instead, and the fade lifts on the last
     page where there is nothing further to promise. */
  .sw-clip::after{content:"";position:absolute;inset:0 0 0 auto;width:72px;
    pointer-events:none;opacity:1;transition:opacity 220ms ease-out;
    background:linear-gradient(to right,rgba(242,240,233,0),var(--nv-bone,#F2F0E9))}
  .sw-clip.at-end::after{opacity:0}
  .sw-track{display:flex;gap:24px;
    transition:transform 400ms cubic-bezier(.2,0,0,1)}
  .sw-card{flex:0 0 328px;padding:24px 0 4px;cursor:pointer;
    border-left:0 !important;border-bottom:0}
  .sw-card h3{color:var(--nv-slate,#3A3D3B)}
  .sw-card .sw-b-k{color:var(--nv-slate,#3A3D3B)}
  .sw-card[data-on="1"] .sw-cap{border-top-color:var(--signal-ink-bone,#41630F)}
  /* the selected card's headline carries the accent — Signal Moss is 6.11:1
     on Bone, and only one card is ever selected, so the page still shows a
     single accent at a time rather than the six the source component had. */
  .sw-card[data-on="1"] h3{color:var(--signal-ink-bone,#41630F)}
  .sw-card[data-on="1"] .sw-b-k{color:var(--signal-ink-bone,#41630F)}
  .sw-card[data-on="1"] .num{color:var(--signal-ink-bone,#41630F)}
}
@media(min-width:1240px){ .sw-card{flex:0 0 372px} }
@media(prefers-reduced-motion:reduce){ .sw-track{transition:none} }

/* ---- the closing line ------------------------------------------------ */
.sw-foot{margin-top:54px;border-left:2px solid var(--signal-ink-bone,#41630F);
  padding:6px 0 7px 26px;font-size:1rem;line-height:1.65;
  color:var(--nv-ash,#5F6461);max-width:70ch}
.sw-foot b{color:var(--nv-carbon,#171918);font-weight:600}

.sw-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
  white-space:nowrap}

@media(max-width:768px){
  .sw{padding:64px 0}
  .sw-app{margin-top:34px}
  .sw-foot{margin-top:38px;padding-left:20px}
}


/* ---- the solve ------------------------------------------------------
   Timeline, once, ~1.9s:
     0.04  driver rows land, 90ms apart
     0.06  the yard marks itself
     0.18  the chosen route draws, start to finish
     0.42  each stop pops as the line reaches it
     0.90  the empty return fades in and its dots march home
     0.30  the four figures count up to what the solve found
     1.25  the deltas arrive last, because they are the claim

   --sw-route-len is written by sw.js from the path's own getTotalLength(),
   so the draw stays exact if the route is ever redrawn. */

button.sw-tool{background:transparent;cursor:pointer;font:inherit;
  font-family:var(--nv-font-mono,"Geist Mono",monospace);font-size:.6875rem;
  letter-spacing:.06em;text-transform:uppercase;line-height:normal}
button.sw-tool:hover{color:var(--nv-bone,#F2F0E9);
  border-color:rgba(242,240,233,.34)}
button.sw-tool:focus-visible{outline:2px solid var(--nv-signal,#D7E84A);
  outline-offset:2px}

@keyframes sw-draw{ from{stroke-dashoffset:var(--sw-route-len,400)} to{stroke-dashoffset:0} }
@keyframes sw-march{ from{opacity:0;stroke-dashoffset:44} to{opacity:1;stroke-dashoffset:0} }
@keyframes sw-pop{ from{opacity:0;transform:scale(.25)} to{opacity:1;transform:scale(1)} }
@keyframes sw-row{ from{opacity:0;transform:translateY(7px)} to{opacity:1;transform:none} }
@keyframes sw-fade{ from{opacity:0} to{opacity:1} }

.sw-app.is-solving .sw-drv{animation:sw-row 420ms cubic-bezier(.22,.61,.36,1) both}
.sw-app.is-solving .sw-drv:nth-child(2){animation-delay:40ms}
.sw-app.is-solving .sw-drv:nth-child(3){animation-delay:130ms}
.sw-app.is-solving .sw-drv:nth-child(4){animation-delay:220ms}
.sw-app.is-solving .sw-drv:nth-child(5){animation-delay:310ms}

/* fill-box so a 10px rect scales about itself, not the 640x300 viewBox */
.sw-app.is-solving .sw-yard rect,
.sw-app.is-solving .sw-sel rect{transform-box:fill-box;transform-origin:center}
.sw-app.is-solving .sw-yard rect{animation:sw-pop 320ms cubic-bezier(.34,1.56,.64,1) 60ms both}

.sw-app.is-solving .sw-route{stroke-dasharray:var(--sw-route-len,400);
  animation:sw-draw 760ms cubic-bezier(.4,0,.2,1) 180ms both}

.sw-app.is-solving .sw-sel rect{animation:sw-pop 300ms cubic-bezier(.34,1.56,.64,1) both}
.sw-app.is-solving .sw-sel rect:nth-child(1){animation-delay:420ms}
.sw-app.is-solving .sw-sel rect:nth-child(2){animation-delay:560ms}
.sw-app.is-solving .sw-sel rect:nth-child(3){animation-delay:700ms}
.sw-app.is-solving .sw-sel rect:nth-child(4){animation-delay:840ms}

.sw-app.is-solving .sw-empty{animation:sw-march 700ms linear 900ms both}

.sw-app.is-solving .sw-kpi .v em{animation:sw-fade 400ms ease-out 1250ms both}

/* The carve-out does not apply to a reader who asked for less motion: the
   class is still added so JS can write the final figures, but nothing moves. */
@media(prefers-reduced-motion:reduce){
  .sw-app.is-solving .sw-drv,
  .sw-app.is-solving .sw-yard rect,
  .sw-app.is-solving .sw-sel rect,
  .sw-app.is-solving .sw-route,
  .sw-app.is-solving .sw-empty,
  .sw-app.is-solving .sw-kpi .v em{animation:none}
  .sw-app.is-solving .sw-route{stroke-dasharray:none}
}
