/* Site chrome, lab tables and the server-rendered SVG charts. App-specific: never
   vendored back into the numbered design-system core. */

/* Clipped to nothing rather than display:none, so a keyboard can still tab to it. In flow
   rather than absolutely placed: focused, it takes a row of its own above the header
   instead of covering the wordmark. Zero-sized it costs no height. */
.skip-link {
  display: block;
  width: 0;
  height: 0;
  padding: 0;
  overflow: hidden;
  background: var(--paper-soft);
  color: var(--ink);
}

.skip-link:focus {
  width: auto;
  height: auto;
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--border-width-1) solid var(--line-strong);
}

.site-header {
  border-bottom: 1px solid var(--line);
  background: var(--paper-soft);
  padding: var(--space-4) 0;
}

.site-wordmark {
  font-family: var(--font-display);
  font-size: var(--font-size-xl);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
}

.site-nav a {
  color: var(--ink-secondary);
  text-decoration: none;
  /* A multi-word entry wraps mid-word on a phone otherwise. */
  white-space: nowrap;
}

.site-nav a:hover {
  color: var(--accent);
}

.site-nav a[aria-current="page"] {
  color: var(--ink);
  font-weight: var(--font-weight-semibold);
}

.site-main {
  /* padding-block only: the shorthand would zero the .container inline gutter. */
  padding-block: var(--space-8);
}

/* A container nested in the page container keeps a single gutter. */
.site-main > .container-narrow,
.site-main > .container-wide {
  padding-inline: 0;
}

/* A heading linked to from another page (#glossary on /how-it-works) would otherwise land
   flush against the top of the viewport, reading as a page that starts mid-section. */
.site-main h2[id],
.site-main h3[id] {
  scroll-margin-top: var(--space-6);
}

.site-footer {
  border-top: 1px solid var(--line);
  padding: var(--space-5) 0;
  color: var(--ink-muted);
}

/* Wide tables scroll inside their own box so the page never scrolls sideways, and the
   backgrounds hint at that overflow: scrollbars are hidden until they move on macOS and
   iOS, where a column clipped mid-number reads as broken data. The paper-coloured covers
   scroll with the content (`local`) and the shadows stay pinned to the box (`scroll`), so
   each shadow is uncovered exactly while there is more table that way. */
.table-scroll {
  overflow-x: auto;
  background-repeat: no-repeat;
  background-attachment: local, local, scroll, scroll;
  background-position: left center, right center, left center, right center;
  background-size: var(--space-8) 100%, var(--space-8) 100%, var(--space-4) 100%, var(--space-4) 100%;
  background-image:
    linear-gradient(to right, var(--paper) 60%, transparent),
    linear-gradient(to left, var(--paper) 60%, transparent),
    radial-gradient(farthest-side at 0 50%, var(--line-strong), transparent),
    radial-gradient(farthest-side at 100% 50%, var(--line-strong), transparent);
}

/* The markup makes the box a focusable region so a keyboard can scroll it, which means it
   needs a ring of its own; it matches the button ring in 01-buttons.css. An outline is
   painted outside the border box and the fade mask below clips everything out there, ring
   included, so a focused box drops the mask and hints its overflow with the covers and
   shadows alone — a ring that cannot be seen is worse than a harder edge. */
.table-scroll:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  mask-image: none;
}

/* The background shadows sit behind the cells, so a status pill at the edge is still cut
   in half and reads as broken rather than as more table. Masking the box's own edge fades
   the clipped column out instead, and the two scroll-driven animations widen each fade
   only while there is table left that way: the first over the opening 2rem of the scroll
   range, the second over its closing 2rem. Black and transparent here are mask alpha, not
   paint, so no colour token applies. */
@property --table-scroll-fade-start {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

@property --table-scroll-fade-end {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

@keyframes table-scroll-fade-start {
  to { --table-scroll-fade-start: var(--space-8); }
}

@keyframes table-scroll-fade-end {
  from { --table-scroll-fade-end: var(--space-8); }
}

@supports (animation-timeline: scroll(self inline)) {
  .table-scroll {
    mask-image: linear-gradient(to right,
      transparent,
      black var(--table-scroll-fade-start),
      black calc(100% - var(--table-scroll-fade-end)),
      transparent);
    animation: table-scroll-fade-start linear both, table-scroll-fade-end linear both;
    animation-timeline: scroll(self inline), scroll(self inline);
    animation-range: 0px var(--space-8), calc(100% - var(--space-8)) 100%;
  }
}

/* A finding's prose is set to a measure, but a table inside it is evidence, not prose, and
   its columns cannot be read through a 600 px window. Once the page is wide enough to hold
   62rem plus its own gutter, a table breaks out of the measure, centred on it. The 37.5rem
   is .container-narrow's max-width in 03-layout.css. A reading partial shared with a sweep
   page wraps its table in a section, which spans the measure too. */
@media (min-width: 64rem) {
  .container-narrow > .table-scroll,
  .container-narrow > .table-figure,
  .container-narrow > section > .table-figure {
    margin-inline: calc((37.5rem - 62rem) / 2);
  }
}

/* A captioned table: the caption is a sibling of the scrolling box, so it wraps at the
   page's width and never gets clipped with the table's overflow. */
.table-figure {
  margin-inline: 0;
  margin-bottom: 0;
}

.table-figure > .table-caption + .table-scroll > .data-table {
  margin-top: var(--space-2);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--space-4) 0;
}

.data-table th,
.data-table td {
  border-bottom: 1px solid var(--line);
  padding: var(--space-2) var(--space-3);
  text-align: left;
}

.data-table th {
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
}

.data-table .numeric {
  text-align: right;
}

.data-table tbody tr:hover {
  background: var(--paper-warm);
}

.mono {
  font-family: var(--font-mono);
}

.facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-1) var(--space-5);
  margin: var(--space-4) 0;
}

.facts dt {
  color: var(--ink-muted);
}

.facts dd {
  margin: 0;
}

/* Definitions long enough to wrap need air between the rows. */
.facts-prose {
  row-gap: var(--space-4);
}

/* And on a phone the term column leaves a sentence a ribbon a few words wide, so the term
   sits above its definition instead. Short pairs (a run's facts, its parameters) keep
   their two columns: stacking those would only make them twice as tall. */
@media (max-width: 40rem) {
  .facts-prose {
    grid-template-columns: 1fr;
    row-gap: var(--space-1);
  }

  .facts-prose dd:not(:last-child) {
    margin-bottom: var(--space-4);
  }
}

.chart {
  margin: var(--space-6) 0;
}

/* A chart quoted inside a finding's narrative: the caption under it names the run. */
.chart-cited {
  margin: var(--space-6) 0;
}

.chart-cited .chart {
  margin: 0 0 var(--space-2);
}

.chart-title {
  font-weight: 600;
  margin-bottom: var(--space-2);
}

.chart-svg {
  /* Label sizes are viewBox user units, so they shrink with the SVG: an 828-unit chart
     drawn 358 px wide renders 13 of them as 6 px. The media queries below step the sizes
     up as the chart narrows, and Charts::Plot::PADDING is the room the largest step
     needs. */
  --chart-label-size: 13px;
  --chart-axis-title-size: 14px;
  width: 100%;
  max-width: 100%;
  height: auto;
  background: var(--paper-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius-default);
}

.chart-grid line {
  stroke: var(--line);
  stroke-width: 1;
}

.chart-axis line {
  stroke: var(--line-strong);
  stroke-width: 1;
}

.chart-tick-label text,
.chart-axis-label text {
  fill: var(--ink-muted);
  font-family: var(--font-mono);
  font-size: var(--chart-label-size);
}

.chart-axis-label text {
  font-family: var(--font-sans);
  font-size: var(--chart-axis-title-size);
}

.chart-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
}

.chart-median {
  stroke-dasharray: 6 3;
}

.chart-dots circle {
  fill: var(--accent);
  fill-opacity: 0.65;
}

/* A crossing the detector flagged that no replicator confirmed: the same position, drawn
   hollow, so it is never read as an emergence. */
.chart-dots-flagged circle {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-opacity: 0.55;
}

.chart-censored circle {
  fill: none;
  stroke: var(--ink-muted);
  stroke-width: 1.5;
}

.chart-censored text {
  fill: var(--ink-muted);
  font-family: var(--font-mono);
  font-size: var(--chart-label-size);
}

/* The agreement chart's three bars per arm: the two disagreements in the signal colours,
   the agreement in the brand's own. Their lightness differs as much as their hue, so the
   bars stay apart in greyscale and in print. */
.chart-bar-flagged {
  fill: var(--negative);
}

.chart-bar-replicated {
  fill: var(--premium);
}

.chart-bar-both {
  fill: var(--accent);
}

.chart-marker line {
  stroke: var(--negative);
  stroke-width: 1.5;
  stroke-dasharray: 4 3;
}

.chart-marker text {
  fill: var(--negative);
  font-family: var(--font-mono);
  font-size: var(--chart-label-size);
  /* On a rising metric the plateau runs straight through this label — the narrower the
     chart, the bigger the label and the surer the collision — so the glyphs are cut out
     of the plot with a halo of the chart's own background. */
  paint-order: stroke;
  stroke: var(--paper-soft);
  stroke-width: 0.3em;
  stroke-linejoin: round;
}

/* A chart that has the page to itself, drawn around 600 px wide. */
@media (max-width: 40rem) {
  .chart-svg {
    --chart-label-size: 17px;
    --chart-axis-title-size: 18px;
  }
}

/* A phone: the same chart drawn around 360 px wide, where a label has to be 23 units to
   reach the 10 px it takes to be read. A y label that size fills the left gutter, so the
   rotated y title gives way — the figcaption above the plot already names the metric. */
@media (max-width: 30rem) {
  .chart-svg {
    --chart-label-size: 23px;
    --chart-axis-title-size: 24px;
  }

  .chart-y-title {
    display: none;
  }
}

/* A legend row carries whatever tells its series apart — the survival chart's dash
   pattern, the agreement chart's bar fill — so the swatch is a miniature SVG of the mark
   rather than a plain colour chip. */
ul.chart-legend {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  padding: 0;
  margin: var(--space-2) 0 0;
}

ul.chart-legend li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.chart-swatch {
  width: 28px;
  height: 8px;
  flex: none;
}

.data-table .row-total td {
  border-top: 1px solid var(--line-strong);
  font-weight: 600;
}

.chart-grid-2 {
  display: grid;
  /* min() keeps the column from forcing a track wider than a narrow viewport. */
  grid-template-columns: repeat(auto-fit, minmax(min(460px, 100%), 1fr));
  gap: var(--space-5);
}

.snapshot-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--space-4);
}

.snapshot img {
  width: 100%;
  image-rendering: pixelated;
  border: 1px solid var(--line);
  background: var(--paper-sunken);
}

.pagination {
  margin: var(--space-5) 0;
}

.pagination a {
  padding: var(--space-1) var(--space-2);
  color: var(--ink-secondary);
}

.pagination a[aria-current="page"] {
  color: var(--accent);
  font-weight: 600;
}

/* The findings log: one card per published claim. */
.finding-list {
  list-style: none;
  margin: var(--space-6) 0;
  padding: 0;
  display: grid;
  gap: var(--space-6);
}

.finding-card {
  border-top: 1px solid var(--line);
  padding-top: var(--space-5);
  /* A summary held to one line must ellipsize inside the card, not widen the grid. */
  min-width: 0;
}

.finding-title {
  margin: var(--space-2) 0;
}

.finding-title a {
  color: var(--ink);
  text-decoration: none;
}

.finding-title a:hover {
  color: var(--accent);
}

.finding-meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
}

.instrument-note {
  margin: var(--space-5) 0;
}

.instrument-note p {
  margin: 0 0 var(--space-2);
}

.instrument-note p:last-child {
  margin-bottom: 0;
}

.instrument-note a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.instrument-note-badge,
.instrument-note-badge:hover,
.objective-badge,
.objective-badge:hover,
.machine-badge,
.machine-badge:hover {
  color: var(--paper);
  text-decoration: none;
}

.instrument-note-badge:hover,
.objective-badge:hover,
.machine-badge:hover {
  background: var(--ink-secondary);
}
