/* ════════════════════════════════════════════════════════════
   TNAADO — SOFTWARE WALKTHROUGH (.swk)
   T-1137, 2026-08-14.

   WHY THIS EXISTS. Ethan, on the deployed preview: "you have
   screenshots as IMAGES instead of CAPTURED IN WELL ANIMATED
   SECTIONS." The captures were never the problem — every one of them
   is a real UI pulled off the running application. The problem is that
   they were dropped onto the page as bare <img> in a full-bleed band,
   which reads as a leftover asset rather than as software.

   It also replaces the section shape he banned outright: "the design
   concept of little text white on black then big image needs to go."
   So this component is the opposite of that shape on every axis —

     GROUND      bone (#faf8f3), dark type. Not light-on-dark.
     TEXT        sectionalised into numbered steps you can scan,
                 not one sparse paragraph floating over a picture.
     IMAGE       contained, with margin, inside a machined frame.
                 Never edge to edge, never the dominant element.
     MOTION      displays something real — the frame holds still and
                 the view moves between regions of the actual UI that
                 prove the step being read. Nothing decorative.

   THE CROP IS THE WHOLE TRICK. A 1600px application screenshot shrunk
   into a page slot is unreadable at any width — that is measured, not
   guessed (see tech-citadel.css, where 1440 rendered at 0.90x and 390
   at 0.26x). So the image is NEVER scaled to fit. It is drawn at its
   intrinsic size and the viewport is a window slid over it, which
   makes the rendered scale independent of the page width. Every step
   is a pair of source-pixel coordinates plus a scale, set inline:

       style="--rx:350; --ry:195; --s:1"

   --rx/--ry are the top-left corner of the crop IN THE SOURCE FILE, in
   pixels, unitless. --s is the zoom. They were read off the actual
   images; do not re-anchor them without opening the file first.

   TRANSFORM ONLY. The pan is a translate3d + scale on a composited
   layer, never a change to width/height/object-position, so it stays
   on the compositor and stays interruptible mid-flight.
   ════════════════════════════════════════════════════════════ */

/* ── THE SECTION ────────────────────────────────────────────── */
.swk {
  position: relative;
  /* Ground and type colour come from .t-light in css/tnaado-system.css —
     every instance carries it. Restated here only as a fallback so the
     component is never transparent over the dark page if the class is
     dropped; the values are the same tokens, not a second opinion.
     Vertical rhythm is deliberately NOT set here: the .t-sec / --tight /
     --tall scale owns it, so sections can be given different weights
     rather than all landing identically. */
  background: var(--sys-bone, var(--bone));
  color: var(--sys-ink, var(--ink));
}

/* Black as SEPARATION, not as the page ground — a hairline shelf top
   and bottom so the bone band reads as a deliberate plate set into the
   dark page rather than as a hole punched in it. */
.swk::before,
.swk::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: 1px;
  background: rgba(10,10,10,.16);
}
.swk::before { top: 0; }
.swk::after { bottom: 0; }

.swk__head { max-width: 62ch; }

.swk .eyebrow,
.swk__eyebrow {
  display: flex; align-items: center;
  gap: .6rem;
  margin: 0 0 1rem;
  font-family: var(--font-mono);
  font-size: .66rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(10,10,10,.62);
}
.swk__eyebrow .eyebrow-rule {
  width: clamp(20px, 3vw, 40px); height: 1px;
  background: var(--sys-red-on-light, #a4132c);
  flex: 0 0 auto;
}
.swk__eyebrow .eyebrow-num { color: var(--sys-red-on-light, #a4132c); }

/* Hierarchy from CONTRAST, not size — ART-GUIDE, and the sitewide ~17%
   type reduction. The heading is the same family and weight as every
   other h2 on the site; it is the colour and the rule above it that
   place it, not a larger number. */
.swk__h2 {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.55rem, 3.1vw, 2.35rem);
  line-height: 1.14;
  letter-spacing: -.015em;
  color: var(--ink);
}
.swk__h2 em { font-style: italic; color: rgba(10,10,10,.58); }

.swk__lede {
  margin: 0;
  max-width: 58ch;
  font-size: clamp(.95rem, 1.35vw, 1.06rem);
  line-height: 1.6;
  color: rgba(10,10,10,.74);
}

/* Plain facts, mono, no badge chrome — a pill would read as a status
   widget and invite a click that goes nowhere. Carried over from the
   section this replaces, where the reasoning still holds. */
.swk__state {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: .55rem;
  margin: 1.1rem 0 0;
  font-family: var(--font-mono);
  font-size: .64rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(10,10,10,.60);
}
.swk__dot {
  width: 6px; height: 6px;
  border: 1px solid rgba(10,10,10,.45);
  flex: 0 0 auto;
}

/* ── THE BODY: STEPS LEFT, SCREEN RIGHT ─────────────────────── */
.swk__body {
  display: grid;
  grid-template-columns: minmax(0, 27rem) minmax(0, 1fr);
  gap: clamp(1.6rem, 3.4vw, 3rem);
  align-items: start;
  margin-top: clamp(2rem, 4vw, 3rem);
}

/* WIDE: for table-shaped UIs. The newsroom CMS and The Shoot are both
   admin screens whose argument lives in a ROW — a title on the left and
   the state pill that gates it 800px to the right. A 580px window at
   1:1 cuts the row in half and loses the half that matters, so these
   two give the screen more of the grid and drop the crop scale a touch
   instead. Citadel keeps the default: its content is a card, not a row,
   so it reads better tighter and larger. */
.swk--wide .swk__body { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); }

/* ── STEPS ──────────────────────────────────────────────────── */
.swk__steps {
  list-style: none;
  margin: 0; padding: 0;
  display: flex; flex-direction: column;
  gap: .5rem;
}

.swk__btn {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .85rem;
  width: 100%;
  text-align: left;
  padding: .95rem 1rem;
  border: 1px solid rgba(10,10,10,.12);
  border-left-width: 2px;
  border-left-color: transparent;
  border-radius: 2px;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
  /* Interruptible: short, transform-free, and every property here is
     cheap to recompute mid-transition. */
  transition:
    background-color 220ms var(--ease-out-expo),
    border-color 220ms var(--ease-out-expo);
}
.swk__btn:hover { background: rgba(10,10,10,.035); }
.swk__btn:focus-visible {
  outline: 2px solid var(--sys-red-on-light, #a4132c);
  outline-offset: 2px;
}

.swk__btn[aria-selected="true"] {
  background: rgba(10,10,10,.045);
  border-left-color: var(--sys-red-on-light, #a4132c);
}

.swk__num {
  font-family: var(--font-mono);
  font-size: .66rem;
  letter-spacing: .1em;
  line-height: 1.5;
  padding-top: .12rem;
  color: rgba(10,10,10,.42);
  transition: color 220ms var(--ease-out-expo);
}
.swk__btn[aria-selected="true"] .swk__num { color: var(--sys-red-on-light, #a4132c); }

.swk__stitle {
  display: block;
  font-size: .96rem;
  line-height: 1.35;
  color: var(--ink);
  margin-bottom: .2rem;
}
.swk__sdesc {
  display: block;
  font-size: .855rem;
  line-height: 1.5;
  color: rgba(10,10,10,.66);
}

/* The progress hairline under an active step: the only piece of pure
   motion in the component, and it is not decorative — it is the timer,
   so the reader can see that the view is about to move and why. It is
   suppressed entirely when the walkthrough is paused or reduced. */
.swk__tick {
  grid-column: 1 / -1;
  height: 1px;
  margin-top: .7rem;
  background: rgba(10,10,10,.10);
  overflow: hidden;
  display: none;
}
.swk__btn[aria-selected="true"] .swk__tick { display: block; }
.swk__tick::after {
  content: "";
  display: block;
  height: 100%;
  background: var(--sys-red-on-light, #a4132c);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
.swk[data-swk-playing="true"] .swk__btn[aria-selected="true"] .swk__tick::after {
  animation: swk-tick var(--swk-dwell, 3400ms) linear forwards;
}
@keyframes swk-tick { 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 shadow that sits it
   on the bone. This is what turns a JPEG into "an application running",
   which is the entire complaint being answered. */
.swk__screen {
  position: relative;
  border: 1px solid rgba(10,10,10,.16);
  border-radius: 6px;
  background: var(--ink);
  overflow: hidden;
  box-shadow:
    0 1px 2px rgba(10,10,10,.06),
    0 12px 28px -12px rgba(10,10,10,.28);
}

.swk__chrome {
  display: flex; align-items: center;
  gap: .6rem;
  padding: .5rem .8rem;
  background: #16161a;
  border-bottom: 1px solid rgba(245,241,232,.10);
}
.swk__lights { display: flex; gap: .34rem; }
.swk__lights span {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: rgba(245,241,232,.20);
}
.swk__chrome-title {
  font-family: var(--font-mono);
  font-size: .6rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: rgba(245,241,232,.52);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* THE WINDOW. max-height is the hard cap Ethan set on screenshots and
   it is not to be raised — nothing on this site goes near full
   viewport height again. aspect-ratio gives it a shape at narrow
   widths; the cap wins at wide ones, which just makes the window
   shorter and wider over the same 1:1 pixels. */
.swk__viewport {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  max-height: clamp(280px, 34vw, 440px);
  overflow: hidden;
  background: var(--ink);
}

.swk__shot {
  position: absolute;
  top: 0; left: 0;
  width: 1600px;      /* intrinsic width of every capture used here */
  height: auto;
  max-width: none;    /* defeats the global img{max-width:100%} */
  transform-origin: 0 0;
  /* scale() applies before translate(), so a source point (rx,ry) lands
     at rx*s + tx. Solving for tx to put it at the window origin gives
     the -rx*s below. */
  transform:
    translate3d(calc(var(--rx, 0) * var(--s, 1) * -1px),
                calc(var(--ry, 0) * var(--s, 1) * -1px), 0)
    scale(var(--s, 1));
  opacity: 0;
  visibility: hidden;
  transition:
    transform 760ms var(--ease-out-expo),
    opacity 300ms linear,
    visibility 0s linear 300ms;
  will-change: transform;
}
.swk__shot.is-active {
  opacity: 1;
  visibility: visible;
  transition:
    transform 760ms var(--ease-out-expo),
    opacity 300ms linear,
    visibility 0s;
}

.swk__cap {
  margin: .85rem 0 0;
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .07em;
  line-height: 1.5;
  color: rgba(10,10,10,.56);
}

/* ── STACKED ────────────────────────────────────────────────── */
@media (max-width: 900px) {
  /* .swk--wide must be re-stated here, not just .swk__body: it is a
     two-class selector and would otherwise outrank this block at every
     width, which left the steps overflowing the viewport at 390. */
  .swk__body,
  .swk--wide .swk__body { grid-template-columns: minmax(0, 1fr); }
  /* Screen first on narrow: the picture is the argument and the steps
     read as its legend underneath. */
  .swk__screen-col { order: -1; }
  .swk__btn { padding: .8rem .85rem; }
  .swk__sdesc { font-size: .82rem; }
}

/* ── REDUCED MOTION: A GENUINE STATIC FALLBACK ──────────────── */
/* Not "the same thing but instant". The walkthrough does not auto-run,
   the pan does not animate, and the timer hairline never appears — the
   steps stay as a clickable list over a still frame, which is a
   complete and usable version of the same information. */
@media (prefers-reduced-motion: reduce) {
  .swk__shot {
    transition: opacity 1ms linear, visibility 0s;
  }
  .swk__btn { transition: none; }
  .swk__tick { display: none !important; }
  .swk[data-swk-playing="true"] .swk__btn[aria-selected="true"] .swk__tick::after {
    animation: none;
  }
}


/* ════════════════════════════════════════════════════════════
   T-1170 · 2026-08-14
   ════════════════════════════════════════════════════════════ */

/* ── 1. THE SITE.CSS SCREENSHOT CAP MUST LAND ON THE WINDOW, ──
      NOT ON THE THING BEING LOOKED AT.

   site.css §T-1146 caps every application capture on the site:

       img[src*="/gallery/systems/"],
       img[src*="/apps/screens/"] {
         max-height: clamp(280px, 34vw, 440px);
         object-fit: cover;
         object-position: top center;
       }

   That rule is right for a capture dropped into a page slot, and it
   is exactly wrong for this component, where the <img> is not the
   picture the reader sees — it is the SOURCE the window slides over.
   Capped, the 1600x1000 file is squashed to 1600x440 and cover-cropped
   to its own top strip, so every --ry below ~440 aims at pixels that
   have already been thrown away and every step of a walkthrough shows
   the same band of the same file. That is precisely what /media was
   doing: the two steps that point at two different regions of one
   screen rendered as pixel-identical twins.

   `img[src*=...]` is (0,1,1) — an element plus an attribute — so the
   single class `.swk__shot` (0,1,0) LOSES to it no matter what order
   the files load in. It has to be beaten on specificity, which is why
   this is `.swk__viewport .swk__shot` (0,2,0) and not a rewrite of
   `.swk__shot` above.

   The cap itself is not being defeated, it is being MOVED: the same
   clamp() lives on .swk__viewport, so the rendered screenshot is still
   never taller than 440px. Ethan's ceiling is intact; it is now the
   window that obeys it instead of the source. */
.swk__viewport .swk__shot {
  max-height: none;
  max-width: none;
  object-fit: fill;
  object-position: 0 0;
}

/* ── 2. THE PLATE ─────────────────────────────────────────────
   Ethan, on the whole site: "all the screenshot placements aren't
   clearly separated and defined come on lots of cluster."

   The component already framed the capture in a bezel. What it did
   not do was separate the WALKTHROUGH from the page: `.swk` paints
   itself bone (#faf8f3) and on /media it sits inside a .mflow--light
   band, which is also bone. Bone on bone, with two 1px hairlines, is
   not an object — it is a hole with a seam. So on this page the
   walkthrough is a PLATE: its own slightly lifted ground, a real
   border on all four sides, a radius, and a shadow that sits it on
   the band. Three surfaces now, each unambiguous: bone band, lifted
   plate, black screen.

   It is `u-feature` in the .mflow grid, so its left edge is the same
   186px (at 1440) as .mledger, .mpipe and every u-feature figure on
   the page. No new rail. */
.swk--plate {
  background: #fffdf8;
  border: 1px solid rgba(10,10,10,.15);
  border-radius: 5px;
  padding: clamp(1rem, 2.2vw, 1.75rem);
  box-shadow:
    0 1px 2px rgba(10,10,10,.04),
    0 20px 44px -30px rgba(10,10,10,.40);
}
/* The full-bleed shelf hairlines belonged to a bone band that ran edge
   to edge. The plate has a border now, so they would draw a second
   line 1px inside the first. */
.swk--plate::before,
.swk--plate::after { content: none; }

/* .swk__body's top margin separated it from a .swk__head that no
   longer lives inside the component — the heading and lede are
   ordinary .mflow children on the page's content column. */
.swk--plate .swk__body { margin-top: 0; }

/* A touch more room for the screen than the shared .swk--wide value:
   the plate is inset from the feature column by its own padding, and
   the steps do not need 20rem to read. (0,3,0) beats .swk--wide
   .swk__body at (0,2,0). */
.swk--plate.swk--wide .swk__body {
  grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
}

/* THE SCREEN CENTRES AGAINST THE STEPS. A four-step walkthrough is
   taller than its own screen — measured at 1440: steps 789px, screen
   plus caption 517px — and with align-items:start the whole 272px
   difference is dumped underneath the picture as one empty white
   corner. Centred, it becomes a margin above and below, which reads
   as the picture being set into the plate rather than as a hole. The
   three-step walkthrough measures 519 against 517 and is unaffected. */
.swk--plate .swk__body { align-items: center; }

/* CONTRAST, measured on #fffdf8 rather than assumed. rgba(10,10,10,.42)
   composites to #969696 on this ground = 2.9:1, which fails at any
   size; .62 composites to #666663 = 5.7:1. Same for the caption, which
   was .56 (4.58:1, passing by 0.08) and is now .62. */
.swk--plate .swk__num { color: rgba(10,10,10,.62); }
.swk--plate .swk__btn[aria-selected="true"] .swk__num { color: var(--sys-red-on-light, #a4132c); }
.swk--plate .swk__sdesc { color: rgba(10,10,10,.68); }

/* The screen is a literal black, not var(--ink): --ink is deliberately
   inverted to bone inside some light scopes on this site, and a bezel
   that resolves to bone puts a bone rectangle where the application
   should be. Nothing here reads a token that can flip. */
.swk--plate .swk__screen { background: #0a0a0a; border-color: rgba(10,10,10,.22); }
.swk--plate .swk__viewport { background: #0a0a0a; }

/* ── 3. THE CAPTION NAMES THE SCREEN ──────────────────────────
   "a caption that says what it is". One line per step, only the
   active one shown, so the reader is never looking at an unlabelled
   picture. Height is reserved for two lines so the plate does not
   jump when a longer caption comes up mid-sequence — the failure this
   component is most prone to is looking right at rest and moving
   under the reader mid-pan. */
.swk__caps {
  margin-top: .8rem;
  min-height: 2.6em;
}
.swk--plate .swk__cap,
.swk__caps .swk__cap {
  display: none;
  margin: 0;
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .06em;
  line-height: 1.5;
  color: rgba(10,10,10,.62);
}
.swk__caps .swk__cap.is-active { display: block; }
.swk__cap-n {
  color: var(--sys-red-on-light, #a4132c);
  margin-right: .5rem;
}

/* ── 4. NARROW ────────────────────────────────────────────────
   The window keeps 1:1 — the whole point of the component is that the
   rendered scale does not depend on the page width — so on a phone it
   simply shows LESS of the application, not a shrunken all of it. A
   16/10 window on a 315px column is 197px tall, which is a letterbox;
   4/3 buys back 40px of the interface without going near the 280px
   cap that clamp() imposes at this width. */
@media (max-width: 900px) {
  .swk--plate { padding: clamp(.85rem, 3vw, 1.2rem); }
  .swk--plate.swk--wide .swk__body { grid-template-columns: minmax(0, 1fr); }
  .swk--plate .swk__viewport { aspect-ratio: 4 / 3; }
  .swk__caps { min-height: 3.4em; }
}

/* ── THE WINDOW WAS TOO SMALL TO READ THROUGH ──────────────────────────
   Ethan: "the screenshots haven't been scaled correctly."

   Measured on /media at 1440: the viewport resolved to 689 x 430 while the
   capture inside it renders at 1600 x 1000 at scale 1. So the window showed
   43% of the screen's width — correct at 1:1, and useless, because you were
   reading a slice through a letterbox.

   The cap came from site.css's T-1135 rule, which existed to stop bare
   screenshots dominating a page at 1210px tall. That reasoning does not
   apply to a windowed walkthrough: the window IS the containment. So the
   window gets bigger and the steps column gives up the room.
   ──────────────────────────────────────────────────────────────────────── */
.swk__body { grid-template-columns: minmax(0, 21rem) minmax(0, 1fr); }
.swk--wide .swk__body { grid-template-columns: minmax(0, 18rem) minmax(0, 1fr); }

.swk__viewport {
  max-height: clamp(300px, 44vw, 620px);
}

@media (max-width: 900px) {
  .swk__viewport { max-height: clamp(240px, 60vw, 460px); }
}

/* NOT taking the full column. That was tried and it broke the plate's
   borders on /media — the plate is designed against the feature column's
   width and its frame does not survive being stretched past it.
   The window gets its extra size from the steps column and the height cap
   instead, both of which are inside the plate. */
