/* ═══════════════════════════════════════════════════════════════════════════
 *  app.css — Scriptwrite's own styles. ONLY WHAT IS PECULIAR TO THIS TOOL.
 *
 *  AI NOTES
 *  ────────
 *  LOADED THIRD, after chrome.css and base.css, so everything here wins. That
 *  is why there is so little of it: a button, a field, a note, a pill and a
 *  table are all in base.css and are the same on every tool of this site. If
 *  you find yourself restyling one of those here, it belongs there instead.
 *
 *  EVERY COLOUR IS A CUSTOM PROPERTY FROM base.css. Not one hex value is typed
 *  in this file. The site's palette is edited in one place and this tool
 *  follows it; a literal colour here is a tool that stops matching the day
 *  somebody changes the theme.
 *
 *  WHAT IS GENUINELY THIS TOOL'S OWN, AND WHY EACH ONE EARNS ITS PLACE:
 *    the three numbered steps      a sequence nothing else on this site has
 *    the four pass rows            progress through named work
 *    the markers in the rewrite    [GAP:], [UNCLEAR:], [CONFLICT:]
 *    the moment cards              a quote, a strength and what to do
 *    the Forge light               the one thing that must be true to run
 * ═══════════════════════════════════════════════════════════════════════════ */


/* ═════════════════════════════════════════ THE LIGHT IN THE HEADER
 *
 * IT RIDES IN THE MENU CAPSULE, which chrome.php offers as 'menu_extra' and
 * puts after the links behind its own divider. See the note there: a status
 * light among menu items reads as a menu item that will not click, so the
 * divider is not decoration.
 *
 * THE WORD IS "Forge" IN BOTH STATES AND THE DOT CARRIES THE NEWS. A header
 * that changes its own text to an error message is where somebody learns to
 * stop reading the header. */
.sw-light { display: inline-flex; align-items: center; gap: .4rem; font-size: .72rem; }
.sw-light i {
  width: .45rem; height: .45rem; border-radius: 50%;
  background: var(--ink-faint); flex: 0 0 auto;
}
.sw-light.is-up   { color: var(--go-ink); }
.sw-light.is-up i { background: var(--go); box-shadow: 0 0 6px var(--go-line); }
.sw-light.is-down   { color: var(--bad); }
.sw-light.is-down i { background: var(--bad); box-shadow: 0 0 6px var(--bad-line); }


/* ═════════════════════════════════════════════ THE THREE STEPS
 *
 * A STEP THAT IS NOT REACHED YET IS DIMMED AND STILL READABLE, not hidden.
 *
 * Hiding step 2 until step 1 is done makes the page appear to grow, and
 * somebody cannot see what is coming — which on a tool whose first action is
 * uploading a personal account is exactly the wrong feeling. Dimmed, the shape
 * of the whole task is visible from the first second.
 *
 * AND IT IS NOT DISABLED EITHER. The controls inside work; pressing Run with
 * nothing read simply does nothing, because the script has no run to start.
 * Disabling a field somebody can see is a control that has to explain itself. */
.sw-step { margin: 0 0 2.2rem; }
.sw-step.is-off { opacity: .45; }
.sw-step.is-off:focus-within { opacity: 1; }

/* THE NUMBER IS PART OF THE HEADING AND NOT A LIST MARKER, because the three
   sections are not a list — they are a sequence with results hanging off the
   last one. */
.sw-num {
  display: inline-grid; place-items: center;
  width: 1.4rem; height: 1.4rem; margin-right: .5rem;
  border-radius: 50%; font-size: .72rem; font-weight: 600;
  border: 1px solid var(--go-line); background: var(--go-soft); color: var(--go-ink);
}

/* The explanatory paragraph under a heading. base.css styles .field .why, and
   this is the same voice outside a field. */
.sw-why { max-width: 46rem; font-size: .78rem; color: var(--ink-faint); margin: -.2rem 0 1rem; }

.sw-hint { font-size: .76rem; color: var(--ink-dim); }

/* The theme suggestions. .pill is from base.css; only the layout is here. */
.sw-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.sw-chip  { cursor: pointer; font: inherit; font-size: .66rem; }


/* ══════════════════════════════════════ WHAT THE TOOL READ
 *
 * A pre-formatted sample of the account, so a conversion that half worked is
 * visible. MONOSPACED ON PURPOSE: this is the text as bytes, not as prose, and
 * a proportional font hides a run of spaces and a stray carriage return. */
.sw-got { margin-top: 1rem; }
.sw-got details { margin-top: .4rem; }
.sw-got summary { cursor: pointer; font-size: .78rem; color: var(--ink-dim); }
.sw-got summary:hover { color: var(--ink); }

.sw-pre {
  margin: .6rem 0 0; padding: .8rem;
  max-height: 22rem; overflow: auto;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--bg-sunk);
  font-family: var(--mono); font-size: .74rem; line-height: 1.6; color: var(--ink-dim);
  /* pre-wrap AND NOT pre: a transcript line can be four hundred characters,
     and a horizontal scrollbar on the one pane somebody is checking for
     correctness is a pane they will not read to the end of. */
  white-space: pre-wrap; word-break: break-word;
}


/* ═════════════════════════════════════════════ THE FOUR PASSES
 *
 * ALL FOUR ARE DRAWN BEFORE ANY OF THEM RUNS. A list that grows as work
 * finishes hides how much is left. See the note in index.php.
 *
 * THE STATE IS AN ATTRIBUTE AND NOT A CLASS PER STATE. One attribute can only
 * hold one value, so a row cannot be both "going" and "done" — which is
 * exactly the bug a set of classes produces when a poll adds one and forgets
 * to remove another. */
.sw-passes { list-style: none; margin: 0 0 1rem; padding: 0; }

.sw-pass {
  display: grid; grid-template-columns: 1fr auto; gap: .2rem 1rem;
  align-items: baseline;
  padding: .7rem .9rem; margin-bottom: .4rem;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--bg-raise);
  border-left: 3px solid var(--line-firm);
}
.sw-pass b    { font-size: .86rem; font-weight: 600; color: var(--ink); }
.sw-pass span { grid-column: 1; font-size: .74rem; color: var(--ink-faint); }
.sw-pass i    { grid-row: 1; grid-column: 2; font-style: normal;
                font-size: .72rem; color: var(--ink-dim); text-align: right; }

.sw-pass[data-state=waiting] { opacity: .5; }
.sw-pass[data-state=going]   { border-left-color: var(--warn); background: var(--warn-soft); }
.sw-pass[data-state=going] b { color: var(--ink-bright); }
.sw-pass[data-state=done]    { border-left-color: var(--go); }
.sw-pass[data-state=done] b  { color: var(--go-ink); }
.sw-pass[data-state=failed]  { border-left-color: var(--bad); background: var(--bad-soft); }
.sw-pass[data-state=failed] b, .sw-pass[data-state=failed] i { color: var(--bad); }

/* THE PASS IN PROGRESS BREATHES, AND IT IS THE ONLY ANIMATION IN THIS FILE.
   A model call returns nothing until it is finished, so there is no real
   progress to draw inside a pass — and a bar that fills on a timer is a lie
   about work nobody can measure. A slow pulse says "still here" and claims
   nothing. */
.sw-pass[data-state=going] i { animation: sw-breathe 2.4s ease-in-out infinite; }
@keyframes sw-breathe { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }

/* SOMEBODY WHO ASKED FOR LESS MOTION GETS NONE. A pulse is decoration; the
   word beside it carries the same information. */
@media (prefers-reduced-motion: reduce) {
  .sw-pass[data-state=going] i { animation: none; opacity: 1; }
}


/* ═════════════════════════════════════════════════ THE RESULTS
 *
 * FIVE PANES BEHIND A ROW OF BUTTONS. The rewrite alone is several pages, and
 * somebody looking for the questions should not scroll past it. */
.sw-out { margin-top: 1.6rem; }
.sw-tabs { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: 1rem; }
.sw-tabs .pill { cursor: pointer; font: inherit; font-size: .7rem; padding: .35rem .75rem; }
.sw-tabs .pill.on {
  border-color: var(--go-line); background: var(--go-soft); color: var(--go-ink);
}

.sw-pane { min-height: 4rem; }
.sw-pane h3 {
  font-size: .82rem; font-weight: 600; color: var(--ink-bright);
  margin: 1.6rem 0 .6rem; letter-spacing: .02em;
}
.sw-pane h3:first-child { margin-top: 0; }
.sw-empty { color: var(--ink-faint); font-size: .84rem; }

/* ─────────────────────────────────────────────── the rewrite
 *
 * WIDER LINE HEIGHT AND A MEASURE, because this is the one thing on the page
 * somebody reads from beginning to end. Everything else here is scanned. */
.sw-doc { max-width: 42rem; }
.sw-doc p { margin: 0 0 1rem; font-size: .9rem; line-height: 1.75; color: var(--ink); }

/* The note the rewrite pass puts at the top of a sectioned account. */
.sw-aside {
  padding: .7rem .9rem; border-left: 2px solid var(--line-firm);
  background: var(--bg-sunk); font-size: .8rem !important; color: var(--ink-dim) !important;
  line-height: 1.6 !important;
}

/* ═══ THE MARKERS, AND THEY ARE THE MOST IMPORTANT THING ON THIS PAGE ═══
 *
 * The rewrite pass is told to leave [GAP: …], [UNCLEAR: …] and [CONFLICT: …]
 * wherever it would otherwise have had to guess. Those are the list of what to
 * ask about.
 *
 * DRAWN AS PLAIN TEXT THEY READ AS UNTIDINESS SOMEBODY SHOULD CLEAN UP before
 * printing. That is the failure this styling exists to prevent: a reader who
 * tidies them away has deleted the findings and kept the prose.
 *
 * THREE COLOURS, AND THEY ARE NOT DECORATIVE. A gap is something to ask
 * (green, the site's "go"). An unclear phrase is a caution (amber). A conflict
 * is the one a reader must not resolve on their own (red). */
.sw-mark {
  display: inline; padding: .08rem .3rem; border-radius: .25rem;
  font-size: .78em; font-weight: 500; letter-spacing: .01em;
  background: none; border: 1px solid var(--line-firm); color: var(--ink-dim);
}
.sw-mark.is-gap      { border-color: var(--go-line);   background: var(--go-soft);   color: var(--go-ink); }
.sw-mark.is-unclear  { border-color: var(--warn-line); background: var(--warn-soft); color: var(--warn); }
.sw-mark.is-conflict { border-color: var(--bad-line);  background: var(--bad-soft);  color: var(--bad); }

/* ─────────────────────────────────────────── the questions */
.sw-qs { list-style: none; margin: 0; padding: 0; }
.sw-qs li {
  padding: .7rem .9rem; margin-bottom: .4rem;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--bg-raise);
}
/* A QUESTION THAT COULD UPSET SOMEBODY IS MARKED ON THE EDGE AS WELL AS WITH
   ITS PILL. Whoever reads this list is deciding what to ask a real person, and
   a hard question that looks like the others is the thing that goes wrong in an
   interview room. Two signals, because one can be missed while scanning. */
.sw-qs li.is-care { border-left: 3px solid var(--bad); }

.sw-q { display: flex; flex-wrap: wrap; align-items: baseline; gap: .45rem; }
.sw-q b { font-size: .88rem; font-weight: 500; color: var(--ink-bright); line-height: 1.55; }
.sw-because { margin: .35rem 0 0; font-size: .76rem; color: var(--ink-faint); font-style: italic; }

/* ─────────────────────────────────── the emotional moments */
.sw-moment {
  padding: .9rem 1rem; margin-bottom: .6rem;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--bg-raise);
}
/* A MOMENT WHOSE QUOTE IS NOT IN THE ACCOUNT IS SHOWN AND MARKED, NEVER
   HIDDEN. Deleting it would make the tool look more reliable than it is, and
   putting a sentence in somebody's mouth is the one failure this tool cannot
   have. See sw_check_quotes(). */
.sw-moment.is-unfound { border-color: var(--bad-line); background: var(--bad-soft); }

.sw-moment-top { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: .5rem; }
.sw-moment-top b { font-size: .84rem; color: var(--ink-bright); }

.sw-moment blockquote {
  margin: 0 0 .6rem; padding: .5rem 0 .5rem .9rem;
  border-left: 2px solid var(--go-line);
  font-size: .88rem; line-height: 1.7; color: var(--ink);
}
.sw-moment p { margin: 0 0 .5rem; font-size: .82rem; line-height: 1.65; color: var(--ink-dim); }
.sw-moment p:last-child { margin-bottom: 0; }
.sw-handle b, .sw-theme-link b { color: var(--go-ink); font-weight: 600; }

/* FIVE MARKS AND NOT A NUMBER. "4" needs a legend; four filled of five does
   not. */
.sw-int { display: inline-flex; gap: .15rem; flex: 0 0 auto; }
.sw-int i {
  width: .3rem; height: .8rem; border-radius: .1rem;
  background: var(--line-firm);
}
.sw-int i.on { background: var(--warn); }

/* ─────────────────────────────────────── what it found */
.sw-subject { margin: 0 0 .8rem; font-size: .88rem; color: var(--ink); }
.sw-subject b { color: var(--ink-bright); }

.sw-timeline { margin: 0; padding-left: 1.4rem; }
.sw-timeline li { margin-bottom: .45rem; font-size: .84rem; line-height: 1.6; color: var(--ink); }
.sw-when { font-family: var(--mono); font-size: .76rem; color: var(--go-ink); }
/* A DATE THE ACCOUNT HEDGED IS DRAWN AS HEDGED. The map pass sets `certain`
   false on "I think it was 1994", and a timeline that prints 1994 flat has
   thrown away the one thing an interviewer needed to know about it. */
.sw-when.is-vague { color: var(--warn); border-bottom: 1px dotted var(--warn-line); }

.sw-pairs { list-style: none; margin: 0 0 1rem; padding: 0; }
.sw-pairs li {
  padding: .5rem .8rem; margin-bottom: .3rem;
  border-left: 2px solid var(--line-firm); background: var(--bg-sunk);
  font-size: .82rem; line-height: 1.6; color: var(--ink-dim);
}
.sw-pairs li b { color: var(--ink); font-weight: 500; }

.sw-inline { display: flex; flex-wrap: wrap; gap: .3rem; margin: 0 0 1rem; }


/* ═════════════════════════════════════════════════ THE RUNS PAGE */
.sw-run {
  display: flex; flex-wrap: wrap; gap: .8rem; align-items: flex-start;
  justify-content: space-between;
  padding: .85rem 1rem; margin-bottom: .5rem;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--bg-raise);
}
.sw-run-main { flex: 1 1 18rem; min-width: 0; }
.sw-run-name { font-size: .9rem; font-weight: 500; color: var(--ink-bright); }
.sw-run-sub {
  display: flex; flex-wrap: wrap; align-items: center; gap: .45rem;
  margin-top: .35rem; font-size: .74rem;
}
.sw-run-theme {
  margin: .4rem 0 0; font-size: .78rem; color: var(--ink-dim); font-style: italic;
  /* ONE LINE AND THEN AN ELLIPSIS. A theme may be six hundred characters, and
     a row that grows to hold one makes the list unscannable. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sw-run-why { margin: .4rem 0 0; font-size: .76rem; color: var(--bad); }
.sw-run-acts { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; flex: 0 0 auto; }


/* ═════════════════════════════════════════════════════ NARROW SCREENS
 *
 * THE PASS ROWS KEEP THEIR SHAPE AND THE TIMING MOVES UNDER THE NAME. On a
 * phone a two-column row with a right-aligned time either wraps badly or
 * squeezes the name to two words per line. */
@media (max-width: 34rem) {
  .sw-pass { grid-template-columns: 1fr; }
  .sw-pass i { grid-row: auto; grid-column: 1; text-align: left; }
  .sw-run { flex-direction: column; }
  .sw-run-acts { width: 100%; }
}


/* ═══════════════════════════════════════════════════════════════════════════
 *  THE SIDE-BY-SIDE READER
 * ═══════════════════════════════════════════════════════════════════════════
 *  Two documents read against each other, with a line from each paragraph of
 *  the rewrite to the passage of the account it was written from. The lines
 *  come from app/scriptwrite/align.php; the drawing is in app.js.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* ═══ LEAVING THE COLUMN, AND THE ARITHMETIC BEHIND IT ═══
 *
 * main.wrap is 62rem wide and centred, because that is a comfortable measure
 * for reading one thing. Two documents read against each other is a different
 * job: the lines between them have to cross real distance to be followed, and
 * squeezed into 31rem each the columns become unreadable and the middle
 * becomes a tangle.
 *
 * HOW IT ESCAPES: a centred block's left edge sits at 50% of its parent, so
 * pulling it back by half the VIEWPORT width puts it at the window's left
 * edge. 100vw is then the whole window.
 *
 * 100vw INCLUDES THE SCROLLBAR where a browser still reserves room for one.
 * Used raw, the pane is then wider than the page by exactly the scrollbar's
 * width and the window gains a horizontal one — the standard bug in every
 * full-bleed technique on the web.
 *
 * THE POPULAR CSS-ONLY FIX IS WRONG AND WAS TRIED HERE FIRST.
 * calc(100vw - (100vw - 100%)) looks like it subtracts the scrollbar, and it
 * does when the parent is the full page. This parent is main.wrap, which is
 * 62rem — so the expression reduces to 100% and the pane never left the
 * column at all. It looked plausible and did nothing.
 *
 * SO THE REAL WIDTH IS MEASURED ONCE IN SCRIPT and handed to the stylesheet as
 * --sw-vw. See setViewportWidth() in app.js. macOS overlay scrollbars make it
 * exactly 100vw; a browser with classic scrollbars gets the honest number. The
 * fallback is plain 100vw, so a page whose script never ran is full width with
 * a scrollbar rather than squeezed back into the column.
 */
.sw-pane.is-wide {
  width: var(--sw-vw, 100vw);
  max-width: var(--sw-vw, 100vw);
  margin-left: calc(50% - var(--sw-vw, 100vw) / 2);
  box-sizing: border-box;
}

.sw-side { display: flex; flex-direction: column; }

/* ─────────────────────────────────────────────────────── the two headers
 *
 * THE SAME THREE COLUMNS AS THE BODY BELOW, so each heading sits over the
 * column it names and the key sits over the lines it describes. */
.sw-side-head {
  display: grid;
  grid-template-columns: 1fr 9rem 1fr;
  gap: 0 1rem;
  align-items: end;
  padding: 0 1.1rem .7rem;
}
.sw-col-head { min-width: 0; }
.sw-col-head b {
  display: block; font-size: .82rem; font-weight: 600; color: var(--ink-bright);
}
.sw-col-sub {
  display: block; margin-top: .15rem;
  font-size: .72rem; color: var(--ink-faint);
  /* ONE LINE. A filename and a theme can both be long, and a header that grows
     to three lines pushes the columns down on every window. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The key, centred over the gutter the lines run through. */
.sw-key {
  display: flex; align-items: center; justify-content: center; gap: .3rem;
  font-size: .64rem; color: var(--ink-faint); padding-bottom: .15rem;
}
.sw-key i { width: .9rem; height: 0; border-top: 2px solid var(--go-line); }
.sw-key i.is-weak { border-top-style: dashed; border-top-color: var(--ink-faint); }
.sw-key span { margin-right: .35rem; }

/* ───────────────────────────────────────────────────────── the two columns
 *
 * ═══ EACH SCROLLS ON ITS OWN, AND THE PAGE DOES NOT ═══
 *
 * The obvious arrangement — two tall columns and one page scrollbar — cannot
 * work here. The documents are different lengths, so one column runs out while
 * the other has pages left, and there is no way to bring a pair level with
 * each other. Two scrollable boxes of equal height is what makes "scroll one
 * and the other follows" a thing that can exist at all.
 *
 * THE HEIGHT IS THE WINDOW LESS WHAT IS ABOVE. 19rem covers the site header,
 * the run's progress list, the tab row and the two column headings. It is a
 * fixed number because measuring it would mean measuring on every resize, and
 * being a few pixels out costs nothing — the columns scroll.
 */
.sw-side-body {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 9rem 1fr;
  gap: 0 1rem;
  height: calc(100vh - 19rem);
  min-height: 26rem;
  padding: 0 1.1rem;
}

.sw-col {
  overflow-y: auto; overflow-x: hidden;
  min-width: 0;
  padding: .9rem 1rem;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--bg-sunk);
  /* THE SCROLL IS DRIVEN BY SCRIPT AS WELL AS BY HAND. Smooth behaviour here
     would fight the synchroniser: it sets scrollTop on every frame of the
     other column's movement, and an animation on top of that lags behind and
     never settles. */
  scroll-behavior: auto;
}

/* ─────────────────────────────────────────────────────────────── the blocks */
.sw-sb, .sw-rb {
  margin: 0 0 .55rem; padding: .5rem .65rem;
  border-radius: var(--r);
  border: 1px solid transparent;
  border-left: 2px solid transparent;
  font-size: .82rem; line-height: 1.65; color: var(--ink-dim);
  cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}

/* THE ACCOUNT KEEPS ITS LINE BREAKS. A transcript is one line per speaker
   turn, and running those together loses who is talking. */
.sw-sb { white-space: pre-wrap; }

.sw-rb { color: var(--ink); }
.sw-rb.is-heading {
  font-size: .78rem; font-weight: 600; color: var(--ink-bright);
  letter-spacing: .02em; margin-top: 1rem; cursor: default;
  padding-bottom: .15rem;
}
.sw-rb.is-heading:first-child { margin-top: 0; }
.sw-rb.is-note { font-size: .74rem; color: var(--ink-faint); font-style: italic; cursor: default; }
.sw-rb.is-marker { padding-top: .25rem; padding-bottom: .25rem; cursor: default; }

/* ═══ HOVER AND PIN ═══
 *
 * THE SAME TREATMENT ON BOTH SIDES, because the pair is one thing seen twice
 * and a reader needs to recognise it instantly in the other column. */
.sw-sb:hover, .sw-rb:hover { border-color: var(--line-firm); }
.sw-sb.is-hot, .sw-rb.is-hot {
  background: var(--go-soft);
  border-color: var(--go-line);
  border-left-color: var(--go);
  color: var(--ink-bright);
}

/* ═══ A PARAGRAPH NOTHING IN THE ACCOUNT EXPLAINS ═══
 *
 * This is the finding the whole view exists to surface, so it is marked on the
 * paragraph itself and not only in the count underneath. The rewrite pass is
 * forbidden to add anything that is not in the account; a paragraph that
 * cannot be traced is either a summary drawn from several places or something
 * the model made up, and only a person can tell which.
 *
 * AMBER AND NOT RED. It is a thing to check, not a thing that is known to be
 * wrong, and crying wolf on the summaries would teach somebody to ignore it. */
.sw-rb.is-untraced {
  border-color: var(--warn-line);
  border-left-color: var(--warn);
  background: var(--warn-soft);
}
.sw-untraced {
  display: block; margin-top: .3rem;
  font-size: .64rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--warn);
}

/* ───────────────────────────────────────────────────────────────── the wires
 *
 * THE OVERLAY IS THE MIDDLE GRID CELL and nothing else: the lines start at its
 * left edge and end at its right, which are exactly the inner edges of the two
 * columns. Stretching it over the whole body and drawing inset would mean two
 * numbers to keep in step with the grid.
 *
 * pointer-events IS OFF so the lines never swallow a click meant for a
 * paragraph underneath. An SVG that eats clicks in the gutter is a column that
 * mysteriously stops responding near its edge. */
.sw-wires {
  position: relative; width: 100%; height: 100%;
  overflow: visible; pointer-events: none;
}
.sw-wire {
  fill: none;
  stroke: var(--go-line);
  stroke-width: 1.25;
  opacity: .5;
  transition: opacity .12s ease, stroke .12s ease, stroke-width .12s ease;
}
/* A PARTLY-TRACED LINK IS BROKEN, NOT FAINTER. Opacity alone reads as
   "further away"; a dashed line reads as "less certain", which is what it
   means. Usually it is a paragraph built from two or three passages at once. */
.sw-wire.is-weak { stroke: var(--ink-faint); stroke-dasharray: 3 4; opacity: .55; }
.sw-wire.is-on {
  stroke: var(--go); stroke-width: 2; opacity: 1;
  /* THE HIGHLIGHTED LINE IS SOLID WHATEVER ITS TIER. Once somebody has asked
     for this one pair, the certainty is told by the label and the dash is just
     noise on the line they are trying to follow. */
  stroke-dasharray: none;
}

.sw-side-foot {
  margin: .7rem 0 0; padding: 0 1.1rem;
  font-size: .72rem; color: var(--ink-faint);
}

/* ═══════════════════════════════════════════════════ A WINDOW TOO NARROW
 *
 * BELOW ABOUT 62rem THE TWO COLUMNS STOP BEING READABLE — a column of about
 * 30 characters is not prose, it is a ticker. So they stack, the gutter closes
 * and the lines are not drawn at all.
 *
 * NOT DRAWN RATHER THAN DRAWN VERTICALLY: a line from a block to another block
 * directly beneath it, past six unrelated paragraphs, says nothing. The pairing
 * is still there on hover, which is the part that survives the narrow window.
 */
@media (max-width: 62rem) {
  .sw-side-head, .sw-side-body { grid-template-columns: 1fr; }
  .sw-key { display: none; }
  .sw-wires { display: none; }
  .sw-side-body { height: auto; min-height: 0; }
  .sw-col { max-height: 26rem; }
  /* THE HEADINGS STAY WITH THEIR COLUMNS when they stack, so it is still
     obvious which is the account and which is the rewrite. */
  .sw-side-head .sw-col-head:last-of-type { margin-top: .6rem; }
}
