/* Cargostrife TV - broadcast overlay furniture.
 *
 * ONE IMPLEMENTATION, TWO HOSTS. It mounts over the <video> on tv.html today,
 * and over the game itself on the stream box when the channel goes live. So
 * nothing here may assume a page around it: no body styles, no fixed pixel
 * sizes, every measurement relative to the container.
 *
 * SCALE. --u is one percent of the container width, set by the script on every
 * resize. A lower third that reads correctly in a 700px web player AND in a
 * 1920px broadcast is the whole reason for it; a px anywhere in this file is a
 * bug at one size or the other.
 *
 * COLOUR. --team is the company being watched, straight from the payload, and
 * every accent reads it. It transitions, so a cut between companies carries the
 * livery change instead of snapping.
 */

.cstv {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  font-family: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
  color: var(--ink);
  --u: 8px;
  --ink: #eaf1f9;
  --dim: #93a3b8;
  --ground: rgba(8, 12, 17, 0.86);
  --edge: rgba(126, 150, 180, 0.22);
  --team: #5aa9e6;
  --safe: calc(var(--u) * 3.2);
  /* THE CORNER INSET FOR THE TWO COMMENTARY CARDS - one number, both sides
   * (user 2026-09-10: "an equal distance from the screen edge so its
   * symmetrical"). 6u = 115px at 1920: the right card clears the corner
   * logo YouTube paints over the player (the old 7u offset was for the same
   * logo and was only ever needed on the right), and the left card comes in
   * off the safe margin to mirror it. If the logo still touches, this is
   * the number to move. */
  --corner: calc(var(--u) * 6);
}

.cstv * { box-sizing: border-box; margin: 0; }

.cstv .mono {
  font-family: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* ---- zones ------------------------------------------------------------- */
.cstv .z { position: absolute; display: flex; }
/* THE CORNERS WERE REDEALT (user ruling 2026-09-08: "move the date to the
 * bottom right the text to the bottom left and the currently player name to
 * the top left"). Three zones rotate:
 *   top left     the company being watched   (was the world/date plate)
 *   bottom left  what the desk is saying     (was bottom right)
 *   bottom right the world and the date      (was top left)
 * The LIVE mark keeps the top right and the insert cards keep the right edge.
 *
 * THE DATE IS HELD OFF THE CORNER ON PURPOSE. YouTube burns its own watermark
 * into the bottom right of the player, so the plate is pushed a further 7% of
 * frame width to the LEFT of the safe margin to sit clear of it - low into the
 * corner, as asked, but not underneath somebody else's logo. That offset is
 * the one number to move if the watermark ever shifts. */
/* REDEALT AGAIN (user 2026-09-10: "move the date/server box from the
 * bottom right to the top right lets build it into the live button so that
 * bottom right is free for a cohost"). The world/date plate now holds the
 * TOP right with LIVE drawn inside it, the LIVE pill's own zone is empty,
 * and the bottom right is the second commentary card (.z-say2). The 7u
 * watermark offset moved down to that card with the corner. */
.cstv .z-ident { top: var(--safe); right: var(--safe); }
.cstv .z-state { top: var(--safe); right: var(--safe); }
.cstv .z-table { top: 50%; right: var(--safe); transform: translateY(-50%); }
/* THE LOWER THIRD AND THE COMMENTARY SHARE THE BOTTOM AND MUST NOT FIGHT FOR
 * IT. Centred subtitles over a bottom-left name plate looked right on an empty
 * frame and clipped the income figure the moment both were up. They sit side
 * by side instead: who on the left, what the desk is saying on the right. The
 * widths are deliberately under half each so a long company name and a long
 * line of commentary still clear each other. */
/* 56%, WIDENED ON REQUEST (user 2026-09-09: "company name panels can be
 * abit wider"). Nothing sits beside it - the LIVE mark keeps the top RIGHT
 * corner, the commentary is bottom-left and the delivery card hangs below -
 * so this width was never contested and 47% was simply cautious. It buys
 * the name column about 860px against 690.
 *
 * IT DOES NOT FIX A NAME THAT ARRIVES CUT. "Yardley Distributi" reaches the
 * overlay already truncated: the payload carries
 * `44-INDU-E-AI Yardley Distributi`, and with a 13-character badge against
 * OpenTTD's 31-character company-name limit an AI has 18 left for a name.
 * No width here can recover those letters. */
/* 62% (user 2026-09-10: "make the box wider as well if needed"): the top
 * right now holds the date plate, which is no wider than the LIVE pill
 * plus a world name, so the plate still has the width. The .who.long
 * threshold in tvoverlay.js rose with it (34 -> 38). */
.cstv .z-lower { top: var(--safe); left: var(--safe); max-width: 62%; }
.cstv .z-say   { left: var(--corner); width: 44%; justify-content: flex-start; }

/* WITH NOTHING BESIDE IT the commentary takes more room, but it STAYS PUT.
 * It used to slide to the middle of the frame, which was right while it lived
 * in the bottom right corner with the name plate beside it - now the date
 * plate holds the bottom right, and a caption that centred itself would drift
 * under it every time the name plate happened to be down. A caption that moves
 * house depending on what else is on screen is harder to read than one that
 * simply gets wider, so it grows leftward-anchored instead. */
.cstv .z-say.solo { width: 62%; }
/* PAIRED: two cards share the bottom, 42% each, both --corner (6u) in from
 * their edge: 42 + 42 + 12 = 96%, so the two can never meet even when both
 * are at full width, and the picture is symmetrical. */
.cstv .z-say.paired { width: 42%; }
.cstv .z-say2 { right: var(--corner); width: 42%;
                justify-content: flex-end;
                bottom: calc(var(--safe) + var(--u) * 0.6); }
/* THE SPEAKER GETS THE ROOM (user 2026-09-10: "extend their text box to
 * about 80% across towards the other head this will allow us to use
 * larger default font and make a better for tv display"). tvoverlay.js
 * seatPatch marks the zone `talking` or `resting` with the card: the
 * talking zone grows to 74%, the resting one shrinks to its portrait.
 * They cannot meet: 74 + 6 + 6 (the corners) + 7 (a portrait) is 93%,
 * and the portrait side is auto width, so the talking card ends at
 * ~80% with the resting face at 93%. The text takes the bigger face
 * the width was bought for: 1.85u, from 1.4u, and the name 1.05u. */
/* 74, from 80 (user 2026-09-10: "shorten the box just a little bit so it's
 * a little further away from the other speaker"): the card now ends at
 * ~80% of the frame, a clear 13% short of the resting face. */
.cstv .z-say.paired.talking, .cstv .z-say2.talking { width: 74%; }
.cstv .z-say.paired.resting, .cstv .z-say2.resting { width: auto; }
/* SUBTITLE SIZE (user 2026-09-10: "treat it like subtitles on tv ... they
 * don't display alot so it can be big"): 2.3u is 44px on a 1080 frame,
 * broadcast subtitle height, and tvoverlay.js subChunks keeps a chunk to
 * two lines of it. The solo card takes 2.1u on its narrower width. */
.cstv .z-say.paired.talking .say .tx, .cstv .z-say2.talking .say .tx {
  font-size: calc(var(--u) * 2.3);
  line-height: 1.22;
}
.cstv .z-say.solo .say .tx {
  font-size: calc(var(--u) * 2.1);
  line-height: 1.22;
}
.cstv .z-say.paired.talking .say .nm, .cstv .z-say2.talking .say .nm {
  font-size: calc(var(--u) * 1.05);
}
.cstv .z-break { inset: 0; }
/* THE BREAKING-NEWS STRIP (tvgfx.js g_news): centred across the top, in the
 * band above the name plate and the LIVE plate, which both start at --safe. */
.cstv .z-tele { top: 50%; left: 50%; transform: translate(-50%, -50%); pointer-events: none; }
.cstv .z-alert { top: calc(var(--u) * 0.5); left: 50%; transform: translateX(-50%);
                 max-width: 58%; justify-content: center; }

/* THE SPEAKERS SIT ON THE SAME FOOT AS THE DATE PLATE (user 2026-09-09:
 * "the speakers need to move down a smidge so theres no overlap of the
 * speakers by center windows"). 2.4u held the caption a further 35px up
 * the frame than anything else along the bottom, which cost the
 * delivery card the room it needed and bought nothing - the game's own
 * status bar is in the 48px the capture crops, not in this band.
 *
 * 0.6u is not a new number: it is z-ident's, so the two bottom-anchored
 * zones share one offset rather than each having its own. On a 1080
 * screen that puts the card's foot 25px clear of the cut. */
.cstv .z-say { bottom: calc(var(--safe) + var(--u) * 0.6); }

/* ---- shared ------------------------------------------------------------ */
.cstv .card {
  background: var(--ground);
  border: 1px solid var(--edge);
  -webkit-backdrop-filter: blur(calc(var(--u) * 0.5)) saturate(1.1);
  backdrop-filter: blur(calc(var(--u) * 0.5)) saturate(1.1);
}

/* ---- ident ------------------------------------------------------------- */
.cstv .ident { display: flex; align-items: stretch; }
.cstv .ident .mark {
  background: var(--team);
  color: #06090d;
  font-weight: 700;
  font-size: calc(var(--u) * 1.5);
  letter-spacing: 0.1em;
  padding: 0 calc(var(--u) * 0.9);
  display: flex;
  align-items: center;
  transition: background 600ms ease;
}
.cstv .ident .body { padding: calc(var(--u) * 0.5) calc(var(--u) * 1.1) calc(var(--u) * 0.6); }
.cstv .ident .world {
  font-weight: 600;
  font-size: calc(var(--u) * 1.45);
  line-height: 1.15;
  white-space: nowrap;
}
.cstv .ident .bar {
  margin-top: calc(var(--u) * 0.42);
  height: calc(var(--u) * 0.2);
  background: rgba(255, 255, 255, 0.14);
  position: relative;
  overflow: hidden;
}
.cstv .ident .bar i {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--team);
  transition: width 900ms ease, background 600ms ease;
}
.cstv .ident .prog {
  margin-top: calc(var(--u) * 0.34);
  font-size: calc(var(--u) * 0.85);
  letter-spacing: 0.1em;
  color: var(--dim);
}
/* LIVE, inside the plate: the same red dot and spaced caps the pill had,
 * as the first line of the body. */
.cstv .ident .onair {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.45);
  font-weight: 700;
  font-size: calc(var(--u) * 0.85);
  letter-spacing: 0.2em;
  margin-bottom: calc(var(--u) * 0.18);
}
.cstv .ident .onair .dot {
  display: inline-block;
  width: calc(var(--u) * 0.5);
  height: calc(var(--u) * 0.5);
  border-radius: 50%;
  background: #ff3b30;
  animation: cstv-pulse 2s ease-in-out infinite;
}

/* ---- live state -------------------------------------------------------- */
.cstv .state {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.55);
  padding: calc(var(--u) * 0.5) calc(var(--u) * 0.95);
}
.cstv .state .dot {
  width: calc(var(--u) * 0.6);
  height: calc(var(--u) * 0.6);
  border-radius: 50%;
  background: #ff3b30;
  animation: cstv-pulse 2s ease-in-out infinite;
}
.cstv .state .txt {
  font-weight: 700;
  font-size: calc(var(--u) * 1.05);
  letter-spacing: 0.2em;
}
@keyframes cstv-pulse { 50% { opacity: 0.25; } }

/* ---- lower third ------------------------------------------------------- */
/* THE CREST SITS BESIDE THE PLATE, NOT ON TOP OF IT (user 2026-09-09:
 * "the company thing needs to move up the company logo needds to move
 * left or right side and be bigger").
 *
 * The canvas used to be the first INLINE child, so it opened a line box
 * of its own ABOVE the strap and pushed the whole plate 45px down the
 * frame. The logo was not merely small - it was the thing holding the
 * company name down, which is why one change answers both halves.
 *
 * A grid gives it a column spanning both rows, so the strap now starts
 * at the safe margin exactly and the name rises with it. LEFT rather
 * than right on purpose: the say card below already carries the
 * speaker's portrait in its left column, so the two plates share one
 * left rail down the frame instead of each finding their own. */
.cstv .lower {
  width: 100%;
  display: grid;
  /* the card, then the mark's column at its right end (see .clogo) */
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  column-gap: calc(var(--u) * 0.8);
  align-items: start;
}
/* THE CEO'S PORTRAIT ON THE LEFT, THE MARK ON THE RIGHT (user 2026-09-13:
 * "more like the ratio of the npc portraits ... the full portrait not a face
 * crop ... the logo on the right side of the ui panel and the portrait on the
 * left ... round the corners and give the frame a color to match the players
 * team color", then "similar to how the hosts appear but bigger").
 *
 * THE HOSTS' PROPORTIONS, SCALED: a host portrait is 5.6u x 7.4u; this is
 * 9.4u x 12.4u, the same 0.76 and about 1.7 times the size. The picture is
 * the catalogue's HEAD-TO-WAIST crop (`<key>_w.jpg`, cut to the hosts' 324:430)
 * - "no need for anything below the waist", like the broadcasters.
 * 12.4u IS UNDER THE PLATE'S OWN HEIGHT (strap + card, measured 13.7u on air
 * with no title line), so it cannot push the card below it down; it sits on the
 * card's bottom edge. The frame is the team colour, like the card's own edge. */
.cstv .lower.cav-on {
  grid-template-columns: auto minmax(0, 1fr) auto;
}
.cstv .lower .cav {
  grid-column: 1;
  grid-row: 1 / span 2;
  align-self: end;
  box-sizing: border-box;
  width: calc(var(--u) * 9.4);
  height: calc(var(--u) * 12.4);
  border: calc(var(--u) * 0.24) solid var(--team);
  border-radius: calc(var(--u) * 0.6);
  overflow: hidden;
  background: #101820;
  transition: border-color 600ms ease;
}
.cstv .lower .cav img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0%;     /* the crop is already head-to-waist */
}
.cstv .lower .strap {
  /* the column BEFORE the mark: col 1 of 2 without a portrait, 2 of 3 with */
  grid-column: -3 / -2;
  grid-row: 1;
  /* A grid item blockifies, so inline-flex would stretch the strap across
   * the whole column. It is a tab over the card, not a bar. */
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--u) * 0.55);
  background: var(--team);
  color: #06090d;
  padding: calc(var(--u) * 0.24) calc(var(--u) * 0.85);
  font-weight: 700;
  font-size: calc(var(--u) * 0.98);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: background 600ms ease;
}
.cstv .lower .main {
  /* THE CARD RUNS UNDER THE MARK'S COLUMN, so the logo sits INSIDE the panel
   * on its right rather than floating beside it (user 2026-09-13: "the logo on
   * the right side of the ui panel"). The right padding keeps every line of
   * text clear of it: the mark's 7.4u, its 1.3u inset, and 1.0u of air. */
  grid-column: -3 / -1;
  grid-row: 2;
  background: var(--ground);
  border: 1px solid var(--edge);
  border-left: calc(var(--u) * 0.3) solid var(--team);
  padding: calc(var(--u) * 0.7) calc(var(--u) * 1.3) calc(var(--u) * 0.85);
  padding-right: calc(var(--u) * 9.7);
  transition: border-color 600ms ease;
}
.cstv .lower .who {
  font-weight: 700;
  font-size: calc(var(--u) * 3.1);
  line-height: 1;
  text-wrap: balance;
}
/* A LONG NAME SHRINKS. THE PLATE'S HEIGHT IS LOAD BEARING - the delivery
 * card hangs from a fixed offset below this panel, and a name that wrapped
 * to a second line pushed the panel 3.1u further down than the card's
 * ceiling allows for. Every alternative was worse: reserving the second
 * line permanently cost the card 58px it needs for its foot, and an
 * ellipsis eats somebody's company name to save a layout.
 *
 * 2.4u fits the engine's longest allowed name - 31 characters, about
 * 550px - inside the 690px column, so this cannot wrap either. The
 * threshold is on the NAME, applied in tvoverlay.js. */
.cstv .lower .who.long { font-size: calc(var(--u) * 2.4); }
/* THE PERSON, UNDER THE COMPANY. Big enough to read as a name rather
 * than a caption, and in the team colour so it belongs to the company
 * above it rather than floating between the two.
 * Nowrap: a founder name is whatever somebody typed, and a wrapped one
 * would push the chips and the figures down into the card below. */
/* THE SEASON TITLE, under the CEO line. Smaller than the CEO and in the
   brand gold, so it reads as a decoration on the person rather than as
   another fact about the company. ONE LINE, NEVER WRAPPED: the delivery
   card's ceiling is derived from the plate's height, so this ellipsises
   where the company name shrinks. */
.cstv .lower .ttl {
  font: 600 15px/1.1 ui-monospace, Consolas, monospace;
  letter-spacing: .04em;
  color: #e8c56a;
  margin-top: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
/* A CREST IS A RING ON THE COMPANY MARK. No art and no download: each is a
   border treatment from a closed table of eight, distinct enough to read at
   broadcast size and quiet enough not to fight the mark inside it. */
.cstv .lower .clogo.cr-laurel { box-shadow: 0 0 0 3px #e8c56a, 0 0 0 6px #6b5420; border-radius: 50%; }
.cstv .lower .clogo.cr-wreath { box-shadow: 0 0 0 3px #e8c56a; border-radius: 50%; }
.cstv .lower .clogo.cr-ring { box-shadow: 0 0 0 3px #cfd8e3; border-radius: 50%; }
.cstv .lower .clogo.cr-bar { box-shadow: 0 3px 0 0 #f0a860; }
.cstv .lower .clogo.cr-compass { box-shadow: 0 0 0 2px #7fd4f0; border-radius: 50%; }
.cstv .lower .clogo.cr-anvil { box-shadow: 0 0 0 2px #f0a860; border-radius: 4px; }
.cstv .lower .clogo.cr-beacon { box-shadow: 0 0 0 2px #77d377; border-radius: 50%; }
.cstv .lower .clogo.cr-hourglass { box-shadow: 0 0 0 2px #f087c8; border-radius: 4px; }
.cstv .lower .ceo {
  font-size: calc(var(--u) * 1.25);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--team);
  line-height: 1.15;
  padding-top: calc(var(--u) * 0.18);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 600ms ease;
}
/* The label in front of the name: small, spaced, dim - a caption on the
 * person, not a second name. */
.cstv .lower .ceo .k {
  font-size: 0.68em;
  letter-spacing: 0.16em;
  color: var(--dim);
  margin-right: 0.5em;
  vertical-align: 0.08em;
}
.cstv .lower .meta {
  margin-top: calc(var(--u) * 0.55);
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 0.5);
  flex-wrap: wrap;
}
.cstv .chip {
  display: inline-flex;
  align-items: baseline;
  gap: calc(var(--u) * 0.3);
  border: 1px solid var(--edge);
  padding: calc(var(--u) * 0.2) calc(var(--u) * 0.6);
  font-size: calc(var(--u) * 1.05);
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 1.3;
}
.cstv .chip .k {
  font-size: calc(var(--u) * 0.78);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}
.cstv .chip.tier { border-color: transparent; color: #06090d; font-weight: 700; }
.cstv .chip.human { background: var(--team); color: #06090d; border-color: transparent; }
.cstv .chip.ai { color: var(--dim); }

/* The figures row. Labels small and quiet, numbers large and mono, because a
 * viewer reads the number and only glances at what it is called. Tabular
 * figures matter here more than anywhere: cash ticks while it is on screen and
 * proportional digits would make the whole row shuffle sideways. */
.cstv .figs {
  margin-top: calc(var(--u) * 0.6);
  padding-top: calc(var(--u) * 0.55);
  border-top: 1px solid rgba(126, 150, 180, 0.16);
  display: flex;
  gap: calc(var(--u) * 1.8);
  flex-wrap: wrap;
}
.cstv .fig { display: flex; flex-direction: column; gap: calc(var(--u) * 0.12); }
.cstv .fig .k {
  font-size: calc(var(--u) * 0.75);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 600;
}
.cstv .fig .v { font-size: calc(var(--u) * 1.5); font-weight: 600; line-height: 1; }
.cstv .fig .v i { font-style: normal; color: var(--dim); font-size: 0.62em; }
.cstv .fig .v.up { color: #6ee7a0; }
.cstv .fig .v.down { color: #f8909a; }

/* ---- standings --------------------------------------------------------- */
.cstv .table { min-width: calc(var(--u) * 30); padding: calc(var(--u) * 0.9) 0 calc(var(--u) * 0.5); }
.cstv .table h4 {
  font-weight: 700;
  font-size: calc(var(--u) * 1.05);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0 calc(var(--u) * 1.1) calc(var(--u) * 0.7);
  color: var(--dim);
}
.cstv .row {
  display: grid;
  grid-template-columns: calc(var(--u) * 2.2) calc(var(--u) * 0.45) 1fr auto;
  align-items: center;
  gap: calc(var(--u) * 0.55);
  padding: calc(var(--u) * 0.34) calc(var(--u) * 1.1);
  font-size: calc(var(--u) * 1.28);
  line-height: 1.25;
}
.cstv .row + .row { border-top: 1px solid rgba(126, 150, 180, 0.1); }
.cstv .row .pos { color: var(--dim); font-weight: 600; text-align: right; }
.cstv .row .swatch { width: calc(var(--u) * 0.45); height: calc(var(--u) * 1.1); }
.cstv .row .nm { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cstv .row .pts { font-weight: 600; }
.cstv .row.me { background: rgba(255, 255, 255, 0.06); }
.cstv .row.bot .nm { color: var(--dim); }

/* ---- the vehicle breakdown --------------------------------------------- */
/* Shares the right-hand zone with the standings, which yield to it. Built as
 * a label/value ladder rather than the chip row the lower third uses, because
 * these are six related figures about one thing and a reader scans them down
 * a column. */
.cstv .veh { min-width: calc(var(--u) * 27); padding: calc(var(--u) * 0.9) 0 calc(var(--u) * 0.7); }
.cstv .veh h4 {
  font-weight: 700;
  font-size: calc(var(--u) * 0.95);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--team);
  padding: 0 calc(var(--u) * 1.1);
  transition: color 600ms ease;
}
.cstv .veh .vname {
  font-size: calc(var(--u) * 1.85);
  font-weight: 700;
  line-height: 1.1;
  padding: calc(var(--u) * 0.22) calc(var(--u) * 1.1) calc(var(--u) * 0.6);
}
.cstv .vload { padding: 0 calc(var(--u) * 1.1) calc(var(--u) * 0.7); }
.cstv .vbar {
  height: calc(var(--u) * 0.34);
  background: rgba(255, 255, 255, 0.13);
  position: relative;
  overflow: hidden;
  margin-bottom: calc(var(--u) * 0.28);
}
.cstv .vbar i { position: absolute; inset: 0 auto 0 0; background: var(--team);
                transition: width 700ms ease, background 600ms ease; }
.cstv .vload span { font-size: calc(var(--u) * 0.95); color: var(--dim);
                    letter-spacing: 0.04em; }
.cstv .vfigs { border-top: 1px solid rgba(126, 150, 180, 0.14); }
.cstv .vrow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: calc(var(--u) * 1.2);
  padding: calc(var(--u) * 0.33) calc(var(--u) * 1.1);
}
.cstv .vrow + .vrow { border-top: 1px solid rgba(126, 150, 180, 0.08); }
.cstv .vrow .k { font-size: calc(var(--u) * 0.98); color: var(--dim); font-weight: 600; }
.cstv .vrow .v { font-size: calc(var(--u) * 1.28); font-weight: 600; }
.cstv .vrow .v.up { color: #6ee7a0; }
.cstv .vrow .v.down { color: #f8909a; }

/* ---- the rundown ------------------------------------------------------- */
/* A break is FURNITURE OVER THE LIVE GAME, never a cut to a card: the world
 * keeps running behind it, which is what makes it read as a broadcast rather
 * than a pause. So the zone is inset rather than full-bleed and the board
 * floats in it with the map still visible around the edges. */
.cstv .z-segment { inset: 0; align-items: center; justify-content: center;
                   pointer-events: none; }
.cstv .segwrap { width: 72%; max-height: 84%; display: flex;
                 align-items: center; justify-content: center; }
.cstv .segwrap.hour { width: 84%; }
.cstv .seg { width: 100%; padding: calc(var(--u) * 1.3) 0 calc(var(--u) * 1.1); }
.cstv .seg .sgeye {
  font-size: calc(var(--u) * 0.78);
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--team);
  font-weight: 700;
  padding: 0 calc(var(--u) * 1.6);
  transition: color 600ms ease;
}
.cstv .seg h2 {
  font-size: calc(var(--u) * 2.9);
  font-weight: 700;
  line-height: 1.04;
  padding: calc(var(--u) * 0.24) calc(var(--u) * 1.6) 0;
}
.cstv .seg .sgsub {
  font-size: calc(var(--u) * 1.02);
  color: var(--dim);
  padding: calc(var(--u) * 0.1) calc(var(--u) * 1.6) calc(var(--u) * 0.7);
}

/* one row per company: rank, name, figure, a bar, and the line that makes it
 * a story rather than a number */
.cstv .seg .sgrows { border-top: 1px solid rgba(126, 150, 180, 0.16); }
.cstv .seg .sgrow {
  display: grid;
  grid-template-columns: calc(var(--u) * 2.2) 1fr auto;
  grid-template-areas: "p n v" "p b b" "p d d" "p s s";
  align-items: baseline;
  gap: calc(var(--u) * 0.12) calc(var(--u) * 0.8);
  padding: calc(var(--u) * 0.5) calc(var(--u) * 1.6);
}
.cstv .seg .sgrow + .sgrow { border-top: 1px solid rgba(126, 150, 180, 0.09); }
.cstv .seg .sgp { grid-area: p; font-size: calc(var(--u) * 1.3); color: var(--dim);
                  font-weight: 700; align-self: center; }
.cstv .seg .sgn { grid-area: n; font-size: calc(var(--u) * 1.5); font-weight: 700;
                  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cstv .seg .sgv { grid-area: v; font-size: calc(var(--u) * 1.5); font-weight: 700;
                  color: var(--team); transition: color 600ms ease; }
.cstv .seg .sgbar { grid-area: b; padding-top: calc(var(--u) * 0.12); }
.cstv .seg .sgd { grid-area: d; font-size: calc(var(--u) * 0.86); color: var(--dim); }

/* the hour carries the three boards that produced each rating */
.cstv .seg .sgb { grid-area: s; display: flex; gap: calc(var(--u) * 1.1);
                  padding-top: calc(var(--u) * 0.18); }
.cstv .seg .sgb span { font-size: calc(var(--u) * 0.92); font-weight: 600;
                       font-variant-numeric: tabular-nums; }
.cstv .seg .sgb i { font-style: normal; color: var(--dim); font-weight: 600;
                    font-size: 0.78em; letter-spacing: 0.12em;
                    text-transform: uppercase; margin-right: 0.45em; }

/* ---- the desk ---------------------------------------------------------- */
/* Four seats. The portraits are being drawn; each seat is a monogram plate in
 * that host's colour, in exactly the place the portrait will occupy, so the
 * layout is already right when the art lands. */
/* ---- THE DESK AND ITS CONVERSATION, SIDE BY SIDE ----------------------
 * The faces used to be four cells across the full width of the card, each
 * a quarter of ~1150px and 88px tall - so a PORTRAIT (the art is 3:4 and
 * always was) got cropped to a letterbox band across somebody's face, and
 * the transcript was squeezed underneath with no room. Reported from air.
 *
 * Now the desk is its own column of correctly proportioned frames and the
 * words sit beside it, which is both the right shape for the art and the
 * right shape for a conversation. `auto 1fr` so the desk takes exactly
 * what its seats need and the transcript takes the rest, however many
 * seats there turn out to be. */
.cstv .sgfoot {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: calc(var(--u) * 1.4);
  align-items: start;
  margin-top: calc(var(--u) * 0.7);
  padding: calc(var(--u) * 0.9) calc(var(--u) * 1.6) 0;
  border-top: 1px solid rgba(126, 150, 180, 0.16);
}
/* No transcript yet - the faces then use the whole width rather than
 * huddling in a narrow column beside nothing. */
.cstv .sgfoot.solo { grid-template-columns: 1fr; }
.cstv .sgdesk {
  display: flex;
  gap: calc(var(--u) * 0.7);
}
.cstv .sgdesk .seat {
  text-align: center;
  width: calc(var(--u) * 5.9);
  min-width: 0;
}
.cstv .sgdesk .face {
  position: relative;
  /* 3:4-ish, which is the shape the art actually is. */
  height: calc(var(--u) * 7.6);
  border-radius: 2px;
  background: linear-gradient(160deg,
              color-mix(in srgb, var(--seat) 30%, transparent),
              rgba(10, 14, 20, 0.55));
  border: 1px solid color-mix(in srgb, var(--seat) 45%, transparent);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
/* A PORTRAIT FILLS ITS FRAME and is cropped to the head, the same crop the
 * say card uses - a face that is a third background reads as a snapshot
 * rather than as a broadcast still. */
.cstv .sgdesk .face.art { background: rgba(10, 14, 20, 0.9); }
.cstv .sgdesk .face img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 12%;
  /* DIMMED UNTIL IT IS THEIR TURN. The lift is what makes the lamp read
   * as somebody speaking; without a resting state there is nothing for
   * the highlight to be brighter THAN. */
  filter: grayscale(0.55) brightness(0.62);
  transition: filter 260ms ease;
}
.cstv .sgdesk .seat.on .face img { filter: none; }
/* The lamp itself: the seat's own colour round the frame, so the picture,
 * the name in the transcript and the plate are all one identity. */
.cstv .sgdesk .face .lit {
  position: absolute; inset: 0;
  border: calc(var(--u) * 0.16) solid var(--seat);
  border-radius: 2px;
  opacity: 0;
  transition: opacity 220ms ease;
  pointer-events: none;
}
.cstv .sgdesk .seat.on .face .lit { opacity: 1; }
.cstv .sgdesk .seat.on .who { color: var(--seat); }
/* A TURN NOT YET REACHED IS NOT DRAWN, rather than drawn faintly: the
 * reveal is the point, and a greyed line that is about to appear tells
 * the viewer the end of the conversation before it has happened. */
.cstv .sgtalk .turn.pend { display: none; }
.cstv .sgtalk .turn.on .tt { color: #fff; }
.cstv .sgdesk .face span {
  font-size: calc(var(--u) * 1.5);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--seat);
}
/* TWO LINES, ALWAYS. "Sir Aubrey Vance-Gantry" does not fit a column this
 * wide on one line and "Vic" does not need two - a fixed height keeps the
 * beats underneath on a straight line across the desk either way. */
.cstv .sgdesk .who { font-size: calc(var(--u) * 0.7); font-weight: 700;
                     padding-top: calc(var(--u) * 0.3);
                     line-height: 1.18;
                     height: calc(var(--u) * 1.65);
                     overflow: hidden; }
.cstv .sgdesk .beat { font-size: calc(var(--u) * 0.58); color: var(--dim);
                      letter-spacing: 0.12em; text-transform: uppercase;
                      white-space: nowrap; overflow: hidden;
                      text-overflow: ellipsis; }

/* ---- the delivery window ----------------------------------------------- */
/* LEFT, AND VERTICALLY CENTRED - the mirror of the insert slot. The only
 * other tenants of this side are the name plate at the top and the
 * commentary at the bottom, so the middle is free at every frame size, and
 * a viewer's eye is already on this half when a train is being followed. */
/* A BAND, NOT A HOOK. Hanging from a fixed top and growing downward stopped
 * the card climbing INTO the company panel, which was the previous bug. It
 * said nothing about the other end, and the card has grown twice since - so
 * it grew DOWN into the speakers instead (user 2026-09-09: "no overlap of
 * the speakers by center windows"). Setting top AND bottom makes the
 * browser enforce the clearance instead of an offset I worked out, so the
 * next row added to the card cannot reopen either overlap.
 *
 * THE OLD COMMENT HAD THE RIGHT MEASUREMENT AND THE WRONG ORIGIN. The
 * overlay window is 1920x1128 and tvstream.html insets #frame by ?top=48,
 * so this stylesheet's y=0 is SCREEN y=48 and the bottom 48px hang off the
 * screen unseen. `418px down` was read off a captured frame - a screen
 * number that already carried that inset - and then called 21.8u of overlay
 * space. In the coordinates the rule is actually written in the panel ended
 * at 370px = safe + 16.1u, so 19.5u was leaving a 66px gap, not 18px. The
 * card was never as close to the panel as the comment believed; it was
 * simply 66px lower down the frame than it needed to be, and the speakers
 * paid for it.
 *
 * 15u, DERIVED, AND COUPLED TO A RULE ABOVE: with the crest out of the flow
 * the panel is strap (28px) + card (235px) = 263px = 13.7u below the safe
 * area, measured on a frame. That is now the WHOLE story, because .who.long
 * stops a company name reaching a second line - it used to be able to, and
 * 18u was the price of allowing for it.
 *
 * THE 58px THAT BUYS BACK IS NOT SPARE. The first card to reach this band
 * measured 402px in a 404px band, and only fitted because that delivery had
 * no network multiplier: .pynet renders under `if (p.net)` and costs about
 * 50px, so the next card with one would have lost its foot line to the
 * clip. 15u leaves 25px over the panel and gives the card 462px.
 *
 * 11.6u AT THE FOOT: the say card sits at 0.6u and stands 165px tall - its
 * portrait is 7.4u and sets the height until the caption reaches four
 * lines, at 187px. 10.2u of reserve covers the tallest form with 9px over,
 * and 0.6 + 10.2 is where the floor goes. */
.cstv .z-pay {
  top: calc(var(--safe) + var(--u) * 15);
  bottom: calc(var(--safe) + var(--u) * 10.8);
  left: var(--safe);
  /* .z is a flex container and stretch is its default, so without this the
   * card would grow to fill the whole band instead of hugging its rows. */
  align-items: flex-start;
}
/* DENSER THAN THE FIRST CUT. The card carries up to eleven rows
 * across four blocks now, and the band it lives in - between the
 * name plate at the top left and the commentary at the bottom left
 * - is about two thirds of the frame height. Every size below was
 * cut to fit that, not to taste. */
.cstv .payc {
  width: calc(var(--u) * 25);
  max-width: 32vw;
  padding: calc(var(--u) * 1.0) calc(var(--u) * 1.25);
  /* THE CARD CLIPS ITSELF RATHER THAN CROSSING THE FLOOR. The zone has a
   * definite height now, so 100% is the band. Losing the foot line - the
   * "points, not money" note - is the right thing to lose: the total is at
   * the top, and a caption half-covered by a panel is worse than a panel
   * missing its last row. */
  max-height: 100%;
  overflow: hidden;
}
.cstv .pyeye {
  font-size: calc(var(--u) * 0.72);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--team);
}
.cstv .pywho {
  font-size: calc(var(--u) * 1.5);
  font-weight: 700;
  line-height: 1.1;
  padding-top: calc(var(--u) * 0.15);
}
.cstv .pysub {
  font-size: calc(var(--u) * 0.82);
  color: var(--dim);
  letter-spacing: 0.04em;
}
/* THE TOTAL IS THE PANEL. Everything else on the card is an explanation
 * of it, so it is the only thing on screen at this size. */
.cstv .pytot {
  font-size: calc(var(--u) * 3.5);
  font-weight: 700;
  line-height: 1.02;
  padding-top: calc(var(--u) * 0.5);
  color: #7fe0a0;
}
.cstv .pytl {
  font-size: calc(var(--u) * 0.74);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  padding-bottom: calc(var(--u) * 0.7);
}
.cstv .pybar {
  display: flex;
  height: calc(var(--u) * 0.5);
  border-radius: calc(var(--u) * 0.25);
  overflow: hidden;
  background: rgba(255, 255, 255, 0.07);
  margin-bottom: calc(var(--u) * 0.75);
}
.cstv .pybar i { display: block; height: 100%; }
.cstv .pybar .h { background: #8fb4d9; }
.cstv .pybar .m { background: #7fe0a0; }
.cstv .pybar .b { background: #e0c07a; }
.cstv .pyr {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  column-gap: calc(var(--u) * 0.8);
  padding: calc(var(--u) * 0.16) 0;
}
.cstv .pyr .pk { font-size: calc(var(--u) * 0.88); font-weight: 600; }
.cstv .pyr .pv { font-size: calc(var(--u) * 1.05); font-weight: 700;
                 text-align: right; }
.cstv .pyr .pv.up { color: #7fe0a0; }
.cstv .pyr .pv.down { color: #e8907a; }
.cstv .pyr .pv.big { font-size: calc(var(--u) * 1.5); }
/* THE NOTES CARRY MARKUP NOW (a bolded rate, an escaped company
 * name), so they are no longer plain text - but they are still one
 * line each. A note that wraps turns a four-block card into a
 * five-block one and pushes the year off the bottom of the frame. */
.cstv .pyr .pn b { color: var(--team); font-weight: 700; }
/* THE MULTIPLIER, BESIDE THE LABEL IT BELONGS TO. "Market bonus x1.85" is one
 * fact and reads as one; the same number in the note underneath read as an
 * afterthought, and when the bonus was zero the row vanished entirely and took
 * the rate with it. */
/* THE TWO-COLUMN CARD. Wider, and about a third shorter for it - the
 * height is what was causing the overlap. There is room on this side:
 * the insert slot lives on the right of the frame. */
.cstv .payc.wide { width: calc(var(--u) * 36); max-width: 44vw; }
.cstv .pycols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: calc(var(--u) * 1.3);
  align-items: start;
}
/* The year block heads its own column now, so it needs no rule above
 * it and no gap pushing it down the page. */
.cstv .pycols .pyyr { margin-top: 0; padding-top: 0; border-top: 0; }
.cstv .pycols .pynet { margin-top: calc(var(--u) * 0.6); }
.cstv .pyr .pchip {
  font-style: normal;
  font-family: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
  font-size: calc(var(--u) * 0.72);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--team);
  background: rgba(255, 255, 255, 0.08);
  border-radius: calc(var(--u) * 0.18);
  padding: 0 calc(var(--u) * 0.3);
  margin-left: calc(var(--u) * 0.4);
}
/* A row that earned nothing is still drawn, but quietly - the figure is the
 * word "none" and it must not read as loudly as money that arrived. */
.cstv .pyr .pv.dim { color: var(--dim); font-weight: 600; }
/* The note spans both columns on the next line: a right-aligned figure
 * and its explanation must not fight for the same row. */
.cstv .pyr .pn {
  grid-column: 1 / -1;
  font-size: calc(var(--u) * 0.66);
  line-height: 1.25;
  color: var(--dim);
  letter-spacing: 0.03em;
}
.cstv .pyr.big .pv { font-size: calc(var(--u) * 1.5); }
.cstv .pyyr {
  margin-top: calc(var(--u) * 0.5);
  padding-top: calc(var(--u) * 0.45);
  border-top: 1px solid rgba(126, 150, 180, 0.18);
}
.cstv .pysect {
  font-size: calc(var(--u) * 0.7);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  padding-bottom: calc(var(--u) * 0.2);
}
.cstv .pynet {
  margin-top: calc(var(--u) * 0.7);
  padding-top: calc(var(--u) * 0.55);
  border-top: 1px solid rgba(126, 150, 180, 0.18);
  font-size: calc(var(--u) * 0.78);
  color: var(--dim);
}
.cstv .pynet b { color: #c6a2f0; }

/* ---- the company mark -------------------------------------------------- */
/* Drawn on a canvas by logo.js after insertion. Sized in --u like everything
 * else so it scales with the frame, and it simply stays blank if logo.js has
 * not loaded - the lower third never depends on it. */
/* 7.4u IS THE SAY CARD'S PORTRAIT HEIGHT, deliberately. The crest and
 * the speaker's face are the two pictures in the left rail and they
 * read as a pair at the same size; picking a round number instead
 * would have made them nearly-but-not-quite equal, which looks like a
 * mistake rather than a choice.
 *
 * IT IS ALSO THE LARGEST SIZE THAT STAYS SHARP. logo.js renders into a
 * 128px canvas (renderLogo sets canvas.width itself, so the width= in
 * the markup is not the backing store) - 7.4u is 142px, an upscale of
 * 1.11x. Going to 9u would be 1.35x and the microtype in the badge
 * would soften visibly. 3.4u -> 7.4u is still 4.7x the area. */
.cstv .lower .clogo {
  /* THE MARK CLOSES THE ROW ON THE RIGHT (2026-09-13) - the portrait took the
   * left, which is where a viewer's eye lands first and where the say card
   * below keeps its host. The last column whatever else the plate carries. */
  grid-column: -2 / -1;
  /* in the CARD's row, over the card's right end, centred on it */
  grid-row: 2;
  align-self: center;
  justify-self: end;
  position: relative;
  z-index: 1;
  width: calc(var(--u) * 7.4);
  height: calc(var(--u) * 7.4);
  margin: 0 calc(var(--u) * 1.3) 0 0;
  border-radius: 2px;
}

/* ---- their prices ------------------------------------------------------ */
/* The personal board carries a fourth column the world board does not: the
 * EDGE over the world price, which is the whole reason the panel exists. */
.cstv .mkt2 .mrow { grid-template-columns: calc(var(--u) * 7) 1fr calc(var(--u) * 3.4) calc(var(--u) * 4.2); }
.cstv .mkt2 .pmw { font-size: calc(var(--u) * 0.85); color: #6ee7a0; text-align: right; }

/* ---- the board breakdown ----------------------------------------------- */
/* Why the leader is the leader: the three boards score.nut actually ranks on,
 * under the same head row the plain standings panel uses. */
.cstv .bd .bdrow { padding: calc(var(--u) * 0.42) 0 calc(var(--u) * 0.5); }
.cstv .bd .bdrow + .bdrow { border-top: 1px solid rgba(126, 150, 180, 0.12); }
.cstv .bd .bdhead {
  display: grid;
  grid-template-columns: calc(var(--u) * 1.6) calc(var(--u) * 0.7) 1fr auto;
  align-items: center;
  gap: calc(var(--u) * 0.6);
  padding: 0 calc(var(--u) * 1.1) calc(var(--u) * 0.3);
}
.cstv .bd .bdhead .nm {
  font-weight: 600;
  font-size: calc(var(--u) * 1.15);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cstv .bd .bdrow.me .bdhead .nm { color: var(--team); }
.cstv .bd .bdrow.bot .bdhead .nm { opacity: 0.72; }
.cstv .bd .bdhead .pts { font-size: calc(var(--u) * 1.25); font-weight: 700; }

/* three measures on one baseline, so the SHAPE of a company reads at a glance */
.cstv .bd .bdbars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: calc(var(--u) * 0.7);
  padding: 0 calc(var(--u) * 1.1);
}
.cstv .bd .bdcell { min-width: 0; }
.cstv .bd .bdlab {
  display: block;
  font-size: calc(var(--u) * 0.66);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cstv .bd .bdval {
  display: block;
  font-size: calc(var(--u) * 0.92);
  font-weight: 600;
  color: var(--dim);
  padding-top: calc(var(--u) * 0.08);
}
/* the board this company LEADS - the actual answer to 'why' */
.cstv .bd .bdcell.top .bdlab { color: #6ee7a0; }
.cstv .bd .bdcell.top .bdval { color: #6ee7a0; font-weight: 700; }

.cstv .bd .bdfoot {
  font-size: calc(var(--u) * 0.72);
  color: var(--dim);
  padding: calc(var(--u) * 0.5) calc(var(--u) * 1.1) 0;
  border-top: 1px solid rgba(126, 150, 180, 0.12);
  margin-top: calc(var(--u) * 0.35);
}

/* ---- the transit map ---------------------------------------------------- */
/* The canvas has a fixed backing store and is scaled by CSS, so the diagram
 * is drawn once in logical pixels and reads the same on a 1080p burn-in and
 * in the small web player. */
.cstv .tmapwrap {
  padding: calc(var(--u) * 0.15) calc(var(--u) * 0.7) 0;
}
.cstv .tmap {
  display: block;
  width: 100%;
  height: auto;
}
.cstv .tlegs {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--u) * 0.75);
  padding: calc(var(--u) * 0.35) calc(var(--u) * 1.1) calc(var(--u) * 0.15);
}
.cstv .tleg {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--u) * 0.3);
  font-size: calc(var(--u) * 0.9);
  color: var(--dim);
}
.cstv .tleg i {
  width: calc(var(--u) * 0.85);
  height: calc(var(--u) * 0.28);
  border-radius: calc(var(--u) * 0.14);
}
.cstv .tleg b { color: #dfe7f1; }

/* THE MAP CARD'S FOOTERS. The footer rule above is scoped `.bd .bdfoot`,
 * and a map is an `.insp.card`, not a `.bd` - so the network map's own
 * "Busiest:" line has never had any styling and has been rendering at
 * full card size. Found by putting the card in front of a browser rather
 * than by reading the CSS. Same treatment, scoped to the map card. */
.cstv .tmapcard .bdfoot {
  font-size: calc(var(--u) * 0.72);
  color: var(--dim);
  padding: calc(var(--u) * 0.5) calc(var(--u) * 1.1) 0;
  border-top: 1px solid rgba(126, 150, 180, 0.12);
  margin-top: calc(var(--u) * 0.35);
}

/* THE GUIDE LINE ON THE HEALTH MAP. The legend names the bands; this says
 * what the bands ARE. It sits ABOVE the ordinary footer and carries no top
 * rule of its own, so guide and figure read as one block rather than two
 * separate footers stacked - which is what they looked like at first.
 * Slightly brighter than a normal footer because it is the one line on the
 * card a viewer is meant to actually read rather than glance at. */
.cstv .tmapcard .bdfoot.tguide {
  border-top: none;
  margin-top: 0;
  padding-bottom: calc(var(--u) * 0.1);
  color: #b8c6d8;
  line-height: 1.35;
}

/* THE MAP CARD IS WIDER THAN ITS SIBLINGS, AND ONLY IT. The inspection cards
 * share one frame so the rotation reads as one graphics package, but the
 * transit map is the only one that has to hold a diagram AND two columns of
 * station names beside it - at 30% the names were being written over the
 * lines, which is the fault this width exists to fix. 38% buys the two
 * gutters back without touching the town, industry, brand or market cards.
 *
 * The canvas grew with it (540 -> 760 logical), so a logical pixel is very
 * nearly the physical size it was: the diagram is not being shrunk to make
 * room for the names, the room is new. */
.cstv .insp.tmapcard { width: calc(var(--u) * 38); }

/* The ring legend sits under the flow legend and is deliberately quieter -
 * one question per row. A hollow ring, because that is what it labels. */
.cstv .tlegs.tlegs2 {
  padding-top: 0;
  padding-bottom: calc(var(--u) * 0.3);
}
.cstv .tleg.tring i {
  width: calc(var(--u) * 0.62);
  height: calc(var(--u) * 0.62);
  border-radius: 50%;
  background: none;
  border: calc(var(--u) * 0.16) solid #8fd4ff;
}

/* ---- the desk's face ---------------------------------------------------- */
/* THE PORTRAIT ARRIVES WITH THE LINE. It is inside the say card, so the
 * card's own enter animation and 12.5s hold carry it - there is no second
 * timer that can leave a host's face on screen after somebody else has
 * started talking. */
.cstv .say.haspic {
  display: flex;
  align-items: flex-end;
  gap: calc(var(--u) * 0.9);
}
.cstv .say.haspic .saybody { flex: 1 1 auto; min-width: 0; }
.cstv .hav {
  flex: 0 0 auto;
  width: calc(var(--u) * 5.6);
  height: calc(var(--u) * 7.4);
  border-radius: calc(var(--u) * 0.3);
  overflow: hidden;
  background: rgba(10, 14, 20, 0.9);
  border: 1px solid rgba(126, 150, 180, 0.28);
  /* The seat colour reads down the left edge, the same accent the name
   * already uses, so the face and the voice are visibly the same person. */
  box-shadow: inset calc(var(--u) * 0.18) 0 0 0 var(--sp, #5aa9e6);
  align-self: flex-end;
}
.cstv .hav img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 12%;   /* the face, not the middle of the canvas */
}
/* The placeholder every seat without art draws instead. */
.cstv .hav.gen {
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(160deg, rgba(255, 255, 255, 0.06), rgba(0, 0, 0, 0.25));
}
.cstv .hav.gen span {
  font-family: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
  font-size: calc(var(--u) * 0.92);
  letter-spacing: 0.08em;
  color: var(--sp, #5aa9e6);
  opacity: 0.92;
}

/* The in-game date, under the world name. Quieter than the name and louder
 * than the countdown: the name says which world, this says when. */
.cstv .ident .wdate {
  font-family: Oswald, "Arial Narrow", sans-serif;
  font-size: calc(var(--u) * 1.05);
  letter-spacing: .04em;
  color: var(--ink);
  opacity: .82;
  margin: calc(var(--u) * 0.06) 0 calc(var(--u) * 0.22);
}

/* The one line the desk can read out. Brighter than a footer because it is
 * meant to be read rather than glanced at. */
.cstv .verdict {
  padding: calc(var(--u) * 0.28) calc(var(--u) * 1.1) calc(var(--u) * 0.1);
  font-size: calc(var(--u) * 0.95);
  color: #b8c6d8;
}
.cstv .verdict b { color: #dfe7f1; }

/* ---- the player, and the board they built -------------------------------- */
/* The board takes the larger half: it is the thing a viewer has never seen and
 * the reason this segment exists. The career column is the caption for it. */
.cstv .qplay {
  display: grid;
  grid-template-columns: 1fr 1.25fr;
  gap: calc(var(--u) * 1.4);
  align-items: center;
  min-height: calc(var(--u) * 17);
}
.cstv .qpname {
  font-size: calc(var(--u) * 2.6); line-height: 1.05; color: #f2f6fb;
  font-weight: 600;
}
.cstv .qpcls {
  display: inline-block; margin: calc(var(--u) * 0.35) 0 0;
  font-size: calc(var(--u) * 0.8); letter-spacing: 0.18em;
  text-transform: uppercase; color: #0d1219; background: var(--pc);
  padding: calc(var(--u) * 0.1) calc(var(--u) * 0.6); border-radius: 2px;
}
.cstv .qpfacts {
  margin-top: calc(var(--u) * 0.7);
  font-size: calc(var(--u) * 1.05); color: #b8c6d8;
}
.cstv .qpspent {
  margin-top: calc(var(--u) * 0.3);
  font-size: calc(var(--u) * 1.05); color: #8fa2b8;
}
.cstv .qpspent b { color: #f2f6fb; }
.cstv .qpth {
  margin-top: calc(var(--u) * 0.8);
  display: flex; flex-wrap: wrap; gap: calc(var(--u) * 0.5);
}
.cstv .qpth .tth {
  font-size: calc(var(--u) * 0.9); color: #b8c6d8;
  display: inline-flex; align-items: center; gap: calc(var(--u) * 0.3);
}
.cstv .qpth .tth i {
  width: calc(var(--u) * 0.42); height: calc(var(--u) * 0.42);
  border-radius: 50%; display: inline-block;
}
.cstv .qpnot {
  margin-top: calc(var(--u) * 0.7);
  display: flex; flex-wrap: wrap; gap: calc(var(--u) * 0.4);
}
.cstv .qpnot span {
  font-size: calc(var(--u) * 0.85); color: #dfe7f1;
  border: 1px solid rgba(126, 150, 180, 0.3);
  padding: calc(var(--u) * 0.1) calc(var(--u) * 0.5); border-radius: 2px;
}
.cstv .qpboard { display: flex; align-items: center; justify-content: center; }
.cstv .qpboard .tboard {
  width: 100%; max-height: calc(var(--u) * 17);
}
.cstv .qpwait { color: #6d7f95; font-size: calc(var(--u) * 0.95); }

/* ---- the form book ------------------------------------------------------- */
/* Two bars in one track: the career average behind, tonight in front. The
 * comparison has to be a LENGTH - a viewer at a glance cannot subtract. */
.cstv .fcrow.qform .fcbar.qtwo { position: relative; }
.cstv .fcrow.qform .qavg {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: rgba(143, 162, 184, 0.35);
  border-radius: 2px;
}
.cstv .fcrow.qform .qnow {
  position: absolute; left: 0; top: 25%; bottom: 25%;
  background: var(--sp, #7fd8e8);
  border-radius: 2px;
}
.cstv .fcrow.qform .fcv.up { color: #4fd1a5; }
.cstv .fcrow.qform .fcv.down { color: #f2b134; }
.cstv .fcrow.qform .qwho {
  display: block; font-style: normal;
  font-size: calc(var(--u) * 0.7);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: #7e96b4;
}

/* ---- the record ---------------------------------------------------------- */
/* ONE ROW, not a table. A record is a single number and a list of them is
 * something nobody reads at a glance. */
.cstv .qrec { text-align: center; padding: calc(var(--u) * 0.6) 0; }
.cstv .qrec .qrl {
  font-size: calc(var(--u) * 0.8); letter-spacing: 0.16em;
  text-transform: uppercase; color: #7e96b4;
}
.cstv .qrec .qrv {
  font-size: calc(var(--u) * 4.2); line-height: 1.05; color: #f2f6fb;
  font-weight: 600;
}
.cstv .qrec .qrh { font-size: calc(var(--u) * 1.05); color: #b8c6d8; }
.cstv .qrec .qrbar {
  height: calc(var(--u) * 0.6); margin: calc(var(--u) * 0.9) 12% 0;
  background: rgba(126, 150, 180, 0.18); border-radius: 3px; overflow: hidden;
}
.cstv .qrec .qrbar i {
  display: block; height: 100%; background: #7fd8e8; border-radius: 3px;
}
.cstv .qrec.beaten .qrbar i { background: #4fd1a5; }
.cstv .qrec .qrn {
  margin-top: calc(var(--u) * 0.5);
  font-size: calc(var(--u) * 1.1); color: #dfe7f1;
}

/* ---- the turn before the last one ---------------------------------------- */
/* THE SETUP LINE IS GONE, and with it the rules that styled it. A beat can
 * produce two turns and only the last was drawn, so one was thrown away - and
 * the fix was to squeeze the first in above the second as small grey text, on
 * the SECOND speaker's card, under the SECOND speaker's portrait. Reported
 * from air, and rightly: every host talks like anybody else now, and the
 * director paces the two turns instead (main._set_say, SAY_TURN_S).
 *
 * The rules come out with the markup on purpose. A style with nothing left to
 * style is an invitation to bring the markup back. */

/* ---- the price matrix ---------------------------------------------------- */
/* Six columns rather than a bar chart, because the question changed: it was
 * "how good is this rate" and it is now "which vehicle should they be using".
 * A bar answers the first and cannot answer the second. */
.cstv .pmgrid .pmrow {
  display: grid;
  grid-template-columns: 1fr repeat(4, auto);
  align-items: baseline;
  gap: 0 calc(var(--u) * 0.5);
  padding: calc(var(--u) * 0.14) 0;
}
.cstv .pmgrid .mn {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cstv .pmgrid .pmc {
  min-width: calc(var(--u) * 2.5);
  text-align: right;
  font-size: calc(var(--u) * 0.92);
  color: #8fa2b8;
}
.cstv .pmgrid .pmc.none { color: #4a5768; }
/* The lane that pays this cargo best. One bright cell per row is the entire
 * readout - a viewer's eye finds the column before they read a number. */
.cstv .pmgrid .pmc.best { color: #f2f6fb; font-weight: 600; }
.cstv .pmgrid .pmc.best.warm { color: #f2b134; }
.cstv .pmgrid .pmc.best.hot { color: #ff6b6b; }
/* The header, and the marker for the lane they actually run on. */
.cstv .pmgrid .pmhead span {
  font-size: calc(var(--u) * 0.72);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #6d7f95;
  padding-bottom: calc(var(--u) * 0.12);
}
.cstv .pmgrid .pmhead .pmc.own {
  color: #b8c6d8;
  border-bottom: 2px solid rgba(184, 198, 216, 0.55);
}

/* ---- who holds it, as a ring ------------------------------------------- */
/* The ring answers "contest or walkover" before a viewer has read a row. It
 * sits beside the list rather than above it so the card stays short enough to
 * share the slot with the town's own panel. */
.cstv .stakewrap {
  display: flex; align-items: center;
  gap: calc(var(--u) * 0.9);
  padding: calc(var(--u) * 0.2) calc(var(--u) * 1.1) 0;
}
.cstv .stakelist { flex: 1 1 auto; min-width: 0; }
.cstv .stakelist .hold { padding-left: 0; padding-right: 0; }
.cstv .donut { flex: 0 0 auto; transform: rotate(-90deg); }
.cstv .donut .dbg { fill: none; stroke: rgba(126, 150, 180, 0.16); }
.cstv .donut .dseg { fill: none; }
/* The swatch that ties a row to its segment. Without it the ring is a
 * decoration rather than a key. */
.cstv .hrow .hsw {
  display: inline-block; width: calc(var(--u) * 0.42);
  height: calc(var(--u) * 0.42); border-radius: 50%;
  margin-right: calc(var(--u) * 0.34);
  vertical-align: baseline;
}

/* The 100% rule on the town chart. A growth line without it is a wiggle;
 * with it a viewer sees the town cross from starving into thriving. */
.cstv .chart .crule {
  stroke: rgba(195, 217, 79, 0.35);
  stroke-width: 1;
  stroke-dasharray: 2 3;
}

/* ---- the match chart --------------------------------------------------- */
/* One industry, every month of the running session. The card used to show a
 * single figure; this shows whether that figure is the top of a climb or the
 * bottom of a collapse, which is the only version a commentator can use. */
.cstv .chart { padding: calc(var(--u) * 0.35) calc(var(--u) * 1.1) 0; }
.cstv .chart svg { display: block; overflow: visible; }
/* Production: filled, in the team accent, because it is the headline. */
.cstv .chart .ca { fill: var(--team); opacity: 0.16; stroke: none; }
.cstv .chart .cl { fill: none; stroke: var(--team); stroke-width: 1.6;
                   stroke-linejoin: round; stroke-linecap: round; }
/* Moved: a thin dashed line on its own true 0-100 scale, deliberately
 * quieter - it is the qualifier, not the story. */
.cstv .chart .cm { fill: none; stroke: #c3d94f; stroke-width: 1.2;
                   stroke-dasharray: 3 2.5; opacity: 0.85;
                   stroke-linejoin: round; }
.cstv .clab {
  display: flex; gap: calc(var(--u) * 0.9); align-items: baseline;
  font-size: calc(var(--u) * 0.72); color: var(--dim);
  padding: calc(var(--u) * 0.25) 0 0;
}
.cstv .clab b { color: #dfe7f1; margin: 0 calc(var(--u) * 0.22); }
.cstv .clab i { display: inline-block; width: calc(var(--u) * 0.7);
                height: calc(var(--u) * 0.2); border-radius: 1px;
                margin-right: calc(var(--u) * 0.28); }
.cstv .clab i.k1 { background: var(--team); }
.cstv .clab i.k2 { background: #c3d94f; }
.cstv .clab .cmo { margin-left: auto; color: var(--faint, #6d7b8b); }

/* ---- inspections ------------------------------------------------------- */
/* One card shape for the town, industry, stakeholder, brand and market
 * panels. They rotate through the same slot, so sharing the frame is what
 * makes the rotation read as one graphics package rather than five unrelated
 * boxes taking turns. */
.cstv .insp { width: calc(var(--u) * 30); padding: calc(var(--u) * 0.9) 0 calc(var(--u) * 0.8); }
.cstv .insp h4 {
  font-weight: 700;
  font-size: calc(var(--u) * 0.95);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--team);
  padding: 0 calc(var(--u) * 1.1);
  transition: color 600ms ease;
}
.cstv .insp .iname {
  font-size: calc(var(--u) * 1.95);
  font-weight: 700;
  line-height: 1.08;
  padding: calc(var(--u) * 0.24) calc(var(--u) * 1.1) 0;
}
.cstv .insp .isub {
  font-size: calc(var(--u) * 0.98);
  color: var(--dim);
  padding: calc(var(--u) * 0.12) calc(var(--u) * 1.1) calc(var(--u) * 0.6);
}
.cstv .insp .sect {
  font-size: calc(var(--u) * 0.8);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 600;
  padding: calc(var(--u) * 0.55) calc(var(--u) * 1.1) calc(var(--u) * 0.3);
  border-top: 1px solid rgba(126, 150, 180, 0.14);
  margin-top: calc(var(--u) * 0.3);
}
.cstv .insp .big {
  font-size: calc(var(--u) * 3.2);
  font-weight: 600;
  line-height: 1;
  padding: calc(var(--u) * 0.3) calc(var(--u) * 1.1) 0;
}
.cstv .insp .big.up { color: #6ee7a0; }
.cstv .insp .biglab {
  font-size: calc(var(--u) * 0.88);
  color: var(--dim);
  padding: 0 calc(var(--u) * 1.1) calc(var(--u) * 0.55);
}

/* label / value ladder, shared */
.cstv .rws { border-top: 1px solid rgba(126, 150, 180, 0.14); }
.cstv .rw {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: calc(var(--u) * 1);
  padding: calc(var(--u) * 0.3) calc(var(--u) * 1.1);
}
.cstv .rw + .rw { border-top: 1px solid rgba(126, 150, 180, 0.08); }
.cstv .rw .k { font-size: calc(var(--u) * 0.95); color: var(--dim); font-weight: 600; }
.cstv .rw .v { font-size: calc(var(--u) * 1.2); font-weight: 600; }
.cstv .rw .v.up { color: #6ee7a0; }
.cstv .rw .v.down { color: #f8909a; }

/* a measure bar, shared by needs, shares, awareness and the market board */
.cstv .mbar {
  height: calc(var(--u) * 0.3);
  background: rgba(255, 255, 255, 0.12);
  position: relative;
  overflow: hidden;
  margin: calc(var(--u) * 0.1) 0;
}
.cstv .mbar i {
  position: absolute; inset: 0 auto 0 0;
  background: var(--team);
  transition: width 700ms ease, background 600ms ease;
}
.cstv .mbar.ok i    { background: #4ade80; }
.cstv .mbar.short i { background: #fbbf24; }
.cstv .mbar.warm i  { background: #fbbf24; }
.cstv .mbar.hot i   { background: #fb7185; }

/* stakeholders: name, share bar, holding, and what it paid */
.cstv .hold { border-top: 1px solid rgba(126, 150, 180, 0.14); }
.cstv .hrow {
  display: grid;
  grid-template-columns: 1fr calc(var(--u) * 6) auto;
  grid-template-areas: "n b p" "n b m";
  align-items: center;
  gap: 0 calc(var(--u) * 0.7);
  padding: calc(var(--u) * 0.42) calc(var(--u) * 1.1);
}
.cstv .hrow + .hrow { border-top: 1px solid rgba(126, 150, 180, 0.08); }
.cstv .hname { grid-area: n; font-weight: 600; font-size: calc(var(--u) * 1.15);
               white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cstv .hbar  { grid-area: b; }
.cstv .hpts  { grid-area: p; font-size: calc(var(--u) * 1.05); font-weight: 600;
               text-align: right; }
.cstv .hpts i { font-style: normal; color: var(--dim); font-size: 0.72em; }
.cstv .hpaid { grid-area: m; font-size: calc(var(--u) * 0.9); color: #6ee7a0;
               text-align: right; }

/* town needs */
.cstv .nrow {
  display: grid;
  grid-template-columns: calc(var(--u) * 7) 1fr auto;
  align-items: center;
  gap: calc(var(--u) * 0.6);
  padding: calc(var(--u) * 0.26) calc(var(--u) * 1.1);
}
.cstv .nrow .nk { font-size: calc(var(--u) * 0.95); font-weight: 600;
                  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cstv .nrow .nv { font-size: calc(var(--u) * 0.95); color: var(--dim); }

/* industry cargo table */
.cstv .cgs { border-top: 1px solid rgba(126, 150, 180, 0.14); }
.cstv .chd, .cstv .crow {
  display: grid;
  grid-template-columns: 1fr auto calc(var(--u) * 4) calc(var(--u) * 3);
  gap: calc(var(--u) * 0.5);
  align-items: baseline;
  padding: calc(var(--u) * 0.26) calc(var(--u) * 1.1);
}
.cstv .chd {
  font-size: calc(var(--u) * 0.72);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 600;
}
.cstv .chd span:not(:first-child), .cstv .crow .cp,
.cstv .crow .ct, .cstv .crow .fx { text-align: right; }
.cstv .crow { font-size: calc(var(--u) * 1.05); font-weight: 600; }
.cstv .crow .cn { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cstv .crow .fx { color: var(--dim); }
.cstv .crow .fx.up { color: #6ee7a0; }
.cstv .crow .ct { color: var(--dim); }

/* market board */
.cstv .mkt { border-top: 1px solid rgba(126, 150, 180, 0.14); }
.cstv .mrow {
  display: grid;
  grid-template-columns: calc(var(--u) * 8) 1fr calc(var(--u) * 3.4);
  align-items: center;
  gap: calc(var(--u) * 0.6);
  padding: calc(var(--u) * 0.28) calc(var(--u) * 1.1);
}
.cstv .mrow .mn { font-size: calc(var(--u) * 1) ; font-weight: 600;
                  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cstv .mrow .mv { font-size: calc(var(--u) * 1.1); font-weight: 600; text-align: right; }
.cstv .mrow .mv.warm { color: #fbbf24; }
.cstv .mrow .mv.hot  { color: #fb7185; }

/* ---- the gallery wipe --------------------------------------------------- */
/* A sports gallery does not cut between graphics. This runs for a third of a
 * second on a CHANGE only - unlike the idle animation that cost a whole core
 * in software rendering, a one-off transition is free. */
.cstv .swipe {
  position: absolute;
  top: 0; bottom: 0; right: 0;
  width: calc(var(--u) * 32);
  background: linear-gradient(90deg, transparent 0%, var(--team) 22%,
                              var(--team) 78%, transparent 100%);
  opacity: 0.85;
  pointer-events: none;
  animation: cstv-swipe 620ms cubic-bezier(.5,0,.2,1) both;
}
@keyframes cstv-swipe {
  0%   { transform: translateX(110%); opacity: 0; }
  35%  { opacity: 0.85; }
  100% { transform: translateX(-115%); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .cstv .swipe { display: none; } }

/* ---- commentary -------------------------------------------------------- */
.cstv .say {
  width: 100%;
  padding: calc(var(--u) * 0.6) calc(var(--u) * 1.1);
  border-left: calc(var(--u) * 0.24) solid var(--sp);
  text-align: left;
}
.cstv .say .nm {
  font-weight: 700;
  font-size: calc(var(--u) * 0.92);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sp);
}
.cstv .say .tx {
  margin-top: calc(var(--u) * 0.22);
  font-size: calc(var(--u) * 1.4);
  line-height: 1.32;
  font-weight: 500;
}
/* a statement (take plus case, director/case.py) drops a size so four
 * or five wrapped lines still sit inside the frame */
.cstv .say .tx.long {
  font-size: calc(var(--u) * 1.12);
  line-height: 1.3;
}
/* ---- the paired cards ------------------------------------------------- */
/* The right-hand card is the left one mirrored: portrait on the outside
 * edge, words growing inward, accent on the outer border. */
.cstv .say.r { border-left: 0; border-right: calc(var(--u) * 0.24) solid var(--sp);
               text-align: right; }
.cstv .say.r.haspic { flex-direction: row-reverse; }
/* RESTING: the host is on the beat but not speaking. The card shrinks to
 * the face and the name and dims, exactly as a desk seat does between
 * turns - so lighting up is a change a viewer can see. The words are not
 * drawn faintly, they are gone: a greyed line is a line being read. */
/* A SLEEPING PORTRAIT, FLOATING (user 2026-09-10: "when you minimise their
 * portrait remove their name and the whole dark text area let it just be a
 * sleeping portrait floating"). Resting, the card is nothing but the face:
 * no ground, no edge, no blur, no name plate - the dimmed portrait alone in
 * its corner, so a host lighting up is the card appearing around them. */
/* THE FACE IS FURNITURE; ONLY THE CARD COMES AND GOES (user 2026-09-10:
 * "when they talk they become unaligned horizontally"). The resting card
 * KEEPS the talking card's whole geometry - the 1px card edge, the 0.24u
 * accent and the 0.6u / 1.1u padding - and makes it transparent instead of
 * removing it, so the portrait sits on the same pixels awake and asleep.
 * `border: 0; padding: 0` dropped the sleeping face 0.6u + 1px lower and
 * 1.34u further into its corner than its talking self, and the pair drifted
 * apart every time the turn changed. border-color (all four sides) wins
 * the seat-colour accent of .say and .say.r because this rule comes later
 * at equal specificity - the gate pins the order. */
.cstv .say.rest { width: auto; background: none; border-color: transparent;
                  -webkit-backdrop-filter: none; backdrop-filter: none; }
/* AND THE FLAT GROUND MUST NOT REACH IT: the burn-in host is `flat`, and
 * `.cstv.flat .card` further down repaints the card ground at equal
 * specificity, later in the sheet - measured on air 2026-09-10 as a dark
 * box round the sleeping face once its padding came back. Four classes
 * beat three, whatever the order. */
.cstv.flat .say.rest { background: none; }
.cstv .say.rest .saybody { display: none; }
.cstv .say.rest .tx { display: none; }
/* THE FRAME STAYS AND DARKENS WITH THE FACE (user 2026-09-10: "the one that
 * sleeps loses its nice rounded corners and border ... so the border stays
 * just darkens like the image does"): the SAME full ring and radius as when
 * talking, and the dimming filter on the FRAME rather than on the image
 * inside it, so ring, edge and face darken together by the same 0.62. The
 * first version rang the sleeping face at 45% strength and dimmed only the
 * picture: a 2px ring at under half alpha did not survive the encoder, and
 * a dark portrait with no visible ring reads as square. The inset accent
 * goes, because that is the part that reads as lit. */
.cstv .say.rest .hav {
  box-shadow: 0 0 0 2px var(--sp, #5aa9e6);
  border-radius: calc(var(--u) * 0.45);
  filter: grayscale(0.55) brightness(0.62);
}
.cstv .say.talk .hav { border-radius: calc(var(--u) * 0.45); }
.cstv .say.talk .hav { box-shadow: inset calc(var(--u) * 0.18) 0 0 0 var(--sp, #5aa9e6),
                                   0 0 0 2px var(--sp, #5aa9e6); }
.cstv .say.r.talk .hav { box-shadow: inset calc(var(--u) * -0.18) 0 0 0 var(--sp, #5aa9e6),
                                     0 0 0 2px var(--sp, #5aa9e6); }
.cstv .say .hav { transition: filter 260ms ease; }

/* ---- break package ----------------------------------------------------- */
.cstv .break {
  width: 100%;
  height: 100%;
  background: linear-gradient(160deg, #0a0f16 0%, #070a0e 62%, #0d0a12 100%);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 calc(var(--u) * 8);
}
.cstv .break .eyebrow {
  font-weight: 700;
  font-size: calc(var(--u) * 1.15);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--team);
}
.cstv .break h2 {
  margin-top: calc(var(--u) * 0.7);
  font-weight: 700;
  font-size: calc(var(--u) * 4.6);
  line-height: 0.98;
  text-wrap: balance;
}
.cstv .break .sub {
  margin-top: calc(var(--u) * 0.5);
  font-size: calc(var(--u) * 1.5);
  color: var(--dim);
}
.cstv .break .facts {
  margin-top: calc(var(--u) * 2.6);
  display: flex;
  gap: calc(var(--u) * 4);
  flex-wrap: wrap;
}
.cstv .break .fact .k {
  font-weight: 600;
  font-size: calc(var(--u) * 1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}
.cstv .break .fact .v {
  margin-top: calc(var(--u) * 0.3);
  font-size: calc(var(--u) * 3.4);
  font-weight: 600;
  line-height: 1;
}
.cstv .break .fact .s {
  margin-top: calc(var(--u) * 0.25);
  font-size: calc(var(--u) * 1.05);
  color: var(--dim);
}
.cstv .break .rule {
  margin-top: calc(var(--u) * 2.2);
  height: calc(var(--u) * 0.22);
  width: calc(var(--u) * 12);
  background: var(--team);
}

/* ---- flat: the burn-in variant ------------------------------------------
 * MEASURED, NOT GUESSED. On the stream box there is no GPU, so backdrop-filter
 * blurs a 1920-wide region in software behind every card, and the pulsing LIVE
 * dot forces the whole overlay to recomposite continuously. Together they cost
 * a FULL CORE - WebKit measured at 104% - which took the box to 2.5 of 3 cores
 * and put the CPU budget out of reach. Both are free on the web player, where a
 * real GPU composites them, so this is a variant rather than a deletion.
 *
 * The ground goes more opaque to make up for the lost blur. Over pixel art at
 * broadcast size the difference is close to invisible; the core is not.
 */
.cstv.flat .card {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: rgba(8, 12, 17, 0.93);
}
.cstv.flat .lower .main { background: rgba(8, 12, 17, 0.93); }
.cstv.flat .state .dot { animation: none; }

/* ---- entrances --------------------------------------------------------- */
.cstv .in-l { animation: cstv-in-l 520ms cubic-bezier(.16,.84,.34,1) both; }
.cstv .in-r { animation: cstv-in-r 520ms cubic-bezier(.16,.84,.34,1) both; }
.cstv .in-u { animation: cstv-in-u 460ms cubic-bezier(.16,.84,.34,1) both; }
.cstv .in-f { animation: cstv-in-f 620ms ease both; }
.cstv .out  { animation: cstv-out 340ms ease both; }

@keyframes cstv-in-l { from { opacity: 0; transform: translateX(calc(var(--u) * -2.2)); } }
@keyframes cstv-in-r { from { opacity: 0; transform: translateX(calc(var(--u) * 2.2)); } }
@keyframes cstv-in-u { from { opacity: 0; transform: translateY(calc(var(--u) * 1.4)); } }
@keyframes cstv-in-f { from { opacity: 0; } }
@keyframes cstv-out  { to   { opacity: 0; transform: translateY(calc(var(--u) * 0.5)); } }

@media (prefers-reduced-motion: reduce) {
  .cstv .in-l, .cstv .in-r, .cstv .in-u, .cstv .in-f, .cstv .out { animation-duration: 1ms; }
  .cstv .state .dot { animation: none; }
}

/* The hourly exchange. Plain type on a plain ground - it sits under a table
   that is already busy, and the only colour is the speaker's own seat. */
/* A COLUMN, NOT A FOOTER. The border and the top margin belonged to the
 * old full-width block; .sgfoot carries them now. */
.sgtalk{margin:0;padding:0;min-width:0}
/* SIZED IN --u LIKE EVERYTHING ELSE. These were fixed pixels from when the
 * transcript was a squeezed footer under four wide boxes; it is the segment's
 * main reading surface now, and fixed px does not scale from a web player to
 * a 1920 broadcast - which is the whole reason --u exists. */
.sgtalk .turn{display:flex;gap:calc(var(--u) * 0.55);align-items:baseline;
  margin:0 0 calc(var(--u) * 0.42)}
.sgtalk .turn:last-child{margin-bottom:0}
.sgtalk .tw{flex:0 0 auto;min-width:calc(var(--u) * 7.2);
  font:600 calc(var(--u) * 0.62)/1.4 'IBM Plex Mono',monospace;
  letter-spacing:.12em;text-transform:uppercase;color:var(--t,#9fb0c4)}
.sgtalk .tt{flex:1 1 auto;
  font:500 calc(var(--u) * 1.02)/1.35 'Barlow Condensed',sans-serif;
  color:#e9eff6;letter-spacing:.01em}

/* ---- the cutaway ------------------------------------------------------- */
/* A GRAPHIC OVER GAMEPLAY IS A CAPTION. Every other panel here sits beside the
 * match and lets the eye stay on the trains, which is right for a lower third
 * and wrong for a breakdown: the numbers get glanced at and the segment reads
 * as decoration. So the show stops. This zone is OPAQUE and full bleed, the
 * overlay is burnt into the video, and between them that is the only way this
 * channel can cut away from the picture at all.
 *
 * The director owns when. It flags a window on the segment itself and this
 * simply renders while the flag is up - the clock belongs to the show, not to
 * whichever panel happens to be mounted.
 *
 * NO BLUR AND NO IDLE ANIMATION, deliberately. This composites in software
 * beside a video encoder on three cores, where backdrop-filter and one pulsing
 * dot were measured at a whole core between them. The only motion is the
 * entrance, and the bars are plain divs with a width. */
/* AN EMPTY ZONE MUST NOT PAINT, and this one is the only zone that could.
 * Every zone div is created at mount and lives for the session, so a zone
 * with inset:0 AND a background is an opaque sheet over the whole frame
 * whenever no panel happens to be in it - which is almost always. Shipped
 * that way for a few minutes and the burn-in went black but for the game's
 * own toolbar, because the sheet is the overlay and the toolbar is not.
 * The other inset zones (break, segment) never hit this: they carry no
 * background of their own and paint nothing when empty. */
.cstv .z-full:empty { display: none; }
.cstv .z-full {
  inset: 0;
  align-items: center;
  justify-content: center;
  background: #070b10;
  pointer-events: none;
}
.cstv .fchart {
  width: 78%;
  max-height: 88%;
  display: flex;
  flex-direction: column;
  /* The team colour rides the top edge, so a cutaway still says whose story
   * this is even with no company name on the screen. */
  border-top: calc(var(--u) * 0.3) solid var(--team);
  padding: calc(var(--u) * 1.4) 0 calc(var(--u) * 0.9);
  transition: border-color 600ms ease;
}
.cstv .fchart .fceye {
  font-size: calc(var(--u) * 0.9);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--team);
  font-weight: 700;
  transition: color 600ms ease;
}
.cstv .fchart h2 {
  font-size: calc(var(--u) * 4.2);
  font-weight: 700;
  line-height: 1.02;
  padding-top: calc(var(--u) * 0.2);
}
.cstv .fchart .fcsub {
  font-size: calc(var(--u) * 1.35);
  color: var(--dim);
  padding-bottom: calc(var(--u) * 0.9);
}
/* THE DESK ACROSS THE FOOT OF A CHART: the seats (the same .sgdesk rules
 * the hour card uses, lamp and all) and the line on air beside them. */
.cstv .fcdesk {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: calc(var(--u) * 1.4);
  align-items: center;
  margin-top: calc(var(--u) * 0.9);
  padding-top: calc(var(--u) * 0.8);
  border-top: 1px solid rgba(126, 150, 180, 0.16);
}
/* 5.4u / 6.8u, from 4.6 / 5.9 (frame looked at 02:59 UTC): at the narrower
 * size a two-word host name clipped on its second line under the seat. */
.cstv .fcdesk .sgdesk .seat { width: calc(var(--u) * 5.4); }
.cstv .fcdesk .sgdesk .face { height: calc(var(--u) * 6.8); }
/* the feature's title and end cards (tvoverlay.js featureCard) */
.cstv .fchart.fcard { display: flex; flex-direction: column; justify-content: center;
  align-items: flex-start; }
.cstv .fchart.fcard h2 { font-size: calc(var(--u) * 7); line-height: 1.02; margin: calc(var(--u) * 0.6) 0; }
.cstv .fchart.fcard .fcsub { font-size: calc(var(--u) * 2.6); font-weight: 600; color: #fff; }
.cstv .fchart.fcard .fcsub2 { font-size: calc(var(--u) * 1.5); margin-top: calc(var(--u) * 0.5);
  color: #b9c3cf; letter-spacing: 0.06em; }
.cstv .fchart.fcard.fend h2 { font-size: calc(var(--u) * 6); }
.cstv .fcsay { min-width: 0; }
.cstv .fcsay .nm {
  font-weight: 700;
  font-size: calc(var(--u) * 0.92);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sp, var(--dim));
}
.cstv .fcsay .tt {
  margin-top: calc(var(--u) * 0.2);
  font-size: calc(var(--u) * 1.6);
  line-height: 1.3;
  font-weight: 500;
}
.cstv .fcsay .tt.long {
  font-size: calc(var(--u) * 1.22);
  line-height: 1.28;
}
.cstv .fchart .fcrows {
  display: flex;
  flex-direction: column;
  gap: calc(var(--u) * 0.62);
}
/* One row is a four column grid so rank, name and figure share a baseline and
 * the bar spans beneath them. Laid out by grid rather than by margins: these
 * repeat, and a repeated thing whose edges drift is the tell that it was
 * spaced by hand. */
.cstv .fchart .fcrow {
  display: grid;
  grid-template-columns: calc(var(--u) * 2.2) 1fr auto;
  grid-template-areas: "p n v" ". b b" ". d d";
  align-items: baseline;
  column-gap: calc(var(--u) * 0.7);
  padding-bottom: calc(var(--u) * 0.2);
  border-bottom: 1px solid var(--edge);
}
.cstv .fchart .fcp { grid-area: p; color: var(--dim); font-size: calc(var(--u) * 1.3); }
.cstv .fchart .fcn { grid-area: n; font-size: calc(var(--u) * 1.7); font-weight: 600; }
.cstv .fchart .fcv { grid-area: v; font-size: calc(var(--u) * 1.7); font-weight: 700; }
.cstv .fchart .fcd {
  grid-area: d;
  font-size: calc(var(--u) * 0.92);
  color: var(--dim);
  padding-bottom: calc(var(--u) * 0.24);
}
.cstv .fchart .fcbar {
  grid-area: b;
  height: calc(var(--u) * 0.42);
  background: rgba(126, 150, 180, 0.16);
  margin: calc(var(--u) * 0.22) 0 calc(var(--u) * 0.16);
}
.cstv .fchart .fcbar > i {
  display: block;
  height: 100%;
  background: var(--team);
  transition: width 700ms cubic-bezier(0.2, 0.8, 0.2, 1), background 600ms ease;
}
/* The leader is the story. It gets the size, and everything under it is read
 * against it rather than competing with it. */
.cstv .fchart .fcrow.lead .fcn,
.cstv .fchart .fcrow.lead .fcv { font-size: calc(var(--u) * 2.3); }
.cstv .fchart .fcrow.lead .fcbar { height: calc(var(--u) * 0.62); }
.cstv .fchart .fcfoot {
  font-size: calc(var(--u) * 0.82);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--dim);
  padding-top: calc(var(--u) * 0.8);
}

/* #42 the industry card's output as one bar, with its key */
.cstv .pbar { display: flex; height: calc(var(--u) * 0.6); background: rgba(255, 255, 255, 0.06);
              margin: calc(var(--u) * 0.2) calc(var(--u) * 1.1) 0; }
.cstv .pbar i { display: block; height: 100%; }
.cstv .pkey { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 0.15) calc(var(--u) * 0.7);
              padding: calc(var(--u) * 0.25) calc(var(--u) * 1.1) calc(var(--u) * 0.1);
              font-size: calc(var(--u) * 0.72); color: var(--dim); }
.cstv .pkey i { display: inline-block; width: calc(var(--u) * 0.6); height: calc(var(--u) * 0.25);
                margin-right: calc(var(--u) * 0.25); vertical-align: 0.15em; }

/* THE INSPECTION CARDS' FOOTERS AND THE PRICE GRID TOUCHED THE CARD'S EDGE
 * (seen on a broadcast frame 2026-09-11, 08:05 UTC). `.insp` carries no side
 * padding - each section brings its own 1.1u - and the footer rule is scoped
 * `.bd .bdfoot` / `.tmapcard .bdfoot`, so the station, town and price cards'
 * footers, and every row of the price grid, sat flush against the frame.
 * Side padding only: the size and weight each card chose are left alone,
 * and the map card, which has its own footer rule, is not touched. */
.cstv .insp:not(.tmapcard) .bdfoot { padding: calc(var(--u) * 0.45) calc(var(--u) * 1.1) 0; }
.cstv .pmgrid .pmrow { padding-left: calc(var(--u) * 1.1); padding-right: calc(var(--u) * 1.1); }

/* ---- THE EVENT SHOWS (tvoverlay.js evCard, director/evshow.py) ---------------
 * A career, a company in the red, a company closed by the bank, the final table
 * and a record falling, each a card in the full zone. Laid out by grid and gap
 * like every other full chart; the charts are SVG with no text inside them, so
 * a stretched viewBox cannot distort a label. */
.cstv .fevent .fcrows { gap: calc(var(--u) * 0.9); }
.cstv .evgrid { display: grid; grid-template-columns: 1fr 1.35fr; gap: calc(var(--u) * 1.6);
                align-items: start; }
.cstv .evgrid.two { grid-template-columns: 1fr 1fr; }
.cstv .evname { font-size: calc(var(--u) * 3); font-weight: 700; line-height: 1.05; color: #f2f6fb; }
.cstv .evco { font-size: calc(var(--u) * 1.4); color: #b8c6d8; margin-top: calc(var(--u) * 0.2); }
.cstv .evco.big { font-size: calc(var(--u) * 2.4); font-weight: 700; color: #f2f6fb; }
.cstv .evco i { font-style: normal; font-size: calc(var(--u) * 1.2); color: #93a3b8;
                margin-left: calc(var(--u) * 0.8); }
.cstv .evchips { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 0.45);
                 margin-top: calc(var(--u) * 0.6); }
.cstv .evchip { font-size: calc(var(--u) * 0.8); letter-spacing: 0.16em; text-transform: uppercase;
                font-weight: 700; color: #0d1219; background: var(--pc, #8fa2b8);
                padding: calc(var(--u) * 0.12) calc(var(--u) * 0.6); border-radius: 2px; }
.cstv .evtiles { display: flex; flex-wrap: wrap; gap: calc(var(--u) * 0.6);
                 margin-top: calc(var(--u) * 0.8); }
.cstv .evtiles.col { flex-direction: column; margin-top: 0; }
.cstv .evtile { display: flex; flex-direction: column; gap: calc(var(--u) * 0.1);
                min-width: calc(var(--u) * 8); padding: calc(var(--u) * 0.45) calc(var(--u) * 0.8);
                background: rgba(126, 150, 180, 0.08); border-left: 3px solid rgba(126, 150, 180, 0.35); }
.cstv .evtile .k { font-size: calc(var(--u) * 0.72); letter-spacing: 0.14em; text-transform: uppercase;
                   color: var(--dim); }
.cstv .evtile b { font-size: calc(var(--u) * 1.55); font-weight: 700; color: #f2f6fb; }
.cstv .evtile b i { font-style: normal; font-size: calc(var(--u) * 0.95); font-weight: 500; color: #93a3b8;
                    margin-left: calc(var(--u) * 0.3); }
.cstv .evtile.dn { border-left-color: #e0676b; }
.cstv .evtile.dn b { color: #ff8a8e; }
.cstv .evtile.up { border-left-color: #4fd1a5; }
.cstv .evtile.up b { color: #7fe6c1; }
.cstv .evnote { margin-top: calc(var(--u) * 0.8); font-size: calc(var(--u) * 1); color: #93a3b8; }
.cstv .evplot { min-width: 0; }
.cstv .evplk { display: flex; justify-content: space-between; align-items: baseline; gap: calc(var(--u) * 0.8);
               font-size: calc(var(--u) * 0.85); letter-spacing: 0.14em; text-transform: uppercase;
               color: var(--dim); font-weight: 700; margin-bottom: calc(var(--u) * 0.4); }
.cstv .evplk span { display: inline-flex; align-items: center; gap: calc(var(--u) * 0.35);
                    letter-spacing: 0.06em; text-transform: none; font-weight: 500; }
.cstv .evplk span i { display: inline-block; width: calc(var(--u) * 0.8); height: calc(var(--u) * 0.45);
                      margin-left: calc(var(--u) * 0.5); }
.cstv .evplk span i.dash { border-top: 2px dashed #eaf1f9; height: 0; }
.cstv .evplk span i.c1 { background: #5aa9e6; }
.cstv .evplk span i.c2 { background: #f2c14e; }
.cstv .evplk span i.sw { background: #4fd1a5; }
.cstv .evplk span i.dot { width: calc(var(--u) * 0.5); border-radius: 50%; background: rgba(143, 162, 184, 0.6); }
.cstv .evsvg { display: block; width: 100%; height: calc(var(--u) * 22); }
.cstv .evsvg.short { height: calc(var(--u) * 11); }
.cstv .evsvg.tall { height: calc(var(--u) * 16); }
/* A SECTION LABEL BELONGS TO WHAT IS UNDER IT: space above it, not below. */
.cstv .evtiles + .evplk, .cstv .evtiles + .evplot, .cstv .evladder + .evtiles,
.cstv .evrec + .evplk { margin-top: calc(var(--u) * 1.2); }
.cstv .evsvg .evzero { stroke-dasharray: 6 5; }
.cstv .evsvg .evavg { stroke: #eaf1f9; stroke-width: 2; stroke-dasharray: 8 6; vector-effect: non-scaling-stroke; }
.cstv .evsvg .evzero { stroke: rgba(234, 241, 249, 0.45); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.cstv .evsvg .evcash { fill: none; stroke: #5aa9e6; stroke-width: 3; vector-effect: non-scaling-stroke; }
.cstv .evsvg .evdebt { fill: none; stroke: #eaf1f9; stroke-width: 2; stroke-dasharray: 6 5;
                       vector-effect: non-scaling-stroke; }
.cstv .evsvg .evval { fill: none; stroke: #f2c14e; stroke-width: 3; vector-effect: non-scaling-stroke; }
.cstv .evsvg .evstair { fill: none; stroke: #f2c14e; stroke-width: 3; vector-effect: non-scaling-stroke; }
.cstv .evsvg .evmark { fill: #f2c14e; stroke: #070b10; stroke-width: 2; vector-effect: non-scaling-stroke; }
.cstv .evsvg .evmark.now { fill: #4fd1a5; }
.cstv .evsvg .evdot { fill: rgba(143, 162, 184, 0.45); }
/* a career: sessions per world, wins inside them */
.cstv .evcols { display: flex; align-items: flex-end; gap: calc(var(--u) * 0.35);
                height: calc(var(--u) * 17); }
.cstv .evcol { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; height: 100%;
               gap: calc(var(--u) * 0.15); min-width: 0; }
.cstv .evbarv { position: relative; flex: 1 1 auto; width: 70%; }
.cstv .evbarv i { position: absolute; left: 0; right: 0; bottom: 0; display: block; }
.cstv .evbarv i.s { background: rgba(126, 150, 180, 0.4); }
.cstv .evbarv i.w { background: #4fd1a5; left: 22%; right: 22%; }
.cstv .evcol b { font-size: calc(var(--u) * 0.95); color: #eaf1f9; }
.cstv .evcol span { font-size: calc(var(--u) * 0.8); color: var(--dim); }
.cstv .evcol.here span { color: #f2c14e; font-weight: 700; }
.cstv .evhb { display: grid; grid-template-columns: calc(var(--u) * 10) 1fr auto; align-items: center;
              gap: calc(var(--u) * 0.6); padding: calc(var(--u) * 0.2) 0; font-size: calc(var(--u) * 1.05); }
.cstv .evhbar { height: calc(var(--u) * 0.55); background: rgba(126, 150, 180, 0.16); }
.cstv .evhbar > i { display: block; height: 100%; }
.cstv .evhbar > i.old { background: rgba(143, 162, 184, 0.7); }
.cstv .evhbar > i.new { background: #4fd1a5; }
/* a debut */
.cstv .evdebut .evblurb { margin-top: calc(var(--u) * 1.2); font-size: calc(var(--u) * 1.9); line-height: 1.3;
                          color: #f2f6fb; max-width: 60em; }
.cstv .evdebut .evpass { margin-top: calc(var(--u) * 0.6); font-size: calc(var(--u) * 1.1); color: #b8c6d8; }
/* in the red: the count */
.cstv .evcount { display: flex; align-items: baseline; gap: calc(var(--u) * 0.8); }
.cstv .evcount b { font-size: calc(var(--u) * 3.8); font-weight: 700; color: #ff8a8e; line-height: 1; }
.cstv .evcount span { font-size: calc(var(--u) * 1.5); color: #eaf1f9; }
.cstv .evladder { display: grid; grid-template-columns: repeat(10, 1fr); gap: calc(var(--u) * 0.3);
                  margin-top: calc(var(--u) * 0.8); }
.cstv .evm { position: relative; height: calc(var(--u) * 2.8); background: rgba(126, 150, 180, 0.12);
             display: flex; flex-direction: column; justify-content: center; align-items: center; }
.cstv .evm b { font-size: calc(var(--u) * 1.1); color: #93a3b8; }
.cstv .evm span { font-size: calc(var(--u) * 0.66); letter-spacing: 0.12em; text-transform: uppercase;
                  color: #eaf1f9; }
.cstv .evm.mk { border-bottom: 3px solid #f2b134; }
.cstv .evm.on { background: rgba(224, 103, 107, 0.75); }
.cstv .evm.on b { color: #fff; }
/* one lane a row, diverging from the middle */
.cstv .evlane { display: grid; grid-template-columns: calc(var(--u) * 7.5) 1fr calc(var(--u) * 8);
                align-items: center; gap: calc(var(--u) * 0.6); padding: calc(var(--u) * 0.35) 0;
                font-size: calc(var(--u) * 1.15); border-bottom: 1px solid var(--edge); }
.cstv .evlane span i { font-style: normal; color: var(--dim); font-size: calc(var(--u) * 0.9); }
.cstv .evlane b { text-align: right; }
.cstv .evlane.dim { opacity: 0.45; }
.cstv .evdiv { position: relative; height: calc(var(--u) * 0.8); background: rgba(126, 150, 180, 0.1); }
.cstv .evdiv::after { content: ""; position: absolute; left: 50%; top: -3px; bottom: -3px; width: 1px;
                      background: rgba(234, 241, 249, 0.4); }
.cstv .evdiv i { position: absolute; top: 0; bottom: 0; display: block; }
.cstv .evdiv i.l { background: #e0676b; }
.cstv .evdiv i.g { background: #4fd1a5; }
.cstv .fevent b.dn, .cstv .fevent .dn { color: #ff8a8e; }
.cstv .fevent b.up, .cstv .fevent .up { color: #7fe6c1; }
/* the final table: a podium, then the rest */
.cstv .evpodium { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: calc(var(--u) * 0.8);
                  align-items: end; }
.cstv .evstep { display: flex; flex-direction: column; align-items: center; gap: calc(var(--u) * 0.2);
                text-align: center; min-width: 0; }
.cstv .evstep .evsn { font-size: calc(var(--u) * 1.5); font-weight: 700; color: #f2f6fb;
                      max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cstv .evstep.p1 .evsn { font-size: calc(var(--u) * 2); }
.cstv .evstep b { font-size: calc(var(--u) * 1.6); }
.cstv .evstep span { font-size: calc(var(--u) * 0.75); letter-spacing: 0.14em; text-transform: uppercase;
                     color: var(--dim); }
.cstv .evblock { width: 100%; display: flex; align-items: flex-start; justify-content: center;
                 padding-top: calc(var(--u) * 0.4); margin-top: calc(var(--u) * 0.3); }
.cstv .evblock i { font-style: normal; font-size: calc(var(--u) * 2.4); font-weight: 700; color: #0d1219; }
.cstv .evstep.p1 .evblock { height: calc(var(--u) * 10); background: #f2c14e; }
.cstv .evstep.p2 .evblock { height: calc(var(--u) * 7); background: #c9d3de; }
.cstv .evstep.p3 .evblock { height: calc(var(--u) * 5); background: #d08b52; }
.cstv .evrests { display: flex; flex-direction: column; gap: calc(var(--u) * 0.3); margin-top: calc(var(--u) * 1); }
.cstv .evrest { display: grid; grid-template-columns: calc(var(--u) * 2.4) 1fr auto; gap: calc(var(--u) * 0.6);
                font-size: calc(var(--u) * 1.2); padding-bottom: calc(var(--u) * 0.2); border-bottom: 1px solid var(--edge); }
.cstv .evrest i { font-style: normal; color: var(--dim); }
/* records: the marks that fell tonight, and one mark falling */
.cstv .evrecs { display: flex; flex-direction: column; gap: calc(var(--u) * 0.45); }
.cstv .evrec { display: grid; grid-template-columns: 1.3fr 1fr auto auto; align-items: baseline;
               gap: calc(var(--u) * 0.8); font-size: calc(var(--u) * 1.3); padding-bottom: calc(var(--u) * 0.3);
               border-bottom: 1px solid var(--edge); }
.cstv .evlegacy .evrec { grid-template-columns: 1fr auto; }
.cstv .evrec em { font-style: normal; color: #b8c6d8; font-size: calc(var(--u) * 1.05); }
.cstv .evrec b { font-weight: 700; color: #f2f6fb; }
.cstv .evscope { font-style: normal; font-size: calc(var(--u) * 0.7); letter-spacing: 0.14em; text-transform: uppercase;
                 color: #0d1219; background: #f2c14e; padding: calc(var(--u) * 0.1) calc(var(--u) * 0.5); }
.cstv .evrecord { text-align: center; }
.cstv .evrecord .evrl { font-size: calc(var(--u) * 1); letter-spacing: 0.16em; text-transform: uppercase; color: #7e96b4; }
.cstv .evrecord .evrv { font-size: calc(var(--u) * 5.4); line-height: 1.05; font-weight: 700; color: #f2f6fb; }
.cstv .evrecord .evrh { font-size: calc(var(--u) * 1.4); color: #dfe7f1; }
.cstv .evvs { display: flex; flex-direction: column; gap: calc(var(--u) * 0.6); margin: calc(var(--u) * 1.2) 8% 0;
              text-align: left; }
.cstv .evvr { display: grid; grid-template-columns: calc(var(--u) * 4) 1fr calc(var(--u) * 9) calc(var(--u) * 14);
              align-items: center; gap: calc(var(--u) * 0.7); font-size: calc(var(--u) * 1.2); }
.cstv .evvr span { color: var(--dim); text-transform: uppercase; letter-spacing: 0.14em; font-size: calc(var(--u) * 0.8); }
.cstv .evvr em { font-style: normal; color: #b8c6d8; font-size: calc(var(--u) * 1); overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
.cstv .evaxis { display: flex; justify-content: space-between; font-size: calc(var(--u) * 0.9); color: var(--dim);
                margin-top: calc(var(--u) * 0.2); }
.cstv .evlegs { display: grid; grid-template-columns: repeat(4, 1fr); gap: calc(var(--u) * 0.6);
                margin-top: calc(var(--u) * 0.6); }
.cstv .evleg { display: flex; flex-direction: column; gap: calc(var(--u) * 0.05); padding-left: calc(var(--u) * 0.5);
               border-left: 3px solid #f2c14e; min-width: 0; }
.cstv .evleg em { font-style: normal; font-size: calc(var(--u) * 0.8); color: var(--dim); }
.cstv .evleg b { font-size: calc(var(--u) * 1.25); }
.cstv .evleg span { font-size: calc(var(--u) * 0.95); color: #b8c6d8; overflow: hidden; text-overflow: ellipsis;
                    white-space: nowrap; }
