/* ============================================================
   /instructors/create-an-assignment — the full-canvas version
   of the wireframe walkthrough.

   Loaded *after* walkthrough.css, which supplies the --wt-* ink
   tokens and the page's front matter (hero, fact band, flow
   strip). Everything here is prefixed .wt2- and governs the
   scroll rig only, so the two versions can be compared without
   either affecting the other.

   The rig: one full-bleed grid cell holds a sticky stage (the
   wireframe, sized to whatever room the stage has — see "How big
   the drawing is allowed to get" below) and, in the same cell, a
   track of eight step-height blocks. Each step carries a card that
   sticks near the bottom right of the viewport, clear of the
   screen's own content, which is left-aligned. js/walkthrough-rig.js lights the active step, swaps
   the frame, and drives the flourishes:

     · the pointer glides to whatever the step is about (--cx/--cy
       per data-active, transitioned)
     · the wireframe leans with the scroll and settles back
     · every line is hand-drawn geometry, and a screen's lines draw
       themselves the first time that screen is shown (see "the
       drawn line" below)
     · the statement types itself; the slider counts up to 30; the
       match counts tick up from zero
     · each frame's elements rise in with a short stagger

   Every flourish is off under prefers-reduced-motion, and without
   JS the rig unstacks into a plain vertical read.
   ============================================================ */

:root {
  --wt2-card-bg: rgba(250, 249, 246, 0.93);
  --wt2-scrim: rgba(250, 249, 246, 0);
}
html.dark-mode {
  --wt2-card-bg: rgba(10, 10, 14, 0.9);
  --wt2-scrim: rgba(0, 0, 0, 0);
}
@supports (background: color-mix(in srgb, red 50%, transparent)) {
  :root { --wt2-card-bg: color-mix(in srgb, var(--page-bg) 93%, transparent); }
  html.dark-mode { --wt2-card-bg: color-mix(in srgb, var(--page-bg) 90%, transparent); }
}

/* The rig is wider than the text column it sits in. Clip rather than hide,
   so the sticky stage still positions against the viewport. This rule is
   safe to write unscoped because the stylesheet is loaded by one page. */
.site-main { overflow-x: clip; }

/* ============================================================
   The reader's own cursor, while the rig is on screen

   The wireframe has an animated pointer of its own, drawn to look
   like a real cursor. While that is moving around inside the
   window, the reader's actual system cursor needs to read as
   plainly not that — so js/walkthrough-rig.js toggles this class
   while any part of #wt2 is in the viewport (an IntersectionObserver,
   not a scroll listener), and the ordinary arrow is swapped for a
   soft circle. It reverts the moment the rig leaves view, in
   either direction.

   Set on <html> rather than every element, so a real link or button
   elsewhere on the page that already declares its own cursor keeps
   it; this only fills in for the elements that had no opinion.
   ============================================================ */
html.wt2-scrolly-cursor {
  cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%3E%3Ccircle%20cx%3D%2213%22%20cy%3D%2213%22%20r%3D%2210%22%20fill%3D%22none%22%20stroke%3D%22%23111118%22%20stroke-opacity%3D%220.28%22%20stroke-width%3D%221.5%22/%3E%3Ccircle%20cx%3D%2213%22%20cy%3D%2213%22%20r%3D%228.5%22%20fill%3D%22%235a5ad1%22%20fill-opacity%3D%220.82%22%20stroke%3D%22%23fffefb%22%20stroke-width%3D%222%22/%3E%3C/svg%3E") 13 13, auto;
}
html.dark-mode.wt2-scrolly-cursor {
  cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%3E%3Ccircle%20cx%3D%2213%22%20cy%3D%2213%22%20r%3D%2210%22%20fill%3D%22none%22%20stroke%3D%22%23f1f1f5%22%20stroke-opacity%3D%220.3%22%20stroke-width%3D%221.5%22/%3E%3Ccircle%20cx%3D%2213%22%20cy%3D%2213%22%20r%3D%228.5%22%20fill%3D%22%238a8aff%22%20fill-opacity%3D%220.85%22%20stroke%3D%22%2315151e%22%20stroke-width%3D%222%22/%3E%3C/svg%3E") 13 13, auto;
}

/* ============================================================
   Full-bleed scroll rig
   ============================================================ */
/* The page itself is the scroller (there is no inner scrolling box), so the
   snap type is declared on the root rather than on .wt2. :has() rather than
   a bare html selector, so this cannot leak onto some other page that happens
   to load this stylesheet later. */
/* Only where the rig is actually running: without JS the steps collapse to
   ordinary short blocks (see the html:not(.wt2-js) rules below), and snapping
   to every one of those would stop the page dead at each in turn. */
html.wt2-js:has(#wt2) { scroll-snap-type: y proximity; }
/* ...and only until the reader is through the screens. Past the last step the
   page is ordinary again — the closing cards and the site footer under them
   are just page — and any snap point still in force there pulls the reader
   back up off the end of the document. walkthrough-rig.js sets this class. */
html.wt2-js.wt2-free:has(#wt2) { scroll-snap-type: none; }
/* ...and not while the rig is flying the page to the next step itself (space,
   or a control the reader pressed — see glideToY in js/walkthrough-rig.js).
   Snapping would fight a scroll it did not start, and site.css's own
   `scroll-behavior: smooth` would smooth every frame of the animation into
   the next one, so the flight could never keep up with itself. Both come back
   on landing, which is exactly on the snap position anyway. */
html.wt2-js.wt2-gliding:has(#wt2) { scroll-snap-type: none; scroll-behavior: auto; }
/* Body deliberately declares no snap position. It used to align its own top,
   from when the snap type here was mandatory and scrolling up out of the
   first step had nowhere valid to land. Under proximity that is no longer
   needed — an upward scroll simply leaves the last snap point behind — and
   it actively trapped the reader: with the top of the document a snap
   position of its own, every small scroll from there was inside the
   proximity threshold and got pulled straight back, so the heading and lede
   above the rig could not be scrolled past at all. With no snap position
   declared there, everything above the first step scrolls like ordinary
   page, and the rig only takes over once the reader reaches it. */
/* The closing section deliberately declares no snap position either. It used
   to align its own bottom to the viewport, to give the reader somewhere to
   land past the last step. That put the section's end at the foot of the
   screen — which is precisely where the site footer begins, so the footer sat
   just off the bottom of the viewport and every attempt to scroll to it was
   inside the proximity threshold and pulled straight back. Releasing the snap
   entirely past the last step (above) is what gives the reader somewhere to
   go, and it does not need a snap point of its own to do it. */

/* ---- How big the drawing is allowed to get ----
   The window used to stop at 1240×800 whatever room it had, which on a
   laptop with the navigation folded away left a third of the width and a
   tenth of the height empty. It now takes the room it is given, under three
   limits:

     --wt2-room-h   the height the stage actually has, which is the same
                    number .wt2-stage arrives at (100vh, less the banner
                    above it and its own padding). Written out rather than
                    read off the box because the card, which is in a
                    different subtree, has to arrive at the same answer.
     --wt2-w        1680px at the widest — past that the drawing is a
                    diagram blown up rather than a browser window — and
                    never wider than 1.95× its own height, so a short
                    viewport gets a window and not a letterbox. The
                    max(1240px, …) floor is what keeps that second limit
                    from ever making the window narrower than it was
                    before this change.
     --wt2-h        1000px at the tallest, for the same reason.

   Both are declared here rather than on .wt2-canvas because .wt2-cardhold
   needs them too: the floating card's right edge is lined up with the
   window's, and the two live in different grid cells. */
.wt2 {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-top: 2.5rem;
  --wt2-room-h: calc(100vh - var(--banner-h) - 2.2rem);
  --wt2-w: min(1680px, max(1240px, var(--wt2-room-h) * 1.95));
  --wt2-h: 1000px;
}
/* Above the sidebar breakpoint the canvas is the window minus the sidebar. */
@media (min-width: 1280px) {
  .wt2 {
    width: calc(100vw - var(--sidebar-w));
    margin-left: calc(50% - (100vw - var(--sidebar-w)) / 2);
  }
}
.wt2-stage, .wt2-track { grid-area: 1 / 1; }

.wt2-stage {
  position: sticky;
  top: var(--banner-h);
  height: calc(100vh - var(--banner-h));
  /* A grid item's automatic minimum is the min-content of what is inside it,
     so on a narrow screen the widest thing in the wireframe — the address
     text, a table — widened the stage past its own column instead of being
     clipped or scrolled by the window that is supposed to contain it. */
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.1rem 2.4rem 1.1rem 1.4rem;
  z-index: 0;
}
/* Track sits above the stage (so a step's floating card renders over the
   wireframe beneath it), but track itself is just a positioning box with
   nothing to click — without this, its own empty area was intercepting
   clicks meant for whatever sits behind it in the stage, namely the dot
   rail. Each step re-disables this itself, and the card re-enables it again,
   so nothing about the card or the wireframe's own controls changes. */
.wt2-track { position: relative; z-index: 1; pointer-events: none; }

/* Steps are the scroll runway. They must not swallow clicks meant for the
   wireframe or the dot rail underneath — only the card takes pointer events. */
.wt2-step {
  min-height: 104vh;
  display: flex;
  /* The card is lined up with the window, not with the page. The step used to
     be full-bleed with a 2.6rem gutter, which put the card's right edge at the
     right-hand end of the *page* — fine while the window was nearly that wide,
     and wrong the moment it was not: at 2560px the window stopped at 1240px
     and the card floated 470px clear of the drawing it was describing. So the
     step centres a holder of the window's own width instead, and the card
     right-aligns inside that. See .wt2-cardhold. */
  justify-content: center;
  padding: 0;
  pointer-events: none;
  scroll-margin-top: var(--banner-h);
  /* A scroll that would otherwise carry past a step, straight into the one
     after it, lands here instead. mandatory looked like the obvious choice
     for "always stop" — it is not used, because with a box taller than the
     viewport it measurably trapped real wheel scrolling: a reader could get
     stuck able to move within a step but never past it, recoverable only
     by a keyboard PageDown. proximity does not do that in any pattern
     tested, and scroll-snap-stop: always still forces a stop at every step
     that lies along the scroll, which is what keeps a single fast flick
     from skipping past more than one. The scroll-margin above already
     shifts the landing point clear of the fixed top banner, so the same
     offset does double duty for the anchor links and for this. */
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
/* The reader picks this rig's pace deliberately; a forced snap after every
   gesture is exactly the sort of motion prefers-reduced-motion asks to be
   spared, so it reverts to ordinary free scrolling. */
@media (prefers-reduced-motion: reduce) {
  html:has(#wt2) { scroll-snap-type: none; }
}
/* Every step, the first included, has to be at least as tall as the holder
   inside it, or sticky clamps the holder to the step's box and the card
   cannot reach the middle. */

/* The card is centred, not parked at the foot of the screen. Centring a
   sticky element by transform does not work: sticky clamps the element to its
   containing block using the untransformed box, so a tall card gets pushed off
   the top. Instead the sticky element is a holder exactly the height of the
   stage, and the card is centred inside it by ordinary flexbox — its height
   never enters into it. The holder is transparent to the pointer so the
   wireframe underneath stays reachable. */
.wt2-cardhold {
  position: sticky;
  top: var(--banner-h);
  height: calc(100vh - var(--banner-h));
  /* The window's own width, arrived at the same way .wt2-canvas arrives at it
     — one is centred in the stage's box (1.4rem and 2.4rem of padding), the
     other in the step's (none), so the holder is the narrower of the two by
     exactly the 1rem that difference comes to. The card right-aligns in here,
     which puts its right edge on the window's however wide the window is. */
  width: min(calc(100% - 4.8rem), calc(var(--wt2-w) - 1rem));
  display: flex;
  align-items: center;
  /* True vertical centre reads as slightly low — the eye weighs the top of
     a block of reading more than the bottom. The extra bottom padding
     shrinks the box flex centres within, so the middle of that shorter box
     — where the card actually lands — sits above true centre without
     touching the centring math itself. */
  padding-bottom: 9vh;
  justify-content: flex-end;
  pointer-events: none;
}

/* ---- The floating card ---- */
/* The card parks at the middle of the stage, not its foot. Sticky pins the
   card's top edge at the stage's midpoint, and the -50% shift puts its centre
   there instead — the card's height is not known in advance, so it cannot be
   centred by offset alone. Because the card sits at the top of its step, the
   moment it pins is the moment the step becomes active, so it fades in already
   in place rather than travelling up the screen first.

   The shift has to be carried through the entrance transform below, which
   would otherwise replace it. */
.wt2-card {
  pointer-events: auto;
  width: min(24rem, 42%);
  padding: 1.4rem 1.55rem 1.5rem;
  border: 1px solid var(--border-subtle);
  border-radius: 14px;
  background: var(--wt2-card-bg);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  visibility: hidden;
  transform: translateY(1.5rem);
  transition: opacity 0.38s ease, transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    visibility 0.38s;
}
@supports (backdrop-filter: blur(6px)) {
  .wt2-card { backdrop-filter: blur(7px) saturate(1.12); }
}
.wt2-step.is-active .wt2-card {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.page-content .wt2-kicker {
  font-family: "Roboto Mono", monospace;
  font-size: 0.64rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin: 0 0 0.35rem;
}
.page-content .wt2-kicker b { color: var(--wt-accent); font-weight: 700; margin-right: 0.5em; }
.page-content h2.wt2-title b { color: var(--wt-accent); font-weight: 700; margin-right: 0.5em; }
.page-content h2.wt2-title {
  font-family: "Roboto Mono", monospace;
  font-weight: 700;
  font-size: clamp(1.05rem, 1.5vw, 1.28rem);
  line-height: 1.32;
  color: var(--text-strong);
  margin: 0 0 0.7rem;
}
/* A link in a title is underlined. The colour alone reads as emphasis rather
   than as somewhere to go, and "click here" has to be visibly a way off this
   page — otherwise "here" looks like the page the reader is already on. */
.page-content h2.wt2-title a {
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 0.07em;
}
.page-content .wt2-card p {
  font-size: 0.83rem;
  line-height: 1.68;
  margin: 0 0 0.7rem;
}
.page-content .wt2-card p:last-child { margin-bottom: 0; }
.page-content .wt2-card ul {
  margin: 0.6rem 0 0;
  padding-left: 1.05rem;
}
.page-content .wt2-card li {
  font-size: 0.8rem;
  line-height: 1.6;
  margin: 0 0 0.42rem;
}
.page-content .wt2-card b {
  font-family: "Roboto Mono", monospace;
  font-size: 0.94em;
  font-weight: 700;
  color: var(--text-strong);
}
.page-content .wt2-kicker b { font-size: inherit; }
/* A short bold phrase naming a control ("+ Create", "+ Add topic") reads as
   a UI label, not prose, so it shouldn't break across two lines the way an
   ordinary run of bold words safely could. */
.page-content b.wt2-nowrap { white-space: nowrap; }
/* Depth on request. The card has to stay short — it floats over the screen it
   is describing — so the trade-offs live in a drawer rather than in the body.
   site.css styles every <details> inside .page-content as an FAQ question, so
   that has to be undone first. */
.page-content details.wt2-note {
  border-bottom: 0;
  padding: 0.1rem 0 0.1rem 0.8rem;
}
.wt2-note {
  margin: 0.9rem 0 0;
  padding: 0.1rem 0 0.1rem 0.8rem;
  border-left: 2px dashed var(--wt-faint);
}
.page-content .wt2-note > summary {
  list-style: none;
  cursor: pointer;
}
.page-content .wt2-note > summary::-webkit-details-marker { display: none; }
.page-content .wt2-note > summary::after {
  content: " +";
  font-weight: 700;
}
.page-content .wt2-note[open] > summary::after { content: " \2212"; }
.page-content .wt2-note > summary:hover { color: var(--link-hover); }
.page-content .wt2-note[open] > summary { margin-bottom: 0.45rem; }
.page-content .wt2-note-label {
  font-family: "Roboto Mono", monospace;
  font-size: 0.58rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--wt-accent);
  margin: 0 0 0.25rem;
}
/* A pointer to the page that covers the note's subject properly. Set apart
   from the label it sits on — not tracked, not uppercase, and in the body
   colour — so the label still reads as one word-group and the link reads as
   an aside rather than as more label. */
.page-content .wt2-note-see {
  font-family: Roboto, -apple-system, sans-serif;
  font-size: 0.68rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--wt-ink);
}
.page-content .wt2-note-see a { color: var(--wt-accent); }
.page-content .wt2-note p { font-size: 0.78rem; line-height: 1.6; margin: 0; }

/* ---- Dot rail ---- */
/* Beside the window, not at the edge of the page. The two are the same place
   at any width where the window fills its stage, which is most of them; where
   it does not, the rail follows it in rather than being left standing on its
   own in the margin. The offset is the room left over on one side — the stage
   centres the window in its content box, so half of what is spare — and
   adding it to the 0.75rem keeps the rail exactly 1.65rem clear of the
   window's right edge whether there is anything spare or not. */
.page-content ol.wt2-dots {
  position: absolute;
  right: calc(0.75rem + max(0px, (100% - 3.8rem - var(--wt2-w)) / 2));
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  list-style: none;
  margin: 0;
  padding: 0;
  z-index: 3;
}
.page-content ol.wt2-dots li { margin: 0; }
.wt2-dots a {
  display: block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--wt-faint);
  transition: transform 0.25s ease, background-color 0.25s ease, border-color 0.25s ease;
}
.wt2-dots a.is-done { border-color: var(--wt-accent); }
.wt2-dots a.is-on {
  background: var(--wt-accent);
  border-color: var(--wt-accent);
  transform: scale(1.45);
}

/* ============================================================
   The window
   ============================================================ */

/* The canvas is exactly the window's box. The pointer sits in here rather
   than inside the window, because anything that animates inside a filtered
   element forces the whole filter to be re-rendered every frame. */
.wt2-canvas {
  position: relative;
  width: min(100%, var(--wt2-w));
  height: min(100%, var(--wt2-h));
  /* The type inside the window is sized against this box rather than against
     the viewport (see .wt2-window below), so the box has to be a query
     container. Safe to declare `size` on: both its dimensions are given
     above, so nothing inside it is being asked to determine it. The no-JS
     branch, where the canvas is height:auto, turns this back off. */
  container-type: size;
}
.wt2-window {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  border: 2px solid var(--wt-ink);
  border-radius: 8px;
  background: var(--wt-paper);
  box-shadow: 6px 6px 0 var(--wt-cast);
  overflow: hidden;
  /* Sized in px on purpose: the wireframe is a diagram, like the site
     chrome, and should not ride the fluid text root.

     Three terms, and the order of them is the whole rule:

       min(8px + 0.62vw, 17px)   what this was, and the floor. Wherever the
                                 window is the size it always was — which is
                                 every viewport under about 1500px, where the
                                 canvas already fills its stage — the answer is
                                 the number that shipped, to the pixel. Nothing
                                 gets smaller anywhere.
       min(1.371cqi, 2.6cqb)     and where the window has grown, the drawing
                                 grows with it. 1.371% of the width is 17px at
                                 the old 1240px cap, so type only outruns the
                                 floor once the box is proportionally wider
                                 than the drawing was designed against.
       2.6cqb                    the limiter. The tallest screen on the page is
                                 what decides whether anything is cut off, and
                                 measured across every screen of every
                                 walkthrough that ceiling sits at 2.94% of the
                                 window's height once the window is 1240px or
                                 wider. 2.6% keeps a screen's worth of margin
                                 under it.

     22px at the top, which is where the student pages already stopped. */
  font-family: "Roboto Mono", monospace;
  font-size: clamp(12px, max(min(8px + 0.62vw, 17px), min(1.371cqi, 2.6cqb)), 22px);
  color: var(--wt-ink);
}
/* --sway is scroll velocity, -1..1, decaying to zero once the page stops: the
   panel drifts a couple of pixels as the page moves, like paper on a desk.
   Transform only, so it composites and costs nothing. */
.wt2-window {
  transform: translateY(calc(var(--sway, 0) * 2px));
}

.wt2-chrome {
  display: flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.6em 0.9em;
  border-bottom: 2px solid var(--wt-ink);
  background: var(--wt-hatch);
}
.wt2-dot {
  width: 0.62em;
  height: 0.62em;
  border: 1.5px solid var(--wt-ink);
  border-radius: 50%;
}
.wt2-url {
  flex: 1;
  /* Without this the address text sets a floor under the whole window: a
     flex item's automatic minimum is its content, and a long URL that is
     also nowrap makes that floor wider than a narrow screen. The ellipsis
     below is the intended behaviour and cannot happen until the item is
     allowed to shrink past its text. */
  min-width: 0;
  margin-left: 0.6em;
  border: 1.5px dashed var(--wt-faint);
  border-radius: 3px;
  padding: 0.2em 0.7em;
  font-size: 0.72em;
  color: var(--text-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.3s ease;
}
.wt2-body { flex: 1; display: flex; align-items: stretch; min-height: 0; }

/* ---- Rail ---- */
.wt2-rail {
  position: relative;
  flex: 0 0 12.5em;
  padding: 1.1em 0.9em;
  border-right: 2px solid var(--wt-ink);
  background: var(--wt-hatch);
}
.wt2-railsel {
  display: flex;
  justify-content: space-between;
  gap: 0.4em;
  border: 1.5px solid var(--wt-faint);
  border-radius: 3px;
  padding: 0.4em 0.6em;
  font-size: 0.68em;
  color: var(--text-soft);
  margin-bottom: 0.9em;
}
.wt2-railbox { border: 1.5px solid var(--wt-ink); border-radius: 3px; }
.wt2-railhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4em;
  padding: 0.5em 0.55em;
  border-bottom: 1.5px solid var(--wt-ink);
  font-size: 0.68em;
  font-weight: 700;
  color: var(--wt-ink-strong);
}
.wt2-create {
  border: 1.5px solid var(--wt-accent);
  border-radius: 3px;
  padding: 0.15em 0.4em;
  font-weight: 500;
  color: var(--wt-accent);
  white-space: nowrap;
}
.page-content .wt2-railempty {
  font-family: "Roboto Mono", monospace;
  font-size: 0.64em;
  line-height: 1.5;
  color: var(--text-soft);
  margin: 0;
  padding: 0.7em 0.6em;
}
.wt2-railcard {
  display: none;
  padding: 0.6em 0.6em 0.65em;
  background: var(--wt-accent-soft);
  border-left: 3px solid var(--wt-accent);
}
.wt2-railcard b {
  display: block;
  font-size: 0.68em;
  line-height: 1.45;
  color: var(--wt-ink-strong);
}
.wt2-railcard span {
  display: block;
  font-size: 0.6em;
  line-height: 1.5;
  color: var(--text-soft);
}
.wt2-railcard span.wt2-badge {
  display: inline-block;
  margin-top: 0.5em;
  border: 1.5px solid var(--wt-accent);
  border-radius: 2px;
  padding: 0.12em 0.45em;
  font-size: 0.56em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--wt-accent);
}
.wt2-window[data-rail="scheduled"] .wt2-railempty { display: none; }
.wt2-window[data-rail="scheduled"] .wt2-railcard {
  display: block;
  animation: wt2-pop 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes wt2-pop {
  from { opacity: 0; transform: translateY(-0.4em) scale(0.97); }
  to { opacity: 1; transform: none; }
}

/* ---- The pointer ----
   One element, moved to whatever the step is about. Each frame sets its
   target as a percentage of the window, and the transition does the rest. */
.wt2-cursor {
  position: absolute;
  left: var(--cx, 8%);
  top: var(--cy, 12%);
  z-index: 4;
  width: 1.5em;
  height: 1.5em;
  opacity: var(--cop, 0);
  color: var(--wt-ink-strong);
  pointer-events: none;
  transition: left 0.775s cubic-bezier(0.34, 1.2, 0.42, 1),
    top 0.775s cubic-bezier(0.34, 1.2, 0.42, 1), opacity 0.3s ease;
}
.wt2-cursor svg {
  display: block;
  width: 100%;
  height: 100%;
  transform-origin: 25% 15%;
  transition: transform 0.16s ease-out;
}
.wt2-cursor.is-clicking svg { transform: scale(0.78); }
.wt2-cursor::after {
  content: "";
  position: absolute;
  left: -0.4em;
  top: -0.4em;
  width: 1.7em;
  height: 1.7em;
  border: 1.5px solid var(--wt-accent);
  border-radius: 50%;
  animation: wt2-pulse 2.2s ease-out infinite;
}
@keyframes wt2-pulse {
  0% { transform: scale(0.45); opacity: 0.9; }
  70% { transform: scale(1.4); opacity: 0; }
  100% { transform: scale(1.4); opacity: 0; }
}
/* Where the pointer goes is measured, not declared: the script reads the box
   of the control the screen names in data-click and sets --cx/--cy in pixels
   on the canvas. They are set on the canvas rather than the window because the
   pointer is a sibling of the window, so both have to inherit from the same
   place. The values below are only the state before the first screen lands. */
.wt2-canvas { --cx: 9%; --cy: 15%; --cop: 0; }

/* Whatever the pointer is on reads as pressed for the moment it is pressed. */
.wt2-next.is-pressed,
.wt2-create.is-pressed {
  background: var(--wt-accent);
  color: var(--wt-paper);
}
.wt2-mode.is-pressed,
.wt2-select.is-pressed,
.wt2-textarea.is-pressed,
.wt2-tl-date.is-pressed,
.wt2-pencil.is-pressed { background: var(--wt-accent-soft); }
/* Three the pointer presses that showed nothing for it: a tab, the Reactivate
   button, and — now that the reader can press it too — an entry in the list of
   topic counts. The first two were already being pressed with no acknowledgement
   before any of this was clickable. */
.page-content .wt2-tabs span.is-pressed,
.wt2-ghostbtn.is-pressed,
.page-content ul.wt2-options li.is-pressed { background: var(--wt-accent-soft); }
/* The knob is dragged, not just pressed, so it stays picked up for as long as
   the pointer is holding it rather than for the length of a click. */
.wt2-knob.is-pressed,
.wt2-knob.is-grabbed { transform: translate(-50%, -50%) scale(1.25); }

/* ---- Controls the reader can press ----
   A reader watching a hand press +Create can press +Create, and gets what the
   hand gets. js/walkthrough-rig.js marks the live controls .wt2-hot for as
   long as their screen is up — from the script, because which controls are
   live is what a frame's data-clicks says and no selector can read an
   attribute's contents.

   Deliberately no look of its own: no hand cursor, no hover fill, nothing
   that says "press me". This is meant to be found by the few readers who
   reach for a button because the drawing made them want to, and to stay
   unnoticed by everyone else. The mark is a hook for the script and for the
   tests; keep it that way. What a pressed control does show is the .is-pressed
   fill above, and that is only ever after the fact.

   So there is no .wt2-hot rule here, and there should not be one. */

/* ---- The slider, under the reader's own finger ----
   The slider is dragged rather than pressed. pan-y keeps a vertical swipe
   scrolling the page — the walkthrough is scrolled through, and a band across
   it that swallowed that would be a trap — while a sideways drag goes to the
   track. The track itself is two pixels tall; the band is what makes it
   something a finger can catch. */
.wt2-drag { touch-action: pan-y; }
.wt2-drag::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -0.9em;
  bottom: -0.9em;
}

/* ---- Frame stack ---- */
/* One grid cell, every screen stacked in it. The column is pinned to the room
   available rather than left to size itself: an implicit auto track takes the
   width of the *widest* screen on the page, so one screen with a wide table in
   it made every screen wider than the window, and the window quietly clipped
   the difference — which is what put content under the floating card. */
/* The row is pinned for the same reason, in the other direction. An implicit
   auto row takes the height of the *tallest* screen's content, and every
   screen in the cell then gets that height — so one screen running past the
   foot of the window pushed the Next row of all the others down past it too,
   since .wt2-actions is held to the bottom of its frame by an auto margin.
   The student walkthroughs, whose message threads are genuinely taller than
   the window, already clamped this on .wt2-student .wt2-frames; it belongs
   here, where every screen that fills its window can reach it. */
.wt2-frames {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
}
.wt2-frame {
  grid-area: 1 / 1;
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 2.4em 2.8em 2.2em;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s;
  /* Every screen is stacked in the same grid cell, and the one being left
     keeps its visibility for the length of the fade — so for a third of a
     second after a swap the old screen was still the topmost thing over the
     new one's controls, and a click meant for Next on the screen that had
     just arrived landed on Next on the screen that was going. visibility
     alone does not settle it, because it is precisely what the transition
     is holding on to. */
  pointer-events: none;
}
.wt2-frame.is-on { pointer-events: auto; }
/* One column for everything on a screen. The content starts at the left
   gutter, where the interface puts it, and every block in the frame — the
   prose, the slider, the table, the Next row — ends at the same right edge.
   Capping each block to its own width is what gave every paragraph a
   different margin and let the slider run the full width under the card.

   24rem is the card's own cap (min(24rem, 42%) never exceeds it), so
   reserving that plus a fixed gutter is always enough, on any window. A
   percentage here would not do the same job: the frame sits to the right
   of the wireframe's own rail, so "42%" written on .wt2-frame resolves
   against a narrower box than the card's own "42%" does, and undershoots
   by exactly the width of that rail. An earlier version also added rem
   and em together as if they were the same unit, which at this site's
   fluid root (up to ~21.5px, well past the 16px a plain "1rem" suggests)
   reserved far more than the card needed and left almost no room for the
   guide text beside the topic-count dropdown. */
@media (min-width: 901px) {
  .wt2-frame { padding-right: calc(24rem + 1.8rem); }
}

.wt2-frame.is-on { opacity: 1; visibility: visible; }
/* Elements rise in as the frame arrives. */
.wt2-frame.is-on > * { animation: wt2-rise 0.44s cubic-bezier(0.22, 1, 0.36, 1) both; }
.wt2-frame.is-on > :nth-child(1) { animation-delay: 0.03s; }
.wt2-frame.is-on > :nth-child(2) { animation-delay: 0.08s; }
.wt2-frame.is-on > :nth-child(3) { animation-delay: 0.13s; }
.wt2-frame.is-on > :nth-child(4) { animation-delay: 0.18s; }
.wt2-frame.is-on > :nth-child(5) { animation-delay: 0.23s; }
.wt2-frame.is-on > :nth-child(6) { animation-delay: 0.28s; }
.wt2-frame.is-on > :nth-child(7) { animation-delay: 0.33s; }
@keyframes wt2-rise {
  from { opacity: 0; transform: translateY(0.7em); }
  to { opacity: 1; transform: none; }
}

/* ============================================================
   Frame furniture
   ============================================================ */
/* Deliberately left to wrap however it likes, including inside a label. Three
   tab names do not fit on one line at 1280px — the frame has about 250px for
   them once the rail and the card's gutter are out — and letting the text
   break where it wants packs them into two lines rather than the three a
   whole-label wrap needs. It is not pretty, and the student pages, whose type
   is bigger, keep their labels whole instead (walkthrough-student.css). Here
   the 20px is worth more than the tidiness: it is the difference between the
   details panel below fitting the window and not. */
.wt2-tabs {
  display: flex;
  gap: 1.3em;
  border-bottom: 1.5px solid var(--wt-faint);
  margin-bottom: 1.2em;
  font-size: 0.7em;
}
.wt2-tabs span { padding-bottom: 0.5em; color: var(--text-soft); }
.wt2-tabs span.is-on {
  color: var(--wt-ink-strong);
  font-weight: 700;
  box-shadow: inset 0 -2px 0 var(--wt-accent);
}
.page-content .wt2-q {
  font-family: "Roboto Mono", monospace;
  font-size: 0.95em;
  font-weight: 700;
  line-height: 1.4;
  color: var(--wt-ink-strong);
  margin: 0 0 0.35em;
}
.page-content .wt2-sub {
  font-family: "Roboto Mono", monospace;
  font-size: 0.72em;
  line-height: 1.5;
  color: var(--text-soft);
  margin: 0 0 1.4em;
}
.page-content .wt2-guide {
  font-family: "Roboto Mono", monospace;
  font-size: 0.68em;
  line-height: 1.75;
  color: var(--text-soft);
  margin: 1.6em 0 0;
}
.page-content .wt2-flabel {
  font-family: "Roboto Mono", monospace;
  font-size: 0.66em;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin: 0 0 0.45em;
}
.wt2-input, .wt2-textarea {
  border: 1.5px solid var(--wt-ink);
  border-radius: 4px;
  padding: 0.6em 0.75em;
  font-size: 0.76em;
  line-height: 1.6;
  color: var(--wt-ink-strong);
  background: var(--wt-paper);
}
.wt2-textarea { min-height: 4.5em; }
.wt2-input + .wt2-flabel, .wt2-textarea + .wt2-flabel { margin-top: 1.2em; }
/* The field the step is about gets a soft ring, like a focused input. */
.wt2-live { box-shadow: 0 0 0 3px var(--wt-accent-soft); }
.wt2-caret {
  display: inline-block;
  width: 1.5px;
  height: 1.05em;
  margin-left: 1px;
  vertical-align: text-bottom;
  background: var(--wt-accent);
  animation: wt2-blink 1.1s steps(1, end) infinite;
}
@keyframes wt2-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

.wt2-actions {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1em;
  margin-top: auto;
  padding-top: 1.2em;
}
.wt2-next {
  border: 1.5px solid var(--wt-ink-strong);
  border-radius: 4px;
  padding: 0.4em 1.4em;
  /* A button's label is one line. "Sign Up" was breaking across two inside the
     box on the registration screen, which reads as a button that did not fit
     rather than as a two-word label. */
  white-space: nowrap;
  font-size: 0.74em;
  font-weight: 700;
  color: var(--wt-ink-strong);
  box-shadow: 2px 2px 0 var(--wt-cast);
}
.wt2-hint { font-size: 0.66em; line-height: 1.5; color: var(--text-soft); }
.wt2-ghostbtn {
  align-self: flex-start;
  border: 1.5px solid var(--wt-faint);
  border-radius: 4px;
  padding: 0.3em 0.8em;
  font-size: 0.68em;
  color: var(--text-soft);
  margin-bottom: 1.2em;
}

.wt2-tip {
  position: relative;
  display: inline-block;
  max-width: 17em;
  border-radius: 4px;
  padding: 0.5em 0.75em;
  background: var(--wt-tip-bg);
  color: var(--wt-tip-text);
  font-size: 0.68em;
  line-height: 1.5;
}
.wt2-tip::before {
  content: "";
  position: absolute;
  top: -0.42em;
  left: 1.2em;
  border-left: 0.42em solid transparent;
  border-right: 0.42em solid transparent;
  border-bottom: 0.42em solid var(--wt-tip-bg);
}
.wt2-tip-w::before {
  top: 0.6em;
  left: -0.42em;
  border: 0.42em solid transparent;
  border-right-color: var(--wt-tip-bg);
  border-left: 0;
}

.wt2-hatch-box {
  border: 1.5px dashed var(--wt-faint);
  border-radius: 4px;
  min-height: 5em;
  background: repeating-linear-gradient(45deg, transparent 0 6px, var(--wt-hatch) 6px 12px);
}
.wt2-frame > .wt2-hatch-box:last-child { flex: 1; }

/* ---- Dropdown ---- */
/* The open menu is drawn over the screen, as a real one is, so the column it
   sits in reserves the height instead of letting the menu cover what follows. */
.wt2-selectrow { display: flex; align-items: flex-start; gap: 2.2em; }
.wt2-selectwrap { flex: 0 0 9em; min-height: 13em; }
/* The menu hangs off the select itself, not off the space reserved for it. */
.wt2-selecthold { position: relative; }
.wt2-selectaside { flex: 1; min-width: 0; }
.wt2-select {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border: 1.5px solid var(--wt-ink-strong);
  border-radius: 4px;
  padding: 0.45em 0.7em;
  font-size: 0.8em;
  font-weight: 700;
  color: var(--wt-ink-strong);
}
.page-content ul.wt2-options {
  position: absolute;
  z-index: 2;
  top: calc(100% + 0.3em);
  left: 0;
  right: 0;
  list-style: none;
  margin: 0;
  padding: 0.3em;
  border: 1.5px solid var(--wt-ink);
  border-radius: 4px;
  background: var(--wt-paper);
  box-shadow: 3px 3px 0 var(--wt-cast);
}
.page-content ul.wt2-options li {
  font-family: "Roboto Mono", monospace;
  font-size: 0.74em;
  line-height: 1.75;
  color: var(--wt-ink);
  padding: 0 0.5em;
  margin: 0;
  border-radius: 2px;
}
.page-content ul.wt2-options li.is-on {
  background: var(--wt-accent);
  color: #fff;
  font-weight: 700;
}
html.dark-mode .page-content ul.wt2-options li.is-on { color: #10101a; }

/* ---- Statement anatomy ---- */
/* ---- Topic set ---- */
.page-content .wt2-ghost {
  font-family: "Roboto Mono", monospace;
  font-size: 0.68em;
  line-height: 1.5;
  color: var(--text-soft);
  border: 1.5px dashed var(--wt-faint);
  border-radius: 4px;
  padding: 0.5em 0.7em;
  margin: 0.3em 0 0;
}

/* ---- Slider ---- */
.wt2-slider { padding: 2.4em 0 0.6em; }
.wt2-track-line { position: relative; height: 2px; background: var(--wt-ink); }
.wt2-knob {
  position: absolute;
  top: 50%;
  left: var(--knob, 50%);
  transform: translate(-50%, -50%);
  width: 1.2em;
  height: 1.2em;
  border-radius: 50%;
  background: var(--wt-accent);
  transition: left 0.9s cubic-bezier(0.22, 1, 0.36, 1);
}
.wt2-knob b {
  position: absolute;
  bottom: 1.5em;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.8em;
  font-weight: 700;
  color: var(--wt-ink-strong);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.wt2-scale {
  position: relative;
  display: flex;
  justify-content: space-between;
  margin-top: 1.1em;
  font-size: 0.62em;
  color: var(--text-soft);
}
/* The recommendation is a mark on the track, not the middle one of three
   labels. The track runs 15 to 50 minutes, so 30 is not its midpoint, and a
   label centred by space-between would sit a good 50px clear of the knob it
   is naming. --rec is the knob's own resting percentage, set by
   js/walkthrough-rig.js from the same constants the knob is placed with. */
.wt2-scale span:nth-child(2) {
  position: absolute;
  left: var(--rec, 50%);
  transform: translateX(-50%);
  white-space: nowrap;
}

/* ---- Deadlines ---- */
.wt2-tl { position: relative; padding-left: 1.5em; }
.wt2-tl::before {
  content: "";
  position: absolute;
  left: 0.32em;
  top: 0.55em;
  bottom: 0.55em;
  width: 2px;
  background: var(--wt-ink);
}
.wt2-tl-node { position: relative; }
.wt2-tl-node::before {
  content: "";
  position: absolute;
  left: -1.5em;
  top: 0.4em;
  width: 0.75em;
  height: 0.75em;
  border-radius: 50%;
  background: var(--wt-accent);
}
.wt2-tl-node b { font-size: 0.78em; font-weight: 700; color: var(--wt-ink-strong); }
.wt2-tl-date {
  display: inline-block;
  margin-left: 0.6em;
  border: 1.5px solid var(--wt-ink);
  border-radius: 4px;
  padding: 0.15em 0.5em;
  font-size: 0.72em;
  color: var(--wt-ink-strong);
  background: var(--wt-paper);
}
.page-content .wt2-tl-what {
  font-family: "Roboto Mono", monospace;
  font-size: 0.68em;
  line-height: 1.55;
  color: var(--text-soft);
  margin: 0.3em 0 0;
}
.wt2-tl-gap {
  position: relative;
  margin: 0.9em 0 1em;
  font-size: 0.64em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wt-accent);
}
.wt2-tl-gap::before {
  content: "";
  position: absolute;
  left: -1.18em;
  top: -0.5em;
  bottom: -0.5em;
  border-left: 1.5px dashed var(--wt-accent);
  transform-origin: top;
}
.wt2-frame.is-on .wt2-tl-gap::before {
  animation: wt2-draw 0.5s ease both;
  animation-delay: 0.35s;
}
@keyframes wt2-draw { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/* The date picker, open on the launch field. */
.wt2-cal {
  position: absolute;
  left: 9.5em;
  top: 2.6em;
  z-index: 3;
  width: 15em;
  border: 1.5px solid var(--wt-ink);
  border-radius: 4px;
  background: var(--wt-paper);
  box-shadow: 4px 4px 0 var(--wt-cast);
  padding: 0.6em 0.7em 0.7em;
  transform-origin: top left;
}
.wt2-frame.is-on .wt2-cal { animation: wt2-pop 0.4s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both; }
.wt2-cal-head {
  display: flex;
  justify-content: space-between;
  font-size: 0.66em;
  font-weight: 700;
  color: var(--wt-ink-strong);
  margin-bottom: 0.5em;
}
.wt2-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0.15em;
  font-size: 0.6em;
  text-align: center;
}
.wt2-cal-grid span { padding: 0.25em 0; color: var(--wt-ink); border-radius: 2px; }
.wt2-cal-grid span.dow { color: var(--text-soft); font-weight: 700; }
.wt2-cal-grid span.off { color: var(--wt-faint); }
.wt2-cal-grid span.on {
  background: var(--wt-accent);
  color: #fff;
  font-weight: 700;
}
html.dark-mode .wt2-cal-grid span.on { color: #10101a; }
.wt2-cal-time {
  display: flex;
  justify-content: center;
  gap: 0.5em;
  margin-top: 0.55em;
  padding-top: 0.5em;
  border-top: 1.5px dashed var(--wt-faint);
  font-size: 0.64em;
  color: var(--wt-ink-strong);
}

/* ---- Details panel ---- */
.wt2-titlerow { display: flex; align-items: center; gap: 0.6em; margin-bottom: 1.1em; }
.wt2-titlerow b { font-size: 0.92em; font-weight: 700; color: var(--wt-ink-strong); }
.wt2-pencil {
  border: 1.5px dashed var(--wt-faint);
  border-radius: 3px;
  padding: 0 0.35em;
  font-size: 0.74em;
  color: var(--text-soft);
}
.wt2-cols {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: 1.4em;
}
.page-content .wt2-kv {
  font-family: "Roboto Mono", monospace;
  font-size: 0.7em;
  line-height: 1.6;
  color: var(--wt-ink-strong);
  margin: 0 0 0.8em;
}
.page-content .wt2-kv span { display: block; color: var(--text-soft); }

/* ---- Mode ----
   Two full-width rows, the chosen one filled. Both modes run the same
   matching, so they are the same shape; only the fill distinguishes them. */
.page-content ul.wt2-modes {
  list-style: none;
  margin: 0.4em 0 1.6em;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}
.page-content ul.wt2-modes li.wt2-mode {
  margin: 0;
  padding: 0.72em 0.95em;
  border: 1.5px solid var(--wt-faint);
  border-radius: 4px;
  font-size: 0.78em;
  color: var(--text-soft);
}
.page-content ul.wt2-modes li.wt2-mode.is-on {
  border-color: var(--wt-ink-strong);
  background: var(--wt-accent);
  color: var(--wt-paper);
  font-weight: 700;
}

/* The topics as the details panel lists them: number, the short name, and the
   statement itself underneath — the panel shows both. */
.page-content ol.wt2-topics {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: wt2t;
}
.page-content ol.wt2-topics li {
  counter-increment: wt2t;
  margin: 0 0 1em;
  padding-left: 0.9em;
  border-left: 2px solid var(--wt-accent);
  font-size: 0.72em;
  line-height: 1.55;
}
.page-content ol.wt2-topics li::before {
  content: counter(wt2t);
  display: block;
  font-family: "Roboto Mono", monospace;
  font-size: 0.9em;
  font-weight: 700;
  color: var(--wt-accent);
}
.page-content ol.wt2-topics li b {
  display: block;
  font-family: "Roboto Mono", monospace;
  font-weight: 700;
  color: var(--wt-ink-strong);
}
.page-content ol.wt2-topics li span {
  display: block;
  font-family: "Roboto Mono", monospace;
  color: var(--text-soft);
  margin-top: 0.15em;
}

/* ---- Matches ---- */
.wt2-tablewrap { overflow-x: auto; }
.page-content table.wt2-table {
  width: 100%;
  border-collapse: collapse;
  border: 1.5px solid var(--wt-ink);
  background: var(--wt-paper);
  box-shadow: none;
  border-radius: 0;
  margin: 0;
  font-family: "Roboto Mono", monospace;
}
.page-content table.wt2-table th,
.page-content table.wt2-table td {
  border: 1.5px solid var(--wt-faint);
  padding: 0.55em 0.5em;
  text-align: center;
  font-size: 0.68em;
  line-height: 1.4;
}
.page-content table.wt2-table th {
  background: var(--wt-hatch);
  color: var(--wt-ink-strong);
  font-weight: 700;
}
.page-content table.wt2-table td {
  color: var(--wt-ink-strong);
  font-variant-numeric: tabular-nums;
  font-size: 0.85em;
  font-weight: 700;
}

/* ============================================================
   Narrow screens
   ============================================================ */
@media (max-width: 900px) {
  .wt2-stage { padding: 0.8rem 1rem; align-items: flex-start; }
  .wt2-canvas { height: min(100%, 62vh); }
  .wt2-step { padding: 0 1rem; min-height: 100vh; }
  /* The card is the full width of the page down here — it sits under the
     wireframe rather than over it — so it is not lined up with anything. */
  .wt2-cardhold { align-items: flex-end; height: calc(100vh - var(--banner-h) - 2.5vh); padding-bottom: 0; width: 100%; }
  .wt2-card { width: 100%; padding: 1.1rem 1.2rem 1.2rem; }
  .page-content ol.wt2-dots { display: none; }
  .wt2-rail { flex: 0 0 9em; }
}
@media (max-width: 620px) {
  .wt2-rail { display: none; }
  .wt2-frame { padding: 1.1em 1.1em 1.2em; }
  .wt2-cal { left: auto; right: 0.5em; }
}

/* ============================================================
   Reduced motion: keep the layout, drop the movement
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .wt2-card { transition: opacity 0.2s ease, visibility 0.2s; transform: none; }
  .wt2-step.is-active .wt2-card { transform: none; }
  .wt2-frame.is-on > * { animation: none; }
  .wt2-frame.is-on .wt2-tl-gap::before,
  .wt2-frame.is-on .wt2-cal,
  .wt2-window[data-rail="scheduled"] .wt2-railcard { animation: none; }
  .wt2-cursor { transition: opacity 0.2s ease; }
  .wt2-cursor.is-clicking svg { transform: none; }
  .wt2-window { transform: none; }
  .wt2-cursor::after { animation: none; opacity: 0.5; }
  .wt2-caret { animation: none; }
  .wt2-knob { transition: none; }
}

/* ============================================================
   No JS: unstack into a plain read
   ============================================================ */
html:not(.wt2-js) .wt2 { display: block; }
html:not(.wt2-js) .wt2-stage { position: static; height: auto; padding: 0; }
html:not(.wt2-js) .wt2-canvas, html:not(.wt2-js) .wt2-window { height: auto; }
/* Both of these follow from the height above. A size container is not sized by
   what is in it, so a height:auto one is no height at all; and with no
   container to resolve against, the cqi/cqb the type is sized in would fall
   back to the viewport and give a wireframe set at 22px. Neither the box nor
   the type is fluid here — every screen is on show at once, as ordinary page. */
html:not(.wt2-js) .wt2-canvas { container-type: normal; }
html:not(.wt2-js) .wt2-window { font-size: clamp(12px, 8px + 0.62vw, 17px); }
html:not(.wt2-js) .wt2-cardhold { width: 100%; }
html:not(.wt2-js) .wt2-frames { display: block; }
html:not(.wt2-js) .wt2-frame {
  opacity: 1;
  visibility: visible;
  /* Every screen is on show at once here, so every screen is reachable —
     the Matches columns explain themselves on hover and focus. */
  pointer-events: auto;
  border-top: 1.5px dashed var(--wt-faint);
}
html:not(.wt2-js) .wt2-frames > .wt2-frame:first-child { border-top: 0; }
html:not(.wt2-js) .wt2-step { min-height: 0; padding: 1.5rem 0 0; display: block; }
html:not(.wt2-js) .wt2-card {
  position: static;
  opacity: 1;
  visibility: visible;
  transform: none;
  width: 100%;
  max-width: 34rem;
  backdrop-filter: none;
}
html:not(.wt2-js) .wt2-cursor,
html:not(.wt2-js) .page-content ol.wt2-dots { display: none; }
html:not(.wt2-js) .wt2-railcard { display: block; }
html:not(.wt2-js) .wt2-options,
html:not(.wt2-js) .wt2-cal { position: static; box-shadow: none; width: auto; margin-top: 0.4em; }


/* ============================================================
   The drawn line — generated strokes, laid down on arrival

   A CSS border is a perfect rectangle, and a perfect rectangle
   never looks drawn. Every line in this wireframe is instead
   generated geometry from js/hand-ink.js: each edge overshoots the
   corner so the strokes cross, lands a little off the mark, bows
   in the middle, and is gone over twice. The imperfection is in
   the shape of the line, not in where the line sits — which is
   what the boil that used to be here got wrong, and why it read as
   a rendering fault rather than as drawing.

   On top of that, the one piece of motion: when a screen is shown
   for the first time its lines draw themselves, structure first
   and then the boxes inside it, edge by edge. It happens once per
   screen, at the moment that screen has the reader's attention,
   and then it is still. Scrolling back to a screen already drawn
   does not draw it again.

   The trigger is .hi-on, set by the script on each inked element
   rather than on a container: the window is an ancestor of every
   frame, so a class there would start all nine screens at once.

   Without JavaScript none of this exists, and the ordinary CSS
   borders below are what the reader sees.
   ============================================================ */

/* Once the strokes are painted, the borders they replace come off. .hi-box is
   set by the script on exactly the elements it inked, so this cannot get out
   of step with what was actually drawn. Squared, too: the generated outline
   has square corners, and a rounded fill under it shows through where the
   strokes cross. */
html.wt2-ink .hi-box {
  position: relative;
  border-color: transparent;
  border-radius: 0;
}
/* Two selectors elsewhere in this file are more specific than the rule above,
   so without these they keep their CSS border and the drawn one sits on top
   of it. */
html.wt2-ink .page-content table.wt2-table th.hi-box,
html.wt2-ink .page-content table.wt2-table td.hi-box,
html.wt2-ink .page-content ul.wt2-modes li.wt2-mode.hi-box { border-color: transparent; }

.hi-ink {
  position: absolute;
  left: -7px;
  top: -7px;
  overflow: visible;
  pointer-events: none;
  fill: none;
  z-index: 3;
}
.hi-ink.hi-inset { left: 0; top: 0; }
.hi-ink path {
  stroke: var(--wt-ink);
  stroke-width: 1.15;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
/* The second pass over an edge is lighter, as a real second pass is. Dimmed
   with stroke-opacity rather than opacity, which the reveal below needs. */
.hi-ink path.hi-p2 { stroke-width: 0.85; stroke-opacity: 0.5; }
.hi-faint path { stroke: var(--wt-faint); stroke-width: 1; }
.hi-heavy path { stroke: var(--wt-ink-strong); stroke-width: 1.4; }
.hi-accent path { stroke: var(--wt-accent); stroke-width: 1.2; }
.hi-dash path { stroke-dasharray: 4 3.5; }

/* ---- the reveal ----
   pathLength="1" normalises every stroke, so one dash length covers any edge
   and a long line takes no longer to draw than a short one. */
@keyframes wt2-draw { to { stroke-dashoffset: 0; } }
@keyframes wt2-inkin { to { opacity: 1; } }

html.wt2-ink .hi-ink:not(.hi-dash) path { stroke-dasharray: 1; stroke-dashoffset: 1; }
html.wt2-ink .hi-on > .hi-ink:not(.hi-dash) path {
  animation: wt2-draw 0.32s cubic-bezier(0.4, 0.05, 0.25, 1) forwards;
  /* The box's place in its screen, then the edge's place in the box. */
  animation-delay: calc(var(--b, 0) * 0.055s + var(--i, 0) * 0.035s);
}
/* A dashed line cannot be revealed by its dash pattern — that pattern is what
   the dashes are made of — so it comes in edge by edge on the same beat. */
html.wt2-ink .hi-dash path { opacity: 0; }
html.wt2-ink .hi-on > .hi-dash path {
  animation: wt2-inkin 0.26s ease forwards;
  animation-delay: calc(var(--b, 0) * 0.055s + var(--i, 0) * 0.035s);
}

@media (prefers-reduced-motion: reduce) {
  html.wt2-ink .hi-ink:not(.hi-dash) path { stroke-dashoffset: 0; animation: none; }
  html.wt2-ink .hi-dash path { opacity: 1; animation: none; }
}

/* ============================================================
   Per-screen card widths and the ASCII empty state
   ============================================================ */

@media (max-width: 900px) {
  .wt2-step[data-w] .wt2-card { width: 100%; }
}

/* The picker sits just under the last of the three rows, with a short leader
   up to it: a popover drawn over the rows would hide them, and the rows are
   the lesson. */
.wt2-cal-aside { left: 0; top: 1.6em; }
.wt2-cal-aside::after {
  content: "";
  position: absolute;
  left: 4em;
  top: -1.4em;
  height: 1.4em;
  border-left: 1.5px dashed var(--wt-faint);
}

.page-content .wt2-flabel.wt2-gap { margin-top: 1.3em; }

/* ---- Empty state ----
   Real applications put an illustration in an empty state. Ours is the Sway
   mark transcribed to a character grid (src/_data/ascii.js), sitting where the
   assignment list would be — faint enough to read as watermark rather than as
   content, and hidden from assistive technology since it says nothing. */
.wt2-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* The mark is the texture here; the hatching would fight it. */
  background: none;
}
.page-content pre.wt2-ascii {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: "Roboto Mono", ui-monospace, Menlo, monospace;
  /* The grid is drawn for a cell twice as tall as it is wide. Roboto Mono
     advances 0.6em, so that cell is a line height of 1.2. */
  /* 55 columns at this size is the same 235px-wide mark the 35-column grid
     drew, with enough cells left for the cap to read as a cap. */
  font-size: 0.42em;
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--wt-ink);
  opacity: 0.2;
  white-space: pre;
  user-select: none;
  transition: opacity 0.6s ease;
}
html.dark-mode .page-content pre.wt2-ascii { opacity: 0.26; }
/* It brightens a little as the screen it belongs to arrives. */
.wt2-frame.is-on pre.wt2-ascii { opacity: 0.34; }
html.dark-mode .wt2-frame.is-on pre.wt2-ascii { opacity: 0.4; }
@media (max-width: 620px) {
  .page-content pre.wt2-ascii { font-size: 0.32em; }
}

/* ---- The opening how-to step ----
   Tells the reader they can press space, which is a desktop keyboard they may
   not have; on a phone the step is not there at all. Hidden rather than
   reworded because scrolling is the only way through on a touch screen, and
   the rig's own activeIndex() skips steps that are not displayed. */
@media (max-width: 900px) {
  .wt2-step.wt2-intro { display: none; }
}

/* ============================================================
   Columns that explain themselves
   ============================================================ */
/* The card used to define all seven Matches columns in a paragraph, which is
   a lot of reading for something the screen can answer in place. The reader
   is pointed at the columns instead, so each has to carry its own
   explanation — the wording is the product's own. Anchored to the table
   rather than to the cell, so every explanation appears in the same place
   under the table and the ones at the right-hand end cannot overhang the
   window. */
.page-content table.wt2-table:has(.wt2-coltip) { position: relative; overflow: visible; }
.wt2-tablewrap:has(.wt2-coltip) { overflow: visible; }
/* That wrapper cannot scroll — the column tooltips have to be able to escape
   it — so on a narrower window the table has to fit instead. Below about
   1400px the seven columns were 38px wider than the space left beside the
   card, and the last of them sat under it. Pre-existing; found while checking
   every screen of every walkthrough for the same fault. */
@media (max-width: 1400px) {
  .wt2-frame[data-frame="matches"] table.wt2-table th,
  .wt2-frame[data-frame="matches"] table.wt2-table td {
    padding-left: 0.2em;
    padding-right: 0.2em;
  }
}
.wt2-col { cursor: help; }
.wt2-coltip {
  display: none;
  position: absolute;
  /* Anchored to the cell, not the table: every inked box is position:relative
     so that its strokes sit over it, which makes the cell the containing
     block whatever the table does. The last few columns hang their
     explanation to the left instead, so it cannot run off the window. */
  left: 0;
  right: auto;
  top: calc(100% + 0.55em);
  width: 34em;
  max-width: none;
  z-index: 5;
  border: 1.5px solid var(--wt-ink);
  border-radius: 4px;
  background: var(--wt-paper);
  box-shadow: 3px 3px 0 var(--wt-cast);
  padding: 0.45em 0.6em;
  font-family: "Roboto Mono", monospace;
  font-size: 0.62em;
  font-weight: 400;
  line-height: 1.5;
  text-align: left;
  color: var(--wt-ink);
  white-space: normal;
}
.wt2-col:nth-child(n+3) .wt2-coltip { left: auto; right: 0; }
.wt2-col:hover .wt2-coltip,
.wt2-col:focus .wt2-coltip,
.wt2-col:focus-visible .wt2-coltip { display: block; }
.wt2-coltip b { color: var(--wt-ink-strong); }

/* Which half of that sentence applies depends on where the card is: beside
   the wireframe on a desktop, below it on a phone. */
.wt2-mob { display: none; }
@media (max-width: 900px) {
  .wt2-desk { display: none; }
  .wt2-mob { display: inline; }
}

/* ---- Two things that were too easy to miss ----
   The course selector is the first thing the opening card talks about, and
   the Scheduled badge is the whole point of the screen it sits on. Both were
   drawn as quiet outlines. */
.wt2-railsel {
  border-color: var(--wt-accent);
  border-width: 2px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--wt-ink-strong);
}
.wt2-railcard span.wt2-badge {
  background: var(--wt-accent);
  border-color: var(--wt-accent);
  color: var(--wt-paper);
  font-weight: 700;
  letter-spacing: 0.08em;
}

/* ---- The two topic-entry screens ----
   These are the only screens whose content is genuinely wide — a full
   sentence in a text field — and the card floats over the right of the
   window on top of them. Without a reserve the field ran under the card at
   ordinary laptop widths, so the end of the statement being typed was
   behind it. The card is min(24rem, 42%); this keeps clear of the whole of
   it and leaves the Next button somewhere the reader can still see. */
@media (min-width: 901px) {
  .wt2-frame[data-frame="fields"],
  .wt2-frame[data-frame="fields2"] { padding-right: calc(24rem + 4.5rem); }
}
