/* The Data Event Platform page.
 *
 * Rides on performance.css for the palette, header, bands and tiles -- this is
 * a page on that site, not a second product, and it must not look like one.
 * Everything here is what that stylesheet has no concept of: the diagram, the
 * supply-path comparison, the lane cards, the embedded map and planner.
 *
 * One rule runs through all of it: a lane's state is carried by *shape* (solid
 * versus dashed) and by words, never by colour alone -- this page gets
 * projected in meeting rooms and photographed off screens.
 */

.viz-root {
  --lane-live: var(--series-1);
  --lane-dark: var(--text-muted);
  --chip: rgba(11, 11, 11, 0.04);
  /* Its own token rather than --status-warning: that amber means "warning"
     wherever else it appears, and this is a brand colour, not a state. */
  --brand-orange: #cf5f12;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) .viz-root {
    --chip: rgba(255, 255, 255, 0.06);
    --brand-orange: #ff8f45;
  }
}
:root[data-theme="dark"] .viz-root {
  --chip: rgba(255, 255, 255, 0.06);
  --brand-orange: #ff8f45;
}

/* ---------- The hero title, which alternates every five seconds ---------- */

/* Both titles occupy the same grid cell, so the hero doesn't reflow as they
   cross over and the block keeps the height of the taller one. */
.hero h1.morph {
  display: grid;
  position: relative;
}

.hero h1.morph > span {
  grid-area: 1 / 1;
  transition: opacity 0.5s ease, transform 0.5s ease, color 0.5s ease;
}

.morph-b {
  color: var(--brand-orange);
  opacity: 0;
  transform: translateY(0.3em);
}

.viz-root.morphed .morph-a {
  opacity: 0;
  transform: translateY(-0.3em);
}

.viz-root.morphed .morph-b {
  opacity: 1;
  transform: none;
}

/* Swap without the movement for anyone who has asked for less of it. */
@media (prefers-reduced-motion: reduce) {
  .hero h1.morph > span { transition: opacity 0.2s linear; transform: none; }
  .viz-root.morphed .morph-a { transform: none; }
}

/* A single word in running text, swapping on the same clock as the title.
   The first word stays in flow to set the text baseline; the second is laid
   over it. The two words are different widths, so the box is given an
   explicit width in JS and transitions between them — without that it stays
   as wide as "events" and leaves a hole in the sentence after "data". */
.wordswap {
  position: relative;
  display: inline-block;
  white-space: nowrap;
  transition: width 0.5s ease;
}

.wordswap > span { transition: opacity 0.5s ease; }

.wordswap .swap-b {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  white-space: nowrap;
  color: var(--brand-orange);
  font-weight: 600;
}

.viz-root.morphed .wordswap .swap-a { opacity: 0; }
.viz-root.morphed .wordswap .swap-b { opacity: 1; }

/* ---------- Hero additions ---------- */

.eyebrow {
  margin: 0 0 10px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.hero-claim {
  max-width: 620px;
  margin: 16px auto 0;
  font-size: 15px;
  color: var(--text-secondary);
}

/* The claims worth colouring: the page's one-line argument, and the sentence
   most likely to be disbelieved in the room. Weight still carries both if the
   colour doesn't reach the back of it. */
.hero-claim strong,
.hot { color: var(--brand-orange); }

/* The page's own name, next to the site wordmark. `margin-right: auto` groups
   it with the wordmark on the left -- .site-head-inner is space-between, which
   would otherwise strand it in the middle of the bar. */
.head-badge {
  margin-right: auto;
  color: var(--brand-orange);
  font-weight: 600;
  font-size: 13.5px;
  white-space: nowrap;
}

/* Dropped before it can push the nav off the bar. */
@media (max-width: 760px) {
  .head-badge { display: none; }
}

.hero-stats {
  margin-top: 26px;
  min-height: 62px;
}

.hero-stats .loading { color: var(--text-muted); font-size: 14px; }

/* Whose numbers these are, said once above the row rather than repeated in
   every tile's caption. */
.hero-scope {
  margin: 0 0 10px;
  font-size: 13px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.hero-scope strong { color: var(--brand-orange); }

.hero-stat-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}

/* Everywhere else, in one sentence. Same figures, deliberately a fifth of the
   weight -- it answers "does this only work in South Yorkshire?" without
   competing with the numbers the room came to see. */
.hero-rest {
  max-width: 680px;
  margin: 14px auto 0;
  font-size: 13.5px;
  color: var(--text-muted);
}

.hero-rest strong {
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.hero-stat {
  min-width: 132px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-1);
  text-align: left;
}

.hero-stat .n {
  display: block;
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.hero-stat .k {
  display: block;
  font-size: 12.5px;
  color: var(--text-muted);
}

/* The reading of the row above, not a second headline. Same tile, stepped down
   a size, so the eye takes the two rows in that order rather than treating
   eight equal figures as one undifferentiated wall. */
.hero-stat-row.small {
  margin-top: 10px;
}

.hero-stat-row.small .hero-stat {
  min-width: 124px;
  padding: 10px 14px;
  background: transparent;
}

.hero-stat-row.small .n { font-size: 21px; }

.hero-stat-row.small .k {
  font-size: 12px;
  color: var(--text-secondary);
}

/* The denominator, kept on the tile. A percentage with no sample size behind
   it is the kind of figure this page exists not to print. */
.hero-stat .sub {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-muted);
}

/* ---------- Diagram ---------- */

.diagram-wrap {
  /* The diagram has a minimum legible width; below it the page scrolls the
     figure rather than the body. */
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-1);
  padding: 8px;
}

/* Two source groups made the canvas wider. Below this the wrap scrolls rather
   than scaling the labels down to unreadable. */
.diagram { min-width: 1040px; }
.diagram svg { display: block; width: 100%; height: auto; }

.diagram .box-fill { fill: var(--surface-1); }
.diagram .group-fill { fill: var(--chip); }
.diagram .rule { stroke: var(--border); fill: none; }
.diagram .rule-dashed { stroke: var(--lane-dark); fill: none; stroke-dasharray: 5 5; }
.diagram .rule-live { stroke: var(--lane-live); fill: none; }

.diagram text {
  font-family: inherit;
  fill: var(--text-primary);
  font-size: 13px;
}

.diagram .t-title { font-size: 14px; font-weight: 700; }
.diagram .t-small { font-size: 11.5px; fill: var(--text-secondary); }
.diagram .t-muted { font-size: 11.5px; fill: var(--text-muted); }
.diagram .t-group { font-size: 11.5px; font-weight: 600; fill: var(--text-muted); letter-spacing: 0.05em; }
.diagram .t-dark { fill: var(--text-muted); }
/* Chip labels sit a step below body text: sixteen of them at 13px crowds the
   grid, and the label is a shorthand the card below spells out anyway. */
.diagram .t-chip { font-size: 12.5px; font-weight: 600; }

.diagram .lane-live { stroke: var(--lane-live); stroke-width: 2; fill: none; }
.diagram .lane-dark { stroke: var(--lane-dark); stroke-width: 1.5; fill: none; stroke-dasharray: 6 5; }

.diagram .hit { cursor: pointer; }
.diagram .hit:hover .box-fill { fill: var(--chip); }

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: 14px;
  font-size: 13px;
  color: var(--text-secondary);
}

.key { display: inline-flex; align-items: center; gap: 8px; }

.swatch {
  width: 26px;
  height: 0;
  border-top: 2px solid var(--lane-live);
}

.swatch.dark { border-top: 1.5px dashed var(--lane-dark); }
/* The direction key has no line to show -- the glyph in the label carries it,
   so the swatch just holds the row's alignment. */
.swatch.flow { border-top: none; width: 0; }

/* ---------- Supply paths ---------- */

.paths {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 16px;
}

.path {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-1);
  padding: 18px 20px;
}

.path.future { border-style: dashed; }

.path-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}

.path-head h3 { margin: 0; font-size: 15.5px; }

.tag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  white-space: nowrap;
}

.tag.now { border-color: var(--lane-live); color: var(--lane-live); }

/* The franchised path's heading line, picked out in the same orange the page
   uses for where it is going rather than where it is. The card keeps its
   dashed border: the state is still "designed for, not connected", and colour
   never carries that on its own here. */
.path.future .path-head h3 { color: var(--brand-orange); }

.path.future .tag {
  border-color: var(--brand-orange);
  color: var(--brand-orange);
}

/* Names the route the hops below it belong to -- there are two per path now,
   and unlabelled they read as one twelve-hop chain. */
.route-label {
  margin: 14px 0 0;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.hops {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 8px 0 14px;
  padding: 0;
  list-style: none;
}

.hops li {
  font-size: 12.5px;
  padding: 5px 10px;
  border-radius: 8px;
  background: var(--chip);
  color: var(--text-secondary);
}

.hops li.terminal { font-weight: 600; color: var(--text-primary); }
.hops .arrow { background: none; padding: 0 2px; color: var(--text-muted); }

.path ul.points { margin: 0; padding-left: 18px; font-size: 13.5px; color: var(--text-secondary); }
.path ul.points li { margin-bottom: 6px; }
.path ul.points li::marker { color: var(--text-muted); }
.path h4 {
  margin: 14px 0 6px;
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.pull {
  margin: 22px 0 0;
  font-size: 16px;
  font-weight: 600;
  text-align: center;
  color: var(--text-primary);
}

/* ---------- Lane cards ---------- */

.lanes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}

.lane {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-1);
  padding: 16px 18px;
}

.lane.dark { border-style: dashed; }

.lane-head { display: flex; align-items: baseline; gap: 8px; }
.lane-head h3 { margin: 0; font-size: 15px; }
/* nowrap so a two-word short name breaks to its own line rather than down the
   middle of itself. */
.lane-head .abbr { font-size: 12px; color: var(--text-muted); font-weight: 600; white-space: nowrap; }

/* A second heading inside one band, for the off-bus half. Needs the top margin
   a new band would have given it. */
.section-head.sub { margin-top: 40px; }

.lane-states {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 10px 0 8px;
}

.lane .state {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
}

.lane.live .state { color: var(--lane-live); }
.lane.dark .state { color: var(--text-muted); }

/* Says which way the lane runs, and only appears when that isn't obvious. */
.lane .direction {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 8px;
}

/* Counts under a connected lane: the evidence that it is connected. */
.lane-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin: 0 0 10px;
}

.lane-figures .figure { display: flex; flex-direction: column; }
.lane-figures .figure b { font-size: 17px; font-variant-numeric: tabular-nums; }
.lane-figures .figure span { font-size: 12px; color: var(--text-muted); }

.lane p { margin: 0 0 8px; font-size: 13.5px; color: var(--text-secondary); }
.lane p.produces { color: var(--text-primary); }
.lane .note { font-size: 13px; }

/* ---------- Map ---------- */

.map-shell {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-1);
}

#map { height: min(62vh, 560px); }

/* The site header is sticky and 58px tall, so an anchor jump (every consumer
   in the diagram links to its own band) otherwise lands with the band's
   heading hidden underneath it. */
.band[id] { scroll-margin-top: 70px; }

/* ---------- Embedded planner and report ---------- */

.embed-shell {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-1);
}

/* Every embedded frame, by id. An iframe with no width is 300px wide by
   default, so a new one added to a band without being listed here renders as
   a narrow column inside a full-width shell. */
#planner,
#punctuality-embed,
#pinch-embed {
  display: block;
  width: 100%;
  height: min(72vh, 680px);
  border: 0;
}

/* The report is a scrolling document rather than a fixed app frame: the
   planner fills whatever height it is given, this one has a filter bar, four
   tiles and a chart to get through before it is showing anything useful. */
.embed-shell.tall #punctuality-embed,
.embed-shell.tall #pinch-embed { height: min(86vh, 900px); }

/* ---------- Tables ---------- */

.table-wrap {
  overflow-x: auto;
  margin-top: 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-1);
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}

table.data th,
table.data td {
  padding: 10px 16px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

table.data thead th {
  font-size: 11.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 600;
}

table.data tbody tr:last-child td { border-bottom: 0; }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }
table.data td.wrap { white-space: normal; min-width: 240px; }

/* ---------- Detail sections rolled up out of the way ---------- */

/* The source catalogues are reference material: sixteen cards that push the
   live map and the embedded consumers below the fold. Collapsed by default so
   the page reaches the working parts first, and still fully there on click. */
.rollup {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-1);
}

.rollup > summary {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  padding: 16px 20px;
  cursor: pointer;
  list-style: none;
  font-weight: 600;
  font-size: 16px;
}

/* Both spellings: WebKit still wants the pseudo-element to drop the marker. */
.rollup > summary::-webkit-details-marker { display: none; }

.rollup > summary::before {
  content: "▸";
  font-size: 11px;
  color: var(--text-muted);
}

.rollup[open] > summary::before { content: "▾"; }
.rollup > summary:hover { color: var(--brand-orange); }

.rollup-hint {
  font-weight: 400;
  font-size: 13px;
  color: var(--text-muted);
}

.rollup-body {
  padding: 0 20px 22px;
  border-top: 1px solid var(--border);
}

.rollup-body > .section-note:first-child { margin-top: 16px; }

.state-chip {
  display: inline-block;
  font-size: 12px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
}

.state-chip.built { border-color: var(--status-good); color: var(--status-good); }
.state-chip.partial { border-color: var(--status-warning); color: var(--text-primary); }
.state-chip.none { border-style: dashed; color: var(--text-muted); }

/* ---------- Live map popup ---------- */

/* Same three states the reporting uses, so a bus called "on time" here and a
   departure counted on time there are visibly the same claim. */
.pop-state { font-weight: 600; }
.pop-state.on-time { color: var(--status-good); }
.pop-state.late { color: var(--status-critical); }
.pop-state.early { color: var(--series-1); }
.pop-state.unknown { color: var(--text-muted); font-weight: 400; }

/* ---------- Tech stack ---------- */

.moves {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin-bottom: 18px;
}

.move {
  display: flex;
  align-items: center;
  gap: 8px;
}

.move-gloss { font-size: 12.5px; color: var(--text-muted); }

#stack-table .stack-layer { font-weight: 600; }

#stack-table .stack-role {
  font-size: 12px;
  color: var(--text-muted);
  white-space: normal;
  max-width: 22ch;
  margin-top: 2px;
}

/* The version column is the evidence; keep it from wrapping to one word a
   line, but let it wrap rather than stretch the table off the page. */
#stack-table td.wrap { min-width: 180px; }
#stack-table td .note {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--text-secondary);
  white-space: normal;
  max-width: 46ch;
}

#stack-table .state-chip { text-transform: capitalize; }

/* ---------- Disruptions ---------- */

.reasons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}

.reason {
  font-size: 12.5px;
  padding: 5px 11px;
  border-radius: 999px;
  background: var(--chip);
  color: var(--text-secondary);
}

.reason b { color: var(--text-primary); font-variant-numeric: tabular-nums; }

.situations {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 12px;
}

.situation {
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: 10px;
  background: var(--surface-1);
  padding: 13px 15px;
}

.situation.severe { border-left-color: var(--status-critical); }
.situation.minor { border-left-color: var(--status-warning); }

.situation h3 { margin: 0 0 6px; font-size: 14px; line-height: 1.35; }
.situation .meta { font-size: 12px; color: var(--text-muted); }
.situation .lines { margin-top: 7px; display: flex; flex-wrap: wrap; gap: 5px; }

.situation .lines span {
  font-size: 11.5px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: 5px;
  background: var(--chip);
}

/* ---------- Footer tick ---------- */

.tick { font-size: 12px; color: var(--text-muted); }

@media (max-width: 700px) {
  .hero-stat { min-width: 110px; }
  #planner { height: 78vh; }
}
