/* ═════════════════════════════════════════════════════════════════════════
   NITRATE — SAGAS.  #/sagas, five connected runs, one at a time.
   Owns .sg-* and .sgd-*.  Reads tokens.css and redefines nothing in it.

   ── THE ARGUMENT, IN ONE PARAGRAPH ────────────────────────────────────────
   The rack this replaces was a card catalogue: thirty-four small uniform
   labelled tiles, because at thirty-four the only thing that matters is that
   all of them are reachable without a carousel.  At FIVE that constraint is
   gone and the opposite one applies — each run has to be given the room to
   look like itself.  So: one saga fills the screen, and its layout is a
   drawing of how that saga is actually built.

   ── ⛔ COLOUR ─────────────────────────────────────────────────────────────
   tokens.css, plus MEASURED tints (`--tint`, set inline from `tint` /
   `poster_tint`, which the poster agent extracts off the real artwork).
   Nothing on this page has a colour that somebody typed in.  Safety orange
   keeps its one site-wide meaning: the thing you picked, and the focus ring.

   ── TYPE ──────────────────────────────────────────────────────────────────
   Two voices and no third.  --font-marquee (Big Shoulders, condensed
   industrial) shouts the saga name and the block labels; --font-mono is the
   EDGE CODE voice — years, counts, intervals, episode numerals, everything
   that is a figure.  --font-ui carries the one or two lines of prose.  The
   contrast between a 92px condensed name and a 10.5px mono year IS the page's
   typographic idea; there is no third face to dilute it.
   ═════════════════════════════════════════════════════════════════════════ */

.sg-page {
  /* ONE KNOB PER THING.  Every layout reads --print, so a Bond frame and a
     Star Wars frame can never disagree about what one film looks like. */
  --print: 104px;          /* one film, wide.  2:3 art => 156px tall         */
  --pgap: 12px;            /* between two films of one run                   */
  --rule-h: 7px;           /* the tint rule under a run                      */
  --rail: 228px;

  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  gap: 0;
  align-items: start;
  min-height: 60vh;
  /* the house gutter — the same var(--pad) .lnd uses, so this page's left edge
     lines up with the masthead's and with every other view's */
  padding: 16px var(--pad) 64px;
}

/* ───────────────────────────────────────────────────────── THE RAIL
   Quiet on purpose.  The boldness on this page is spent entirely on the five
   drawings; a selector that competed with them would be an accessory.  Five
   rows, each carrying its own measured tint rule so the rail is a preview of
   the instrument the stage uses. */
/* 🔴 --sg-top IS THE MASTHEAD, MEASURED.  .masthead is `position: sticky;
   top: 0; z-index: 30`, so a second sticky at top:0 slides UNDER it and loses
   its first row — measured live, the James Bond tab disappeared behind the
   stats strip on any scroll.  sagas.js reads the masthead's real height into
   --sg-top and re-reads it on every tier crossing; the fallback of 0 is the
   old behaviour rather than a broken one. */
.sg-rail {
  position: sticky; top: var(--sg-top, 0px); z-index: 2;
  align-self: start;
  max-height: calc(100vh - var(--sg-top, 0px));
  max-height: calc(100dvh - var(--sg-top, 0px));
  overflow-y: auto;
  padding: 14px 16px 18px 0;
  background: var(--ground);
  border-right: var(--hair) solid var(--rule);
}
/* one shared line: the way out, and how many runs there are */
.sg-rail-lab {
  display: flex; align-items: baseline; gap: 10px;
  padding: 0 2px 8px;
}
.sg-rail-n {
  margin-left: auto;
  font-family: var(--font-mono); font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}
.sg-rail-list { display: flex; flex-direction: column; gap: 2px; }

/* 🔴 SIZED SO FIFTEEN FIT WITHOUT SCROLLING THEIR OWN LIST.  Under a 108px
   masthead a 900px window leaves 792px; at the five-board size (65px a tab)
   fifteen wanted 1,005px and three boards sat below the fold of the navigation
   itself.  49px a tab brings the whole deck on screen at once, which is the
   one property the rack was built to protect and this page must not lose.
   The count moves up beside the name and the span takes the second line, so
   nothing is dropped to get there — the rail still says what every other
   surface says.  overflow-y on .sg-rail stays as the belt for short windows. */
.sg-tab {
  appearance: none; -webkit-appearance: none;
  display: block; width: 100%; text-align: left;
  background: none; border: 0; cursor: pointer;
  padding: 5px 9px 4px 10px;
  position: relative;
  color: var(--ink-2);
  border-left: 3px solid var(--tint, var(--rule));
}
.sg-tab-top { display: flex; align-items: baseline; gap: 8px; }
.sg-tab-name {
  flex: 0 1 auto; min-width: 0;
  font-family: var(--font-marquee); font-weight: 600;
  font-size: 17px; line-height: 1; letter-spacing: .01em;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sg-tab-n {
  margin-left: auto;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ink-2);
}
.sg-tab-yr {
  display: block; margin-top: 1px; line-height: 1.25;
  font-family: var(--font-mono); font-size: 10.5px;
  font-variant-numeric: tabular-nums; color: var(--muted);
}
.sg-tab-top { line-height: 1; }
.sg-tab .sg-rule { height: 3px; margin-top: 3px; opacity: .55; }
.sg-tab:hover { background: var(--panel); }
.sg-tab:hover .sg-tab-name { color: var(--ink); }
.sg-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sg-tab.is-on {
  background: var(--panel-2);
  border-left-color: var(--accent);
}
.sg-tab.is-on .sg-tab-name { color: var(--ink); }
.sg-tab.is-on .sg-rule { opacity: 1; }

.sg-back {
  appearance: none; -webkit-appearance: none;
  margin: 0; padding: 2px 0;
  background: none; border: 0;
  color: var(--muted); cursor: pointer;
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase;
}
.sg-back:hover { color: var(--ink); border-color: var(--muted); }
.sg-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ───────────────────────────────────────────────────────── THE STAGE */
.sg-stage { min-width: 0; padding: 0 0 0 26px; }

/* THE PLATE.  Real artwork at the top of every saga — the cached 1280px
   backdrop of the best-rated film on that board — and the one place on this
   page where the type is allowed to be enormous.  ⛔ If the image fails, the
   name stands on the saga's own measured tint; there is no grey box. */
.sg-plate {
  position: relative;
  height: clamp(210px, 30vh, 330px);
  overflow: hidden;
  background: var(--tint, var(--panel-2));
  border: var(--hair) solid var(--rule);
}
.sg-plate img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 42%;
  opacity: .74;
}
.sg-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, var(--ground) 0%,
                    color-mix(in srgb, var(--ground) 62%, transparent) 46%,
                    transparent 88%),
    linear-gradient(0deg, var(--ground) 0%,
                    color-mix(in srgb, var(--ground) 30%, transparent) 46%,
                    transparent 78%);
}
.sg-plate.is-bare img { display: none; }
.sg-plate-cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 0 22px 14px 20px;
}
.sg-title {
  margin: 0;
  font-family: var(--font-marquee); font-weight: 800;
  font-size: clamp(40px, 6.4vw, 92px);
  line-height: .88; letter-spacing: -.005em;
  text-transform: uppercase;
  color: var(--ink);
}
.sg-lede {
  margin: 8px 0 0; max-width: 62ch;
  font-family: var(--font-ui); font-size: 13.5px; line-height: 1.42;
  color: var(--ink-2);
}

/* The figures.  Same shape as the masthead's stats strip so the page speaks
   the house's own figure voice rather than inventing a stat tile. */
.sg-bar { padding: 12px 0 0; }
.sg-figs {
  display: flex; flex-wrap: wrap; gap: 4px 20px;
  font-family: var(--font-mono); font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.sg-fig b { color: var(--ink); font-weight: 700; }
.sg-fig .k { color: var(--muted); }
.sg-fig .k::before { content: ' '; }

.sg-caveat {
  margin: 10px 0 0; max-width: var(--cap-col);
  max-width: min(70ch, 100%);
  font-family: var(--font-ui); font-size: 12px; line-height: 1.45;
  color: var(--muted);
}
.sg-caveat.is-warn { color: var(--crit); }

/* ───────────────────────────────────────────────── PRINT + RUN + RULE */
.sg-print {
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; padding: 0; cursor: pointer;
  width: var(--print); flex: 0 0 var(--print);
  text-align: left; color: var(--ink-2);
  display: block;
}
.sg-art {
  position: relative; display: block;
  aspect-ratio: 2 / 3; width: 100%;
  background: var(--tint, var(--panel-2));
  border: var(--hair) solid var(--rule);
  overflow: hidden;
}
.sg-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ⛔ the fallback is TYPE, never a placeholder image */
.sg-art.is-type { display: flex; align-items: flex-end; }
.sg-art-t {
  padding: 6px 7px;
  font-family: var(--font-marquee); font-weight: 700;
  font-size: 13px; line-height: 1.02; text-transform: uppercase;
  color: var(--ink);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
}
.sg-badge {
  position: absolute; top: 0; left: 0;
  padding: 2px 5px 2px 4px;
  background: var(--ground); color: var(--ink);
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: .04em;
  border-right: var(--hair) solid var(--rule);
  border-bottom: var(--hair) solid var(--rule);
}
.sg-cap { display: block; padding: 5px 2px 0; }
.sg-t {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  font-family: var(--font-ui); font-size: 11.5px; line-height: 1.22;
  color: var(--ink-2);
}
.sg-y, .sg-pn {
  display: block; margin-top: 1px;
  font-family: var(--font-mono); font-size: 10.5px;
  font-variant-numeric: tabular-nums; color: var(--muted);
}
.sg-pn { color: var(--cool); }
.sg-print:hover .sg-t { color: var(--ink); }
.sg-print:hover .sg-art { border-color: var(--muted); }
.sg-print:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* a played print is marked on its own edge — the site's one existing signal
   for "this has been through the gate", not a new badge */
.sg-print.is-watched .sg-art { box-shadow: inset 0 -3px 0 var(--accent); }

.sg-run { width: max-content; max-width: 100%; }
.sg-prints { display: flex; gap: var(--pgap); align-items: flex-start; }
.sg-rule {
  height: var(--rule-h); margin-top: 9px;
  background: var(--panel-2);
  background-repeat: no-repeat;
}

/* ───────────────────────────────────────────────────── ERAS — BOND
   Six blocks left-aligned and NOT justified, so the ragged right edge is a
   chart of each man's tenure drawn in the actual prints.  Nothing labels that;
   Lazenby's single frame between two long runs is the caption. */
.sgd { margin-top: 24px; }
.sg-era, .sg-blk, .sg-aside { margin: 0 0 26px; }
/* the house's own section head — title, a hairline that fills, the figure at
   the right (.lnd-sec-hd / .lnd-rule / .lnd-sec-meta).  A count pushed to the
   far end of a 1,200px header by `margin-left:auto` reads as stranded; the
   rule is what connects it back to the name it belongs to. */
.sg-era-hd {
  display: flex; align-items: baseline; gap: 12px;
  margin-bottom: 10px;
}
.sg-era-name {
  margin: 0; flex: 0 1 auto; min-width: 0;
  font-family: var(--font-marquee); font-weight: 700;
  font-size: 27px; line-height: 1; letter-spacing: .01em;
  text-transform: uppercase; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sg-era-yr, .sg-era-n {
  font-family: var(--font-mono); font-size: 11px;
  font-variant-numeric: tabular-nums; color: var(--muted);
  white-space: nowrap;
}
.sg-era-hr {
  flex: 1 1 auto; min-width: 16px; height: 0;
  border-top: var(--hair) solid var(--rule);
  transform: translateY(-6px);
}
.sg-note {
  margin: -3px 0 9px; max-width: min(66ch, 100%);
  font-family: var(--font-ui); font-size: 12px; line-height: 1.42;
  color: var(--muted);
}
/* the one film that is not part of the official line stands apart and says so */
.sg-aside, .sg-era.is-aside {
  margin-top: 30px; padding-top: 20px;
  border-top: var(--hair) dashed var(--rule);
}
.sg-era.is-aside .sg-era-name, .sg-aside .sg-era-name {
  font-size: 20px; color: var(--ink-2);
}

/* ─────────────────────────────────────────────── ORDERS — STAR WARS
   Two rails of the same fifteen films and a ribbon from each film's place in
   one to its place in the other.  The ribbons are coloured by the film's own
   measured poster tint, so a single film can be traced across the crossing. */
.sgd-orders .sg-web { position: relative; }
.sg-ord-lab {
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  padding: 0 0 8px;
}
.sg-ord:last-child .sg-ord-lab { padding: 8px 0 0; }
.sg-ord-run { width: 100%; }
/* ⚠️ SCOPED TO THE RAILS, not to the layout.  `.sgd-orders .sg-print` also
   caught the three Lego specials in the aside below and stretched them to a
   third of the page each — measured live, they were four times the size of a
   film on the rail above and looked like the most important thing here. */
.sg-ord-run .sg-prints { gap: 8px; }
.sg-ord-run .sg-print { flex: 1 1 0; width: auto; min-width: 0; }
/* fifteen across is 71px of caption; a third line is the difference between
   "Star Wars: Episode VI…" and something you can actually read */
.sg-ord-run .sg-t { -webkit-line-clamp: 3; font-size: 11px; }
.sg-ribbons {
  display: block; width: 100%; height: 96px;
  overflow: visible;
}

/* ────────────────────────────────────────────── BREAKS — TRANSFORMERS
   Two breaks, drawn as the two different things they are.  A GAP is calendar:
   it gets width in proportion to its years (--yrs), so twenty-one years of
   nothing is physically a long way across the page.  A CUT is a continuity
   change inside one year: a fixed narrow splice mark, because what changed
   was the film and not the clock. */
.sgd-breaks .sg-strip {
  display: flex; align-items: stretch; gap: 0;
  padding-top: 50px;                 /* the absolute heads live in here */
}
/* 🔴 TWO RULES THAT KEEP THIS STRIP INSIDE THE PAGE, both found by measuring
   it at 1440 and 1000 where it ran 158px off the right edge:
   · the HEAD IS ABSOLUTE, so a 250px label on a ONE-FILM block ("G1,
     animated" over a single 1986 poster) cannot set that block's width.  It
     overhangs into the gap beside it, which is empty by construction.
   · the PRINTS SHRINK.  `flex: 0 1` with no grow means they are 104px when
     there is room and narrower when there is not, so nine prints, a
     twenty-one-year void and a splice fit any stage width without a
     horizontal scrollbar — which this house does not use. */
.sg-blk { flex: 0 1 auto; min-width: 0; margin-bottom: 0; position: relative; }
/* ⚠️ TWO LINES AND A SMALLER NAME, because an absolute head still has to fit
   the PAGE even when it does not set its block's width.  At 1000px the last
   block is three narrow prints wide and a one-line head ran 85px off the right
   edge — measured.  Stacking the figures under the name halves the head's
   max-content width and the overhang goes with it. */
.sgd-breaks .sg-era-hd {
  position: absolute; top: -42px; left: 0;
  display: block; width: max-content; max-width: 260px; margin: 0;
}
.sgd-breaks .sg-era-name { display: block; font-size: 17px; }
.sgd-breaks .sg-era-yr, .sgd-breaks .sg-era-n { display: inline; }
.sgd-breaks .sg-era-n::before { content: ' · '; }
.sgd-breaks .sg-era-hr { display: none; }
.sgd-breaks .sg-run { width: auto; min-width: 0; }
.sgd-breaks .sg-print { flex: 0 1 var(--print); min-width: 0; }
.sg-join {
  flex: 0 0 auto;
  position: relative;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 4px;
  padding: 0 10px;
  align-self: stretch;
}
.sg-join.is-gap {
  /* the years are the flex basis AND the label — one number, two jobs, so
     they cannot disagree */
  flex: 1 1 calc(var(--yrs, 1) * 9px);
  min-width: 96px;
}
.sg-join.is-gap::before {
  content: ''; position: absolute; left: 6px; right: 6px; top: 50%;
  border-top: var(--hair) dashed var(--rule);
}
/* the splice: two bars through the strip, with the label punched through them
   on the page's own ground — a cut in the film rather than a wait */
/* ⚠️ WIDE ENOUGH TO HOLD ITS OWN LABEL.  At 62px the note overhung 8px each
   side and printed across the last poster of the run beside it. */
.sg-join.is-cut { flex: 0 0 104px; width: 104px; padding: 0; }
.sg-join.is-cut::before,
.sg-join.is-cut::after {
  content: ''; position: absolute; top: 6%; bottom: 6%;
  border-left: 2px solid var(--rule);
}
.sg-join.is-cut::before { left: 47px; }
.sg-join.is-cut::after { left: 55px; }
.sg-join-n, .sg-join-t {
  position: relative;
  background: var(--ground); padding: 2px 6px;
  font-family: var(--font-mono); font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  text-align: center; line-height: 1.25;
}
.sg-join-n { color: var(--ink); font-weight: 700; }
.sg-join-t { color: var(--muted); }
.sg-join.is-cut .sg-join-t {
  width: 96px; padding: 8px 0;
}

/* ────────────────────────────────────────────────── FORK — FAST
   The main line runs straight and the spur leaves it after the film it left
   after, drops a level and STOPS.  The stop is drawn: a spur with an open end
   would be a promise of a twelfth film. */
.sg-forkw { position: relative; width: max-content; max-width: 100%; }
.sg-spur {
  position: relative;
  margin-top: 26px;
  padding-left: calc(var(--at) * (var(--print) + var(--pgap)));
}
.sg-elbow {
  position: absolute; top: -26px;
  left: calc(var(--at) * (var(--print) + var(--pgap))
             - var(--pgap) - (var(--print) / 2));
  width: calc(var(--print) / 2 + var(--pgap)); height: 42px;
  border-left: 2px solid var(--rule);
  border-bottom: 2px solid var(--rule);
  border-bottom-left-radius: 16px;
}
.sg-spur-body { position: relative; }
.sg-stop {
  position: absolute; top: 52px;
  left: calc(var(--sn, 1) * (var(--print) + var(--pgap)));
  width: 2px; height: 72px;
  background: var(--rule);
}
.sg-stop::after {
  content: 'ends here';
  position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
  white-space: nowrap;
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}

/* ──────────────────────────────────────── LINE — MISSION: IMPOSSIBLE
   One rule, films placed by their real year, intervals named.  There is
   nothing else to draw and that IS the finding. */
.sgd-line { padding-top: 4px; }
/* 🔴 THE PRINT IS SIZED BY THE TIGHTEST INTERVAL IN THE RUN.  Placing films at
   their real year is the whole idea here, and the run's closest pair is two
   years apart out of twenty-nine — 7% of the track, or 76px at 1440.  At the
   house's 104px print the last two films OVERLAPPED, one poster sitting on top
   of the other, which is exactly the kind of thing a passing test suite does
   not catch.  drawLine() publishes the coefficient as --line-print; the phone
   tier overrides --print below and wins because it comes later in this file,
   which is why the JS must never set --print directly. */
.sgd-line .sg-track { --print: var(--line-print, 104px); }
.sg-track {
  position: relative;
  /* 40 for the rule and the intervals above the prints, 1.5× for the art, and
     the rest for a THREE-line caption plus its year — the clamp below is 3, and
     at 2 lines' worth of room the end markers sat on top of the last film's
     year.  Measured on Indiana Jones. */
  height: calc(var(--print) * 1.5 + 106px);
  padding: 0 calc(var(--print) / 2);
}
/* ⭐ THE ONE PIECE OF ARITHMETIC ON THIS PAGE, written once.  `--x` is a plain
   fraction 0…1 of the run's span in YEARS.  The track reserves half a print of
   padding at each end, so the usable span is (100% − print) and a film's centre
   sits at  print/2 + x·(100% − print).  translateX(-50%) does the centring, so
   the same two lines place a print, an interval label and nothing else. */
.sg-track-rule {
  position: absolute; margin: 0;
  left: calc(var(--print) / 2); right: calc(var(--print) / 2);
  top: 0; height: 9px;
}
.sg-print.sg-at {
  position: absolute; top: 40px;
  left: calc(var(--print) / 2 + var(--x) * (100% - var(--print)));
  transform: translateX(-50%);
}
/* the film hangs off the rule it belongs to */
.sg-print.sg-at::before {
  content: ''; position: absolute; left: 50%; top: -31px;
  width: var(--hair); height: 31px; background: var(--rule);
}
.sg-int {
  position: absolute; top: 17px;
  left: calc(var(--print) / 2 + var(--x) * (100% - var(--print)));
  transform: translateX(-50%);
  padding: 0 5px; background: var(--ground);
  font-family: var(--font-mono); font-size: 10.5px;
  font-variant-numeric: tabular-nums; color: var(--muted);
  white-space: nowrap;
}
/* the track has vertical room the other layouts do not, so the caption can
   afford the third line that turns "Mission: Impossibl…" into a title */
.sgd-line .sg-t { -webkit-line-clamp: 3; }
.sg-ends {
  display: flex; justify-content: space-between;
  margin-top: 6px;
  font-family: var(--font-marquee); font-weight: 700; font-size: 30px;
  line-height: 1; color: var(--ink-2);
}

.sg-fail { padding: 40px 4px; }
.sg-fail h2 {
  margin: 0 0 8px;
  font-family: var(--font-marquee); font-weight: 800; font-size: 46px;
  text-transform: uppercase; color: var(--ink);
}
.sg-fail p { margin: 0; font-family: var(--font-ui); color: var(--crit); }

/* ═════════════════════════════════════════════════════════════════════════
   PHONE — 760px.  DESIGNED, not scaled.  MOBILE_SPEC §1: two tiers, no third.

   Three of the five drawings are natively vertical (a break, a fork and a
   line all read down a column at least as well as across a row), so those
   simply turn.  The two that do not each get a real answer:

     BOND    the tint rule DETACHES from the prints and becomes a proportional
             bar (--share, the block's size against the biggest block), so the
             tenure comparison the ragged edge carries on a desk is carried by
             the bar here.  The prints themselves become a snapping row you
             swipe — six short rows, not one six-screen page.
     STARWARS ribbons need two rails side by side and a phone has room for one.
             So the crossing is expressed as NUMBERS instead of lines: one rail
             in story order, each film carrying its release position, which
             runs 6, 7, 9, 8, 13, 11, 1, 2 … — visibly not in order.
   ═════════════════════════════════════════════════════════════════════════ */
@media (max-width: 760px) {
  .sg-page {
    --print: 92px;
    --pgap: 10px;
    grid-template-columns: minmax(0, 1fr);
  }
  /* the rail becomes a strip under the masthead — one thumb-height row of
     five, scrolled sideways, and it stays put while the stage scrolls */
  .sg-rail {
    position: sticky; top: 0; z-index: 3;
    padding: 8px 0 9px;
    border-right: 0; border-bottom: var(--hair) solid var(--rule);
    background: var(--ground);
  }
  .sg-rail-lab { display: none; }
  .sg-rail-list {
    flex-direction: row; gap: 8px;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity; scroll-padding-left: 2px;
    padding-bottom: 2px;
  }
  .sg-tab {
    flex: 0 0 auto; width: auto; min-width: 138px;
    scroll-snap-align: start;
    padding: 7px 12px 7px 10px; min-height: 44px;
  }
  .sg-tab-name { font-size: 17px; white-space: nowrap; }
  .sg-tab-top { gap: 10px; }
  .sg-tab-n, .sg-tab-yr { font-size: 11px; }
  .sg-tab .sg-rule { height: 3px; margin-top: 5px; }
  /* ⚠️ the phone keeps its way out in the shell's own bottom tab bar, so the
     rail's link would be a second answer to the same question */
  .sg-back { display: none; }

  .sg-stage { padding: 0 0 32px; }
  .sg-plate { height: auto; aspect-ratio: 16 / 9; max-height: 42vh; }
  .sg-scrim {
    background: linear-gradient(0deg, var(--ground) 2%,
                color-mix(in srgb, var(--ground) 46%, transparent) 52%,
                transparent 92%);
  }
  .sg-plate-cap { padding: 0 12px 10px; }
  .sg-title { font-size: clamp(30px, 10vw, 46px); }
  .sg-lede { font-size: 12.5px; margin-top: 5px; }
  .sg-figs { gap: 3px 16px; font-size: 11px; }
  .sg-era-name { font-size: 21px; }
  .sg-era-yr, .sg-era-n, .sg-y, .sg-pn, .sg-int, .sg-join-n, .sg-join-t,
  .sg-tab-meta, .sg-rail-lab { font-size: 11px; }
  .sgd { margin-top: 18px; }

  /* every run becomes a swipeable strip.  ⚠️ a rail that snaps must set its
     own scroll-padding or the first card parks under its own inset
     (MOBILE_SPEC §4.7) */
  .sg-run { width: auto; max-width: none; }
  .sg-prints {
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity; scroll-padding-left: 0;
    padding-bottom: 4px;
  }
  .sg-print { scroll-snap-align: start; }

  /* BOND — the rule leaves the prints and becomes the comparison */
  .sgd-eras .sg-rule { width: calc(var(--share) * 100%); margin-top: 8px; }

  /* STAR WARS — the ribbons need two rails side by side and a phone has room
     for one, so the crossing is carried by NUMBERS instead of lines: the story
     rail comes first (its eras climb 32 BBY → 35 ABY while its years jump
     1999, 2002, 2008, 2005…) and the release rail second (its years climb while
     its eras jump).  Both rails are still here and every film is still on both. */
  .sg-ribbons { display: none; }
  .sgd-orders .sg-web { display: flex; flex-direction: column-reverse; }
  .sgd-orders .sg-ord { display: flex; flex-direction: column; margin-bottom: 16px; }
  /* ⚠️ column-reverse put the two labels back to back in the middle — run,
     label, label, run.  Each rail keeps its own label ABOVE it instead. */
  .sg-ord:last-child .sg-ord-lab { order: -1; padding: 0 0 8px; }
  .sg-ord-run .sg-print { flex: 0 0 var(--print); width: var(--print); }
  .sg-ord-run .sg-t { -webkit-line-clamp: 2; font-size: 11.5px; }

  /* TRANSFORMERS — the strip turns; the gap becomes a vertical void that is
     still sized by its years, and the splice is still a splice */
  .sgd-breaks .sg-strip { flex-direction: column; }
  .sg-blk { margin-bottom: 0; }
  .sg-join { align-self: stretch; padding: 14px 0; flex-direction: row; gap: 10px; }
  .sg-join.is-gap { min-width: 0; min-height: calc(var(--yrs, 1) * 2.2px + 30px); }
  .sg-join.is-gap::before {
    left: 50%; right: auto; top: 6px; bottom: 6px;
    border-top: 0; border-left: var(--hair) dashed var(--rule);
  }
  .sg-join.is-cut { width: auto; min-height: 44px; }
  .sg-join.is-cut::before, .sg-join.is-cut::after {
    top: auto; bottom: auto; left: 50%; height: 2px; width: 40%;
    border-left: 0; border-top: 2px solid var(--rule);
    transform: translateX(-50%);
  }
  .sg-join.is-cut::before { top: 12px; }
  .sg-join.is-cut::after { top: 20px; }
  .sg-join.is-cut .sg-join-t {
    position: static; transform: none; width: auto; margin-top: 14px;
  }

  /* FAST — the spur indents instead of dropping */
  .sg-forkw { width: auto; }
  .sg-spur { padding-left: 22px; margin-top: 22px; }
  .sg-elbow {
    top: -18px; left: 0;
    width: 16px; height: 34px;
  }
  /* ⚠️ `position: relative`, not `static`.  The label is an ::after, and with a
     static stop it anchored to .sg-spur-body instead and printed "ENDS HERE"
     across the HOBBS & SHAW heading — measured live at 390. */
  /* 🔴 `left` MUST BE RESET WITH THE POSITION.  The desk rule places the stop
     past the last spur print with `left: calc(--sn * (print + gap))`; switching
     to `position: relative` here without clearing it turned that into a
     306px RELATIVE OFFSET on X-Men's three-film spur and pushed the "ends here"
     label 45px off the right of a 390px screen.  Measured — and it was latent
     before the spur could hold more than one film. */
  .sg-stop {
    position: relative; left: auto; top: auto; display: block;
    width: 44px; height: 2px; margin: 16px 0 0 2px;
  }
  .sg-stop::after { left: 52px; top: 50%; }

  /* M:I — the track turns.  Absolute year placement does not survive a
     92px-wide column, so the run becomes a vertical rule with the prints
     hung off it and the intervals in the gaps between them. */
  .sgd-line .sg-track {
    --print: 92px;                    /* ⚠️ later than the --line-print rule
                                         above on purpose — see the note there */
    height: auto; padding: 0 0 0 26px;
    display: flex; flex-direction: column; gap: 22px;
  }
  /* ⚠️ The rule turns with the track, and it turns into ONE colour: the inline
     gradient is `90deg` and cannot be re-aimed without rebuilding it, so the
     vertical rule is the SAGA's own measured tint rather than a rotated
     approximation of the eight films'.  For this run that loses nothing — all
     eight tints are the same red, which is the finding — and it is still a
     measured colour, never a chosen one. */
  .sgd-line .sg-track-rule { display: none; }
  .sgd-line .sg-track::before {
    content: ''; position: absolute; left: 9px; top: 8px; bottom: 8px;
    width: 7px;
    background: var(--tint, var(--panel-2));
  }
  /* the tick that hangs a film off a HORIZONTAL rule is nonsense on a vertical
     one — it drew a stray stub over the middle of every row */
  .sgd-line .sg-print.sg-at::before { display: none; }
  .sg-print.sg-at {
    position: relative; left: auto; top: auto; transform: none;
    display: flex; gap: 12px; align-items: center;
    width: 100%; flex: 0 0 auto;
  }
  .sg-print.sg-at .sg-art { width: var(--print); flex: 0 0 var(--print); }
  .sg-print.sg-at .sg-cap { padding-top: 0; }
  .sg-print.sg-at .sg-t { font-size: 13px; -webkit-line-clamp: 3; }
  .sg-int {
    position: relative; left: auto; top: auto; transform: none;
    align-self: flex-start; margin-left: -18px; padding: 0 6px;
  }
  .sg-ends { display: none; }
}

@media (max-width: 430px) {
  .sg-page { --print: 84px; }
  .sg-tab { min-width: 124px; }
}

/* ⭐ TOUCH, not the 760 tier: a phone in landscape is 844px wide and above the
   mobile tier entirely (MOBILE_SPEC §10.3), and the finger is still a finger. */
@media (hover: none) {
  .sg-tab { min-height: 44px; }
  .sg-back { min-height: 44px; }
}

/* Motion.  Nothing here is on a clock — no carousel, no reveal, no ambient
   loop — so there is nothing to unwind but the browser's own smooth scroll. */
@media (prefers-reduced-motion: reduce) {
  .sg-prints, .sg-rail-list { scroll-behavior: auto; }
}

/* the one film-level exception the catalogue can produce, said plainly rather
   than filtered away — see the note in sagas.js::print */
.sg-pn.is-unreleased { color: var(--warn); }

/* a restored roster board (Marvel / DC) — one block per strand, no numbering
   across them, because a roster has no running order.  See the roster branch
   in sagas.js::paintStage. */
.sgd-roster .sg-run { margin-bottom: 22px; }
.sgd-roster .sg-prints { flex-wrap: wrap; row-gap: 14px; }

/* the LAST block has no page to its right, so its head hangs LEFT — into the
   splice beside it, which is empty by construction.  Without this it was the
   only thing on the page still reaching past the right edge (4px at 860). */
.sgd-breaks .sg-blk:last-child .sg-era-hd { left: auto; right: 0; text-align: right; }

/* ═══════════════════════════════════════ THE FRANCHISE MARK ON #/sagas ═══
   John, 2026-08-13: "bring that sagalogos inside of its saga page". The same
   cached clearLogo art the #/movies deck uses (sagalogos.py -> web/logos).
   ⛔ Both the <h2> and the tab name STAY in the DOM and are only clipped once
   the image really loads — a logo is a picture of a word, and the heading is
   what a screen reader, the document outline and in-page search rely on. */
/* ⚠️ `.sg-plate-cap .sg-mark`, not `.sg-mark`: `.sg-plate img` scores (0,1,1)
   and a bare class scores (0,1,0), so the reset LOST and the mark stayed
   absolute. Two classes (0,2,0) win. Measured twice — the first fix looked
   right and changed nothing. */
.sg-plate-cap .sg-mark {
  /* 🔴 RESET FIRST. `.sg-plate img` (above) is a BROAD selector that makes the
     backdrop fill the plate — position:absolute, inset:0, 100%x100%, cover —
     and this mark is an <img> inside .sg-plate-cap, so it inherited all of it.
     The result: the wordmark was pulled out of flow and rendered ON TOP of the
     lede. ⚠️ Measured, not guessed — the mark read position:absolute when
     nothing here set it. ⛔ Don't drop these resets. */
  position: static; inset: auto; opacity: 1;
  display: none;
  max-width: min(560px, 72%); max-height: 132px;
  height: auto; width: auto;
  object-fit: contain; object-position: left bottom;
  margin: 0 0 6px;
  filter: drop-shadow(0 2px 10px rgba(0,0,0,.6));
}
.sg-plate-cap.has-mark .sg-mark { display: block; }
.sg-plate-cap.has-mark .sg-title {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* the rail row — hard-capped, and it replaces ONLY the name */
.sg-tab-mark {
  display: none;
  max-width: 116px; max-height: 20px;
  height: auto; width: auto; object-fit: contain; object-position: left center;
}
.sg-tab.has-mark .sg-tab-mark { display: block; }
.sg-tab.has-mark .sg-tab-name {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* ⭐ Most marks are white-on-transparent: they vanish on a light panel, so the
   LIGHT theme gets a drop shadow and dark leaves them clean. */
.sg-tab.has-mark .sg-tab-mark { filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sg-tab.has-mark .sg-tab-mark { filter: none; }
}
:root[data-theme="dark"] .sg-tab.has-mark .sg-tab-mark { filter: none; }
