/* ════════════════════════════════════════════════════════════════════════
   css/components/walkthrough.css — THE SOFTWARE WALKTHROUGH
   service.tnaado.ca · reusable · pairs with js/walkthrough.js

   PORTED from the old live site's js/software-walkthrough.js +
   css/software-walkthrough.css. Ranked the highest-value remaining port
   because it is already markup-driven and needs no data source: the number
   of steps, their order and what each one aims at are all in the HTML, so
   adding a step is an HTML edit and nothing in the driver changes.

   WHAT IT SHOWS. A numbered list of steps beside a screen. Picking a step —
   or letting it advance — moves the screen to that step's capture and, if
   the step asks for one, zooms toward the part of it being talked about.
   It is how you say "here is what the software does" without a paragraph
   that says "our software is powerful and intuitive".

   WHERE IT DIFFERS FROM THE LAPTOP (css/components/laptop.css), because the
   two look adjacent and are not: the laptop shows ONE WHOLE SCREEN PER
   SYSTEM and its tabs are a way to browse between systems. This shows A
   SEQUENCE THROUGH ONE SYSTEM — the queue, then the decision, then the
   audit trail — in order, with a caption per step. That ordering is the
   whole argument: "a human has to release this before it goes anywhere" is
   a claim you can only really make by showing the three screens in the
   order they happen.

   ── ONE REAL CHANGE FROM THE ORIGINAL, AND WHY ────────────────────────
   The old stylesheet pans with ABSOLUTE PIXELS against a hardcoded
   intrinsic width:

       .swk__shot { width: 1600px; max-width: none; }
       transform: translate3d(calc(var(--rx) * var(--s) * -1px), …) scale(…)

   Every `--rx` / `--ry` in that build is a pixel coordinate measured on a
   1600px-wide capture. THIS REPO'S CAPTURES ARE NOT ALL 1600px WIDE.
   Counted, not assumed: of the 65 files in assets/gallery/systems/,
   **39 are 1280×800 and 26 are 1600×1000**. Under the old rule the 39
   narrow ones are upscaled to 1600 and every crop coordinate on them
   misaims by a factor of 1.25 — silently, and worse the further from the
   origin you aim.

   So the pan is expressed in PERCENTAGES OF THE IMAGE instead, as a focal
   point plus a scale:

       --fx / --fy   where to look, 0%..100% of the capture
       --s           how far in, 1 = the whole screen

   `transform-origin: var(--fx) var(--fy)` with `scale(var(--s))` enlarges
   the picture about that point, so the point stays put and the area around
   it fills more of the window. No solving for a translate, no intrinsic
   width anywhere, and a capture of any size behaves identically. Swapping
   a 1280 capture for a 1600 one changes nothing.

   AUTHORING NOTE: `--s: 1` (the default) is a legitimate step. A
   walkthrough of four whole screens is a walkthrough. Only add a focal
   zoom when there is genuinely one region being discussed, and pick the
   numbers by LOOKING at the capture — a guessed crop aims at nothing and
   is worse than no crop.

   ── THE FINISHED STATE / DEGRADATION ──────────────────────────────────
   Same rule as the laptop, and for the same reason: a stepper can show one
   step, and the captures are the evidence. With no script, `.is-live` is
   never added, the step buttons do not render, and EVERY capture is
   visible in order with its caption under it. The reader loses the tour
   and keeps all the proof.

   ── COEXISTENCE WITH THE STORM ────────────────────────────────────────
   No rAF, no scroll listener, no resize listener, no measurement. The pan
   is a CSS transition on `transform`, which composites off the main
   thread; the driver only moves a class, so a transition already in flight
   is RE-TARGETED rather than queued or restarted. The one timer is the
   auto-advance interval.
   ════════════════════════════════════════════════════════════════════════ */

.swk {
  /* TOKEN BRIDGE — scene.css + chrome.css declare none of these. */
  --wk-mono:  var(--mono, 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace);
  --wk-sans:  var(--sans, 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, sans-serif);
  --wk-serif: var(--serif, Fraunces, Georgia, serif);
  --wk-fg:    var(--bone, #f5f1e8);
  --wk-dim:   var(--dim-ink, #b9b3a6);
  --wk-acc:   var(--red-ink, #ef5c6e);
  --wk-rule:  var(--hair-ink, rgba(245, 241, 232, .14));
  --wk-plate: var(--plate, rgba(245, 241, 232, .03));
  --wk-e:     var(--ease-out-expo, cubic-bezier(.19, 1, .22, 1));
  --wk-dwell: 3400ms;

  /* chrome.css makes <body> a COLUMN flex container, which puts ancestors in
     a shrink-to-fit context where an overflow:hidden box can report its
     max-content as its preferred width. See the FLEX-ITEM TRAP note in
     css/components/proofcard.css — same estate, same trap. */
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

.swk__body {
  display: grid;
  grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
  gap: clamp(1.1rem, 3vw, 2.2rem);
  align-items: start;
}
@media (max-width: 900px) {
  .swk__body { grid-template-columns: minmax(0, 1fr); }
}

/* ══ THE STEPS ════════════════════════════════════════════════════════════
   A real tablist: roving tabindex, arrow keys, visible focus. It does not
   render at all until js/walkthrough.js adds `.is-live`, because a step
   button that cannot step anything is a lie in the accessibility tree. */
.swk:not(.is-live) .swk__steps { display: none; }

.swk__steps { display: flex; flex-direction: column; gap: .3rem; margin: 0; padding: 0; }

.swk__btn {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .1rem .6rem;
  text-align: left;
  padding: .7rem .8rem;
  background: transparent;
  border: 1px solid transparent;
  border-left: 1px solid var(--wk-rule);
  cursor: pointer;
  color: inherit;
}
.swk__btn[aria-selected="true"] {
  background: var(--wk-plate);
  border-color: var(--wk-rule);
  border-left-color: var(--red, #c8102e);
}
.swk__btn:focus-visible { outline: 2px solid var(--wk-acc); outline-offset: 2px; }
@media (hover: hover) {
  .swk__btn { transition: background-color .2s var(--wk-e), border-color .2s var(--wk-e); }
  .swk__btn:not([aria-selected="true"]):hover { background: rgba(245, 241, 232, .04); }
}

.swk__num {
  grid-row: 1 / 3;
  font-family: var(--wk-mono); font-size: .62rem;
  letter-spacing: .08em; color: var(--wk-dim);
  padding-top: .12rem;
}
.swk__btn[aria-selected="true"] .swk__num { color: var(--wk-acc); }
.swk__stitle {
  font-family: var(--wk-sans); font-weight: 500; font-size: .9rem;
  line-height: 1.3; color: var(--wk-fg);
}
.swk__sdesc {
  font-family: var(--wk-sans); font-size: .8rem; line-height: 1.5;
  color: var(--wk-dim); margin: .18rem 0 0;
}

/* The progress tick. Only under the selected step, only while actually
   playing — so it is a readout of a real timer, never decoration. */
.swk__tick {
  grid-column: 1 / -1;
  height: 1px; margin-top: .6rem;
  background: var(--wk-rule);
  overflow: hidden; display: none;
}
.swk__btn[aria-selected="true"] .swk__tick { display: block; }
.swk__tick::after {
  content: ""; display: block; height: 100%;
  background: var(--red, #c8102e);
  transform: scaleX(0); transform-origin: 0 50%;
}
.swk[data-swk-playing="true"] .swk__btn[aria-selected="true"] .swk__tick::after {
  animation: swkTick var(--wk-dwell, 3400ms) linear forwards;
}
@keyframes swkTick { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ══ THE SCREEN ═══════════════════════════════════════════════════════════
   A machined bezel, not a browser skeuomorph: one hairline, a title strip
   carrying the application's own name, and a ground under it. This is what
   turns a JPEG into "an application running", which is the complaint being
   answered. */
.swk__screen {
  border: 1px solid var(--wk-rule);
  background: var(--wk-plate);
  min-width: 0;
}
.swk__chrome {
  display: flex; align-items: center; gap: .45rem;
  padding: .38rem .55rem;
  border-bottom: 1px solid var(--wk-rule);
}
.swk__lights { display: flex; gap: .3rem; flex: 0 0 auto; }
.swk__lights i { width: 5px; height: 5px; border-radius: 50%; background: rgba(245, 241, 232, .22); }
.swk__ctitle {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--wk-mono); font-size: .55rem;
  letter-spacing: .08em; text-transform: uppercase;
  color: rgba(245, 241, 232, .56);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.swk__viewport {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: #08080a;
}

/* ── THE CAPTURES ─────────────────────────────────────────────────────────
   Percentage geometry, so a 1280×800 and a 1600×1000 capture behave
   identically. See the header for why that is not a detail. */
.swk__shot {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  max-width: none; max-height: none;      /* defeat the site-wide caps */
  object-fit: cover; object-position: top center;
  transform-origin: var(--fx, 50%) var(--fy, 50%);
  transform: scale(var(--s, 1));
  opacity: 0; visibility: hidden;
  transition:
    transform 760ms var(--wk-e),
    opacity 300ms linear,
    visibility 0s linear 300ms;
  will-change: transform;
}
.swk__shot.is-active {
  opacity: 1; visibility: visible;
  transition:
    transform 760ms var(--wk-e),
    opacity 300ms linear,
    visibility 0s;
}

/* ── THE CAPTIONS ─────────────────────────────────────────────────────────
   One per step, so the reader is never looking at an unlabelled
   screenshot. Inactive captions stay in the DOM. */
.swk__caps { position: relative; }
.swk__cap {
  font-family: var(--wk-mono); font-size: .62rem;
  letter-spacing: .06em; line-height: 1.55;
  color: var(--wk-dim);
  margin: .8rem 0 0;
}
.swk.is-live .swk__cap { display: none; }
.swk.is-live .swk__cap.is-active { display: block; }

/* ══ NO SCRIPT — THE HONEST STACK ═════════════════════════════════════════
   Every capture, visible, in order, each with its caption. Not one screen
   and three hidden. */
/* The step column is display:none without a script, so .swk__screen becomes
   the FIRST grid item and lands in the 17rem step track — a 272px-wide screen
   on a 1100px page. Caught by measuring the JS-off render, not by reading it. */
.swk:not(.is-live) .swk__body { grid-template-columns: minmax(0, 1fr); }

.swk:not(.is-live) .swk__viewport {
  position: static; aspect-ratio: auto; overflow: visible; background: none;
}
.swk:not(.is-live) .swk__chrome { display: none; }
.swk:not(.is-live) .swk__shot {
  position: static; display: block;
  width: 100%; height: auto;
  aspect-ratio: 16 / 10;
  max-height: clamp(280px, 34vw, 440px);
  transform: none;
  opacity: 1; visibility: visible;
  transition: none;
  border: 1px solid var(--wk-rule);
}
.swk:not(.is-live) .swk__shot + .swk__shot { margin-top: .7rem; }
.swk:not(.is-live) .swk__cap { margin: .5rem 0 1.2rem; }

/* ══ LIGHT GROUND ═════════════════════════════════════════════════════════ */
.fp-band--bone .swk, .fp-band--ivory .swk, .swk--on-bone {
  --wk-fg:    var(--ink, #0a0a0a);
  --wk-dim:   var(--dim-bone, #5c574f);
  --wk-acc:   var(--red-bone, #a4132c);
  --wk-rule:  var(--hair-bone, rgba(10, 10, 10, .16));
  --wk-plate: rgba(10, 10, 10, .03);
}
.fp-band--bone .swk__ctitle, .swk--on-bone .swk__ctitle { color: rgba(10, 10, 10, .56); }
.fp-band--bone .swk__lights i, .swk--on-bone .swk__lights i { background: rgba(10, 10, 10, .22); }

/* ══ REDUCED MOTION ═══════════════════════════════════════════════════════
   The steps stay clickable over a still frame — the same information, no
   motion. js/walkthrough.js never starts the auto-advance here, so the tick
   never animates either. */
@media (prefers-reduced-motion: reduce) {
  .swk__shot { transition: opacity 200ms linear, visibility 0s linear 200ms; }
  .swk__shot.is-active { transition: opacity 200ms linear, visibility 0s; }
  .swk__tick::after { animation: none !important; }
  .swk__btn { transition: none; }
}
