/* chetnole.com
 *
 * One stylesheet, no framework, no webfont.
 *
 * The reference is the Ordnance Survey Explorer sheet folded in a car door:
 * cool grey green paper, hairline rules, water in blue, and one warm orange
 * that is reserved for the things that need doing today. Not a cream and
 * serif village pastiche and not a council template.
 *
 * No webfont, deliberately. Loading one means a request to somebody else's
 * server on every page view, and the footer claims this site makes none.
 * A local serif carries the headings instead, and the figures are set in the
 * platform's monospace so that times and levels line up in a column.
 */

:root {
  color-scheme: light;

  --sheet: #EDEFE9;      /* page ground, grey green rather than cream */
  --surface: #F8F9F4;
  --surface-2: #E4E7DE;
  --line: #CBD1C4;
  --line-soft: #DDE1D7;

  --ink: #181D19;
  --ink-2: #444F46;
  --ink-3: #67716A;

  --water: #1C5670;      /* links, live figures, the river trace */
  --water-soft: #D7E5EC;
  --route: #A44A1D;      /* reserved: needs doing today */
  --route-soft: #F3DFD2;
  --field: #3F5A2C;      /* normal, open, passable, on time */
  --field-soft: #DEE7D3;
  --flood: #8A241B;      /* closed, warning, cancelled */
  --flood-soft: #F1D8D4;

  --board-bg: #1B211D;   /* the departure board, and only that */
  --board-ink: #E9EDE4;
  --board-dim: rgba(233, 237, 228, 0.58);
  --board-rule: rgba(233, 237, 228, 0.16);
  --board-good: #8FCF9C;
  --board-warn: #E8B04B;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Consolas, monospace;

  --measure: 63ch;
  --gutter: clamp(1rem, 4vw, 2rem);
}

/* Dark is a choice the reader makes, not one the operating system makes for
 * them. Light is the default and stays the default: this is a paper coloured
 * site about a place, and most people reading it are outdoors or at a kitchen
 * table. The switch in the header is the only thing that turns this block on,
 * and what it sets is remembered in that browser and nowhere else. */
:root[data-theme="dark"] {
  color-scheme: dark;

    --sheet: #12160F;
    --surface: #1A1F19;
    --surface-2: #232A21;
    --line: #333C31;
    --line-soft: #28301F;

    --ink: #E8ECE1;
    --ink-2: #AEB8AB;
    --ink-3: #838D83;

    --water: #8CC0D7;
    --water-soft: #1B3540;
    --route: #E39A69;
    --route-soft: #3A2517;
    --field: #9CBC81;
    --field-soft: #22301B;
    --flood: #E08D82;
    --flood-soft: #381915;

    /* Deeper than the panel it sits in, not merely dark. In light mode the
     * board lifts off the page by being the only dark thing; in dark mode it
     * has to earn the same separation by going further down. */
    --board-bg: #05080B;
    --board-rule: rgba(233, 237, 228, 0.2);
}

*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--sheet);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 600;
  margin: 0;
  text-wrap: balance;
  line-height: 1.14;
}

h1 { font-size: clamp(2.1rem, 5.2vw, 3.2rem); letter-spacing: -0.015em; }
h2 { font-size: clamp(1.45rem, 3vw, 1.95rem); letter-spacing: -0.01em; }
h3 { font-size: 1.2rem; }
h4 { font-size: 1rem; font-family: var(--sans); font-weight: 700; line-height: 1.3; }

p, ul, ol, dl, table { margin: 0 0 1rem; }
p, li { max-width: var(--measure); }

a { color: var(--water); text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }

strong { font-weight: 700; }

:focus-visible {
  outline: 3px solid var(--water);
  outline-offset: 2px;
}

.container {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* The board is a dashboard and the written pages are prose, and they want
 * opposite things from a wide screen. Prose stays at a readable measure
 * however much room there is. The board spreads, because three cards side by
 * side is three cards you can see at once rather than one you have to scroll
 * to. */
@media (min-width: 78rem) {
  .band--dash > .container { max-width: 96rem; }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--sheet);
  padding: 0.6rem 1rem;
  z-index: 10;
}
.skip:focus { left: 0; }

/* ---- header ------------------------------------------------------------- */

.site-head {
  border-top: 5px solid var(--route);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.site-head__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.75rem 2rem;
  padding-top: 1rem;
  padding-bottom: 0.85rem;
}

.brand { text-decoration: none; color: inherit; display: block; }
.brand__name {
  display: block;
  font-family: var(--serif);
  font-size: 1.7rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1;
}
.brand__dot { color: var(--route); }

/* The county is the quieter half of the name. Same line and same face, so it
   reads as one address rather than as a second title, but lighter so the
   village is still the thing the eye lands on. */
.brand__county {
  color: var(--ink-3);
  font-weight: 400;
}
.brand__strap {
  display: block;
  font-size: 0.8125rem;
  color: var(--ink-3);
  margin-top: 0.3rem;
}

.site-head nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.15rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.site-head nav li { max-width: none; }
.site-head nav a {
  display: inline-block;
  padding: 0.15rem 0;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
.site-head nav a:hover { color: var(--ink); border-bottom-color: var(--line); }
.site-head nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--route); }

/* ---- bands -------------------------------------------------------------- */

.band { padding: clamp(2rem, 5vw, 3.25rem) 0; border-bottom: 1px solid var(--line); }
.band--alt { background: var(--surface); }
.band--tight { padding: clamp(1.5rem, 3vw, 2rem) 0; }
.band:last-of-type { border-bottom: 0; }

.lead {
  font-size: 1.1875rem;
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 60ch;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 0.6rem;
}

.stamp {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-3);
  letter-spacing: 0.02em;
}

.source {
  font-size: 0.8125rem;
  color: var(--ink-3);
  max-width: 60ch;
}

.unchecked {
  border-left: 3px solid var(--route);
  background: var(--route-soft);
  color: var(--ink);
  padding: 0.85rem 1.1rem;
  font-size: 0.9375rem;
  max-width: 60ch;
}

.note {
  border-left: 3px solid var(--line);
  padding: 0.15rem 0 0.15rem 1.1rem;
  color: var(--ink-2);
  font-size: 0.9375rem;
  max-width: 60ch;
}

/* ---- the live board ----------------------------------------------------- */

.board {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
  margin-top: 1.5rem;
}
@media (min-width: 46rem) {
  .board { grid-template-columns: 1.5fr 1fr; align-items: start; }
}

.board__col { display: grid; gap: 1rem; }

.panel {
  border: 1px solid var(--line);
  background: var(--surface);
  overflow: hidden;
}

.panel__head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.4rem 1rem;
  padding: 0.5rem 0.85rem;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.panel__head h2, .panel__head h3 {
  font-family: inherit;
  font-size: inherit;
  font-weight: 500;
  letter-spacing: inherit;
  color: inherit;
  margin: 0;
}
.panel__body { padding: 0.85rem; }
.panel__body > :last-child { margin-bottom: 0; }

/* the departure board is the one thing that lifts off the page, because it is
 * the one thing read at arm's length on a platform */
.departures {
  background: var(--board-bg);
  color: var(--board-ink);
  padding: 0.9rem 0.95rem 1rem;
}

.departures__dir {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--board-dim);
  margin: 0 0 0.55rem;
}
.departures__dir + .departures__dir { margin-top: 1rem; }

.departures ul { list-style: none; margin: 0; padding: 0; }
.departures li {
  display: grid;
  grid-template-columns: 3.6rem 1fr auto;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--board-rule);
  max-width: none;
}
.departures li:last-child { border-bottom: 0; }

.dep__time { font-family: var(--mono); font-size: 0.9375rem; font-variant-numeric: tabular-nums; }
.dep__to { font-size: 0.9375rem; }
.dep__calls { display: block; color: var(--board-dim); font-size: 0.75rem; margin-top: 0.1rem; }
.dep__status { font-family: var(--mono); font-size: 0.75rem; color: var(--board-good); text-align: right; }
.dep__status--late { color: var(--board-warn); }
.dep__status--off { color: var(--board-warn); }

.departures__hail {
  margin: 0.7rem 0 0;
  padding-top: 0.65rem;
  border-top: 1px solid rgba(232, 176, 75, 0.35);
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--board-warn);
  max-width: none;
}

.departures__none {
  color: var(--board-dim);
  font-size: 0.9375rem;
  margin: 0;
  max-width: none;
}

/* ---- fact rows ---------------------------------------------------------- */

.facts { margin: 0; }
.facts div {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem 0.75rem;
  align-items: baseline;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--line-soft);
}
.facts div:first-child { padding-top: 0; }
.facts div:last-child { border-bottom: 0; padding-bottom: 0; }
.facts dt { font-size: 0.9375rem; color: var(--ink-2); }
.facts dt small { display: block; color: var(--ink-3); font-size: 0.75rem; }
.facts dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.pill {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.1rem 0.45rem;
  white-space: nowrap;
}
.pill--ok { background: var(--field-soft); color: var(--field); }
.pill--todo { background: var(--route-soft); color: var(--route); }
.pill--bad { background: var(--flood-soft); color: var(--flood); }
.pill--info { background: var(--water-soft); color: var(--water); }

/* ---- events ------------------------------------------------------------- */

.events { list-style: none; margin: 0; padding: 0; }
.events li {
  display: grid;
  grid-template-columns: 2.6rem 1fr;
  gap: 0.85rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--line-soft);
  max-width: none;
}
.events li:last-child { border-bottom: 0; }
.event__when {
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-3);
  text-align: center;
  margin: 0;
  line-height: 1.25;
}
.event__day { display: block; font-size: 1.15rem; color: var(--ink); letter-spacing: 0; }
.event__what { font-family: var(--sans); font-size: 0.9375rem; font-weight: 600; margin: 0; }
.event__where { font-size: 0.8125rem; color: var(--ink-3); margin: 0.1rem 0 0; }

/* ---- pairs and tables --------------------------------------------------- */

.pairs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin: 0 0 1rem;
  max-width: 46rem;
  border-top: 1px solid var(--line);
}
.pairs dt {
  font-weight: 600;
  font-size: 0.9375rem;
  padding: 0.6rem 0 0;
}
.pairs dd {
  margin: 0;
  padding: 0 0 0.6rem;
  color: var(--ink-2);
  font-size: 0.9375rem;
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 34rem) {
  .pairs { grid-template-columns: 13rem 1fr; }
  .pairs dt { padding: 0.6rem 1rem 0.6rem 0; border-bottom: 1px solid var(--line-soft); }
  .pairs dd { padding: 0.6rem 0; }
}
.tel { white-space: nowrap; }

.scroller { overflow-x: auto; margin: 0 0 1rem; }


.grid {
  border-collapse: collapse;
  width: 100%;
  min-width: 30rem;
  font-size: 0.9375rem;
  background: var(--surface);
  border: 1px solid var(--line);
}


.grid th, .grid td {
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
}
.grid thead th {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.grid tbody th { font-weight: 600; white-space: nowrap; }
.grid tbody tr:last-child th, .grid tbody tr:last-child td { border-bottom: 0; }
.grid td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* A figure coloured by what it means, the same way the tiles colour a river
 * that is up or a train that is cancelled. Two vocabularies meeting here: what
 * it is like outside, and how much notice to take. Both land on the same three
 * colours, which is the point of having only three.
 *
 * Colour is never the only signal. Every one of these rows says the same thing
 * in the column beside it, in words, because a reader who cannot tell these
 * apart still has to be able to read the board. */
.grid td.num--icy,
.grid td.num--cold   { color: var(--water); }
.grid td.num--warm,
.grid td.num--raised { color: var(--route); }
.grid td.num--hot,
.grid td.num--high   { color: var(--flood); font-weight: 600; }

/* ---- prose blocks ------------------------------------------------------- */

.prose > * + * { margin-top: 1rem; }
.prose h2 { margin-top: 2.25rem; }
.prose h3 { margin-top: 1.75rem; }
.prose ul, .prose ol { padding-left: 1.35rem; }
.prose li + li { margin-top: 0.35rem; }

.cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem 2.5rem;
}
@media (min-width: 50rem) {
  .cols { grid-template-columns: 1fr 1fr; }
}

.steps { list-style: none; margin: 1rem 0 0; padding: 0; border-top: 1px solid var(--line); }
.steps li {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: 0.9rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line-soft);
  max-width: 46rem;
}
.steps li::before {
  content: counter(step);
  counter-increment: step;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-3);
  padding-top: 0.15rem;
}
.steps { counter-reset: step; }

/* ---- the list at the apex ------------------------------------------------
 *
 * A way in rather than a front page. Each entry says what the village is in
 * one line read from its own file, because a name on its own does not tell
 * somebody whether they have found the Thornford they meant.
 */

.index { padding: clamp(1.5rem, 4vw, 2.5rem) 0 0; }
.index h1 { margin: 0 0 0.75rem; }
.index__lead {
  font-size: 1.1875rem;
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 60ch;
  margin: 0 0 0.75rem;
}
.index__count {
  color: var(--ink-3);
  max-width: 60ch;
  margin: 0 0 1.75rem;
  font-size: 0.9375rem;
}

.index__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.85rem;
}
@media (min-width: 40rem) {
  .index__list { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 64rem) {
  .index__list { grid-template-columns: repeat(3, 1fr); }
}

.index__item { margin: 0; max-width: none; }

/* The card is the link rather than the name inside it, so the whole thing is
 * the target. On a phone that is the difference between a list you can use and
 * one you have to aim at. */
.index__card {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  height: 100%;
  padding: 0.95rem 1rem 1.05rem;
  border: 1px solid var(--line-soft);
  border-top: 3px solid var(--field);
  border-radius: 6px;
  background: var(--surface);
  text-decoration: none;
  color: inherit;
}
.index__card:hover { background: var(--wash-field); }
.index__card:hover .index__name { text-decoration: underline; }
.index__card:focus-visible { outline: 3px solid var(--water); outline-offset: 2px; }

.index__name {
  font-family: var(--serif);
  font-size: 1.25rem;
  line-height: 1.15;
  color: var(--ink);
}
.index__county {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* The sentence about the village itself, which is the thing somebody choosing
 * between twelve cards is actually reading. It gets the reading size and the
 * darker ink; the arithmetic underneath it gets neither. */
.index__about {
  margin-top: 0.5rem;
  flex: 0 0 auto;
  color: var(--ink-2);
  font-size: 1rem;
  line-height: 1.5;
}
.index__facts {
  margin-top: 0.5rem;
  flex: 0 0 auto;
  color: var(--ink-3);
  font-size: 0.875rem;
  line-height: 1.45;
}
/* The station on its own line, so a column of cards can be scanned for it.
 * Takes the railway colour, the way everything else about the railway on this
 * site does. */
.index__station {
  margin-top: 0.6rem;
  font-size: 0.875rem;
  color: var(--route);
}
.index__station::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  margin-right: 0.45rem;
  border-radius: 50%;
  background: var(--route);
  vertical-align: 0.05rem;
}

.index__where {
  margin-top: auto;
  padding-top: 0.7rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-3);
}

.index__note {
  margin: 1.75rem 0 0;
  color: var(--ink-3);
  font-size: 0.9375rem;
  max-width: 60ch;
}

/* ---- the line ------------------------------------------------------------
 *
 * A railway drawn the way it is drawn in a carriage: a strip down the page
 * with the stations on it and the minutes in the gaps. It answers how these
 * villages are joined without a sentence, which is the point of the page.
 *
 * Twenty two stations is a tall thing, so the work here is mostly about
 * making it scannable rather than decorative: the rail thin and continuous,
 * the minutes small enough to skip, and our four villages banded so the eye
 * lands on the part of the line the reader actually lives on.
 */

.strip {
  margin: 2rem 0 2.5rem;
  max-width: 34rem;
}

.strip__end {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0;
  padding-left: 2.4rem;
  max-width: none;
}

.strip__list { list-style: none; margin: 0.5rem 0; padding: 0; }
.strip__list li { max-width: none; }

/* The rail, drawn once as a border and shared by every row so it is one
 * continuous line rather than a stack of segments that never quite meet. */
.strip__stop,
.strip__gap {
  position: relative;
  padding-left: 2.4rem;
  margin-left: 0.5rem;
  border-left: 2px solid var(--route);
}

.strip__stop {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  border-radius: 0 5px 5px 0;
}

.strip__stop::before {
  content: "";
  position: absolute;
  left: -0.44rem;
  top: 0.95rem;
  width: 0.66rem;
  height: 0.66rem;
  border-radius: 50%;
  background: var(--sheet);
  border: 2px solid var(--route);
}

/* One of ours. A filled mark and a band across the row, because a village on
 * this list is looking for the four stations it might actually use and a
 * hollow circle against a filled one is not enough to find at a glance. */
.strip__stop--ours {
  background: var(--wash-route);
  padding-right: 0.7rem;
}
.strip__stop--ours::before {
  background: var(--route);
  width: 0.85rem;
  height: 0.85rem;
  left: -0.53rem;
  top: 0.88rem;
}
.strip__stop--ours .strip__name { font-weight: 600; }

/* The station somebody arrived here to look at, from their own board. */
.strip__stop:target { outline: 2px solid var(--water); outline-offset: 2px; }

.strip__name { font-size: 1.0625rem; line-height: 1.3; }

.strip__code {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  color: var(--ink-3);
  margin-left: 0.5rem;
  vertical-align: 0.05rem;
}

.strip__note {
  display: block;
  font-size: 0.8125rem;
  color: var(--route);
  margin-top: 0.1rem;
}

/* The minutes, small and set back, so a reader going from one end to the other
 * steps over them and a reader counting a journey can still find them. */
.strip__gap {
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}
.strip__gap span {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: var(--ink-3);
}

/* ---- footer ------------------------------------------------------------- */

.site-foot {
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding: clamp(2rem, 4vw, 2.75rem) 0 3rem;
  font-size: 0.9375rem;
}
/* The two link lists sit side by side even on a phone, where one column made
 * the footer taller than the screen. Get in touch takes the full width under
 * them until there is room for a third column. Same shape as chetnole.com. */
.site-foot__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.75rem 1.5rem;
}
.site-foot__who { grid-column: 1 / -1; }
@media (min-width: 46rem) {
  .site-foot__grid { grid-template-columns: 1fr 1.4fr 1.2fr; column-gap: 2.5rem; }
  .site-foot__who { grid-column: auto; }
}
.site-foot h2 {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 0.6rem;
}
.site-foot ul { list-style: none; margin: 0; padding: 0; }
.site-foot li + li { margin-top: 0.3rem; }
.site-foot__who p { color: var(--ink-2); }
.site-foot__who p:last-child { margin-bottom: 0; }
.site-foot__legal {
  margin-top: 1.75rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  color: var(--ink-3);
  font-size: 0.8125rem;
}
.site-foot__legal p { margin: 0 0 0.5rem; max-width: 80ch; }
.site-foot__legal p:last-child { margin-bottom: 0; }
.site-foot__legal a { color: var(--ink-2); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---- colour rails -------------------------------------------------------
 *
 * Each panel on the board carries a hairline of its own colour along the top,
 * following the Ordnance Survey habit of colouring a thing by what it is:
 * water blue for the river, the route orange for the railway, green for the
 * everyday village business. It is the only colour on the board that is not
 * either a photograph or a status, and it earns its place by telling the eye
 * which panel it has landed on before it has read the heading.
 */

.panel { border-top-width: 3px; border-top-style: solid; }
.panel--trains  { border-top-color: var(--route); }
.panel--river   { border-top-color: var(--water); }
.panel--village { border-top-color: var(--field); }
.panel--diary   { border-top-color: var(--ink-3); }
.panel--inn     { border-top-color: var(--ink-3); }

/* ---- the river gauge ---------------------------------------------------- */

.gauge { margin: 0 0 0.9rem; }
.gauge__track {
  position: relative;
  height: 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  overflow: hidden;
}
.gauge__fill { position: absolute; inset: 0 auto 0 0; background: var(--water); }
.gauge__fill--raised { background: var(--route); }
.gauge__fill--high { background: var(--flood); }
.gauge__mark {
  position: absolute;
  top: -3px; bottom: -3px;
  width: 2px;
  background: var(--ink);
  transform: translateX(-1px);
}
.gauge__key {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 0.35rem 0 0;
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: var(--ink-3);
  max-width: none;
}
.gauge__key span:last-child { text-align: right; }

/* ---- photographs --------------------------------------------------------
 *
 * Every picture here belongs to the photographer who took it and is used
 * under Creative Commons Attribution ShareAlike. The credit is part of the
 * picture, not a footnote, which is why it sits in the figure and not at the
 * bottom of the page.
 */

.shot { margin: 0 0 1.25rem; }
.shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  background: var(--surface-2);
}
.shot--wide img { aspect-ratio: 16 / 7; object-fit: cover; }
.shot figcaption {
  margin-top: 0.4rem;
  font-size: 0.75rem;
  color: var(--ink-3);
  line-height: 1.45;
}
.shot figcaption a { color: var(--ink-3); }
.shot figcaption a:hover { color: var(--water); }

/* The masthead picture on the home page. Sized to what it holds rather than
 * to the viewport, so the board is still the first thing on screen. */
.hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem 2rem;
  align-items: end;
}
@media (min-width: 52rem) {
  .hero { grid-template-columns: 1.05fr 0.95fr; }
  .hero .shot { margin-bottom: 0; }
  .hero--flip .shot { order: -1; }
}
.hero .shot img { aspect-ratio: 4 / 3; object-fit: cover; }

/* ---- the theme switch --------------------------------------------------- */

.themer {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  padding: 0.3rem 0.7rem;
  cursor: pointer;
  line-height: 1.4;
}
.themer:hover { border-color: var(--ink-3); color: var(--ink); }
.site-head__inner { position: relative; }
.site-head__tools { display: flex; align-items: center; gap: 0.75rem; }

/* The list drops the name and the "All parishes" link, because on the list
 * both of those point at the list. That leaves the theme button on its own,
 * and a lone flex item goes to the start unless it is told otherwise. */
.site-head__inner > .site-head__tools:only-child {
  margin-left: auto;
  justify-content: flex-end;
}

/* ---- a little more colour in the furniture ------------------------------ */

.band--field { background: var(--field-soft); border-bottom-color: var(--field); }
.band--field .lead, .band--field p { color: var(--ink); }

.eyebrow--route { color: var(--route); }
.eyebrow--water { color: var(--water); }
.eyebrow--field { color: var(--field); }

.rule {
  height: 3px;
  border: 0;
  margin: 0 0 1.25rem;
  background: linear-gradient(
    to right,
    var(--route) 0 22%,
    var(--field) 22% 44%,
    var(--water) 44% 100%
  );
}

/* Width and offset steps for the river gauge. The content security policy
 * forbids inline styles, so the bar is drawn with classes instead of a style
 * attribute. Five percent steps, which is finer than the bar can show. */
.w0 { width: 0%; }
.w5 { width: 5%; }
.w10 { width: 10%; }
.w15 { width: 15%; }
.w20 { width: 20%; }
.w25 { width: 25%; }
.w30 { width: 30%; }
.w35 { width: 35%; }
.w40 { width: 40%; }
.w45 { width: 45%; }
.w50 { width: 50%; }
.w55 { width: 55%; }
.w60 { width: 60%; }
.w65 { width: 65%; }
.w70 { width: 70%; }
.w75 { width: 75%; }
.w80 { width: 80%; }
.w85 { width: 85%; }
.w90 { width: 90%; }
.w95 { width: 95%; }
.w100 { width: 100%; }
.l0 { left: 0%; }
.l5 { left: 5%; }
.l10 { left: 10%; }
.l15 { left: 15%; }
.l20 { left: 20%; }
.l25 { left: 25%; }
.l30 { left: 30%; }
.l35 { left: 35%; }
.l40 { left: 40%; }
.l45 { left: 45%; }
.l50 { left: 50%; }
.l55 { left: 55%; }
.l60 { left: 60%; }
.l65 { left: 65%; }
.l70 { left: 70%; }
.l75 { left: 75%; }
.l80 { left: 80%; }
.l85 { left: 85%; }
.l90 { left: 90%; }
.l95 { left: 95%; }
.l100 { left: 100%; }

/* ---- the what and where block -------------------------------------------
 *
 * Fills the column beside the photograph on the front page. A visitor who has
 * never heard of the place should be able to place it before they scroll.
 */

.summary { margin-top: 1.5rem; border-top: 1px solid var(--line); padding-top: 1rem; }
.summary__what { font-size: 1rem; color: var(--ink); max-width: 52ch; }
.summary__facts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin: 0 0 0.6rem;
  font-size: 0.875rem;
}
@media (min-width: 30rem) {
  .summary__facts { grid-template-columns: 8.5rem 1fr; }
}
.summary__facts dt {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 0.45rem 1rem 0 0;
}
.summary__facts dd {
  margin: 0;
  padding: 0.15rem 0 0.45rem;
  color: var(--ink-2);
  border-bottom: 1px solid var(--line-soft);
}
@media (min-width: 30rem) {
  .summary__facts dt { padding-top: 0.5rem; border-bottom: 1px solid var(--line-soft); }
  .summary__facts dd { padding-top: 0.5rem; }
}
.summary .source { margin-bottom: 0; }

/* ---- a warning band ------------------------------------------------------
 *
 * Shown only when the Environment Agency has something in force. A band that
 * appears every day saying all is well is a band nobody reads on the day it
 * matters.
 */

.warning {
  border: 1px solid var(--route);
  border-left-width: 5px;
  background: var(--route-soft);
  padding: 0.9rem 1.1rem;
  margin: 1.25rem 0 0;
}
.warning--severe { border-color: var(--flood); background: var(--flood-soft); }
.warning__head {
  font-weight: 700;
  margin: 0 0 0.4rem;
  color: var(--ink);
}
.warning ul { margin: 0 0 0.5rem; padding-left: 1.2rem; }
.warning li { color: var(--ink); font-size: 0.9375rem; }
.warning__foot { margin: 0; font-size: 0.875rem; color: var(--ink-2); }

.panel--weather { border-top-color: var(--water); }

/* ---- the history timeline ------------------------------------------------ */

.timeline { list-style: none; margin: 0 0 1rem; padding: 0; border-top: 1px solid var(--line); }
.timeline li {
  padding: 0.7rem 0 0.7rem 1.1rem;
  border-bottom: 1px solid var(--line-soft);
  border-left: 3px solid var(--line);
  max-width: 62ch;
  color: var(--ink-2);
}
.timeline li:hover { border-left-color: var(--route); }

/* The building survey: a name and its date on the left, what it is on the
 * right. The date belongs with the name because that is the pair a reader is
 * scanning for. */
.pairs--houses dt small {
  display: block;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 400;
  margin-top: 0.15rem;
}
@media (min-width: 34rem) {
  .pairs--houses { grid-template-columns: 15rem 1fr; }
}

/* ---- the dashboard -------------------------------------------------------
 *
 * Scanned rather than read, so the craft moves from typography to information
 * design. The figure is the biggest thing in each tile because on a dashboard
 * the number is the content and the words are labels. State is carried by a
 * coloured edge as well as by the value, so what needs attention reads before
 * anything has been understood.
 */

.band--dash { padding-top: 1.25rem; }

.dashbar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.4rem 1.5rem;
  padding: 0 0 0.85rem;
  border-bottom: 2px solid var(--ink);
  margin-bottom: 1.25rem;
}
.dashbar p { margin: 0; max-width: none; }
.dashbar__clock { font-size: 1rem; color: var(--ink-2); }
.dashbar__time {
  font-family: var(--mono);
  font-size: 1.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  letter-spacing: -0.02em;
  margin-right: 0.35rem;
}
.dashbar__age, .dashbar__refresh {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  overflow: hidden;
  margin-bottom: 1.5rem;
}

/* A tile that goes somewhere is a link, and has to look like the others until
 * it is touched. On a phone this is the whole navigation: the summary at the
 * top is already a list of what is below it, so tapping the one you care about
 * is the menu, and there is no menu to build. */
a.tile { text-decoration: none; color: inherit; display: block; }
a.tile:hover .tile__label span { text-decoration: underline; }
a.tile:focus-visible { outline: 3px solid var(--water); outline-offset: -3px; }

/* Far enough that a heading does not sit against the top edge of the window
 * after a jump. */
.dashcard[id] { scroll-margin-top: 1rem; }

.tile {
  background: var(--surface);
  padding: 0.85rem 0.95rem 0.95rem;
  border-top: 3px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.tile--normal { border-top-color: var(--field); }
.tile--raised { border-top-color: var(--route); }
.tile--high   { border-top-color: var(--flood); }
.tile--off    { border-top-color: var(--line); }

.tile p { margin: 0; max-width: none; }
.tile__label {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.tile__value {
  font-family: var(--mono);
  font-size: 1.9rem;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin-top: 0.2rem !important;
}
.tile__value small {
  font-size: 0.8125rem;
  letter-spacing: 0;
  color: var(--ink-3);
}
.tile__none { font-size: 0.9375rem; color: var(--ink-3); letter-spacing: 0; }
.tile__sub { font-size: 0.75rem; color: var(--ink-2); line-height: 1.3; margin-top: 0.25rem !important; }
.tile--high .tile__value { color: var(--flood); }
.tile--raised .tile__value { color: var(--route); }

.dashgrid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 46rem) {
  .dashgrid { grid-template-columns: 1fr 1fr; }
  .dashcard--wide { grid-column: 1 / -1; }
}
@media (min-width: 78rem) {
  .dashgrid { grid-template-columns: repeat(3, 1fr); }
}

.dashcard {
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  background: var(--surface);
  padding: 1.1rem 1.15rem 1.15rem;

  /* A grid item is as wide as its widest content unless it is told otherwise,
   * and the tables inside these cards have a min-width so their columns stay
   * readable. So the departures card grew to 518px inside a 375px phone, the
   * scroller it sits in never had anything to scroll, and the whole page slid
   * sideways. This is the line that lets the scroller do its job. */
  min-width: 0;
}
.dashcard h2 {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 0.6rem;
}
/* Which station the departures below are read from, code and all. It sits
   under the heading rather than in it, because the heading is a label in small
   caps and a three letter code set in small caps reads as another word. */
/* The line, beside the station it belongs to, in the railway colour. */
.dashcard__line { white-space: nowrap; }
.dashcard__line a { color: var(--route); }

.dashcard__where {
  margin: -0.35rem 0 0.6rem;
  font-size: 0.8125rem;
  color: var(--ink-2);
}

/* A second heading inside a card, for the rainfall gauges under the river.
   Set like the card heading so the two read as one hierarchy, a step smaller
   so it does not compete with it. */
.dashcard .subhead {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 1rem 0 0.5rem;
}

.dashcard .scroller, .dashcard .grid { margin-bottom: 0; }

/* A table in a card is as wide as the card, at every screen size.
 *
 * The minimum width on .grid is for a table that has the page to itself. A
 * dashboard card never does: it is one of two or three columns, so it is about
 * 340px on a tablet, 390px at 1280 and 480px only past about 1700. The tables
 * were asking for 480px inside all of those, so seven of the eight had a
 * scrollbar and a cut off last column on every screen anybody actually uses.
 *
 * That last column is the one worth reading: whether today is warmer than
 * usual, whether the train is on time. So the card wins and the table fits. */
.dashcard .grid { min-width: 0; }
.dashcard .grid thead th, .dashcard .grid tbody th { white-space: normal; }
.dashcard .grid { border: 0; }
.dashcard .grid th, .dashcard .grid td { padding: 0.4rem 0.6rem 0.4rem 0; }
.dashcard .note { margin-bottom: 0; }

/* ---- icons ---------------------------------------------------------------
 *
 * Inline SVG, one stroke weight, taking their colour from the text beside
 * them. Decoration only: every one has a real label next to it, which is why
 * they are hidden from the accessibility tree rather than given a name.
 */

.ico {
  width: 1em;
  height: 1em;
  flex: none;
  vertical-align: -0.12em;
}

/* ---- tile colour ---------------------------------------------------------
 *
 * Two jobs, kept apart. The hue says what a tile is about, the way a map
 * colours a thing by what it is. The state says whether it needs attention,
 * and state wins: on the day the river is up, the last thing that tile should
 * be is tastefully blue.
 */

.tile { position: relative; }
.tile__label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.625rem;
}
.tile__label .ico { width: 0.95rem; height: 0.95rem; opacity: 0.9; }

.tile--recycling { --tile-hue: var(--field); }
.tile--rubbish   { --tile-hue: var(--ink-3); }
.tile--water  { --tile-hue: var(--water); }
.tile--sky    { --tile-hue: var(--water); }
.tile--route  { --tile-hue: var(--route); }
.tile--field  { --tile-hue: var(--field); }
.tile--spark  { --tile-hue: var(--ink-3); }
.tile--ink    { --tile-hue: var(--ink-3); }

.tile { border-top-color: var(--tile-hue, var(--line)); }
.tile__label .ico { color: var(--tile-hue, var(--ink-3)); }

/* State overrides the hue, for the edge and the icon both. */
.tile--raised { border-top-color: var(--route); }
.tile--high   { border-top-color: var(--flood); }
.tile--off    { border-top-color: var(--line); }
.tile--raised .tile__label .ico { color: var(--route); }
.tile--high .tile__label .ico   { color: var(--flood); }
.tile--off .tile__label .ico    { color: var(--ink-3); }

.dashcard h2 { display: flex; align-items: center; gap: 0.4rem; }

/* ---- navigation ----------------------------------------------------------
 *
 * Nine destinations is more than a row of plain links carries. They become
 * pills: bigger targets, a clear current state, and they wrap into tidy rows
 * on a phone instead of a ragged line.
 */

.site-head nav ul { gap: 0.35rem; }
.site-head nav a {
  padding: 0.3rem 0.7rem;
  border: 1px solid transparent;
  border-bottom-width: 1px;
  border-radius: 2px;
  font-size: 0.875rem;
  color: var(--ink-2);
}
.site-head nav a:hover {
  color: var(--ink);
  background: var(--surface-2);
  border-color: var(--line);
}
.site-head nav a[aria-current="page"] {
  color: var(--surface);
  background: var(--ink);
  border-color: var(--ink);
}

@media (max-width: 45rem) {
  .site-head__inner { align-items: flex-start; }
  .site-head__tools { width: 100%; justify-content: space-between; align-items: flex-start; }
  .site-head__inner > .site-head__tools:only-child { justify-content: flex-end; }
  .site-head nav ul { gap: 0.3rem; }
}

/* A quiet footer link row, so the foot of a long page is also a way out. */
.site-foot ul a { text-decoration-thickness: 1px; }

/* ---- coloured tiles ------------------------------------------------------
 *
 * The box itself carries the colour now, not only its top edge. Each family
 * gets a wash of its own hue at a strength that still passes contrast for the
 * figure sitting on it, and the state washes override where something needs
 * attention. Dark mode redefines the washes rather than reusing them, because
 * a tint that reads as a whisper on paper reads as a shout on a dark ground.
 */

:root {
  /* Family tints, and they are deliberately about half the strength of the
   * status colours below them. A tile belongs to a family always and is a
   * warning rarely, so the family has to be quiet enough that the eye does not
   * read it as one. The old ones were not: wash-route was #F6E6DB against a
   * raised state of #F3DFD2, near enough identical, so every trains tile
   * looked like a train running late. */
  --wash-water: #EEF4F7;
  --wash-route: #FBF2EC;
  --wash-field: #F1F5EB;
  --wash-ink:   #F2F3EE;
}

:root[data-theme="dark"] {
  --wash-water: #141F24;
  --wash-route: #211913;
  --wash-field: #171E14;
  --wash-ink:   #1A1F19;
}

/* A tile is washed by what it is telling you, never by what it is about.
 *
 * It used to be washed by family: everything to do with the railway took the
 * route colour, everything to do with power took it too. Which meant a tile
 * saying every train is running to time was the same warm orange as a tile
 * saying one is late, and a tile saying there is no power cut anywhere near
 * you was orange as well. Good news painted as a warning, on the two tiles
 * people look at first.
 *
 * So the wash is status now and only status. The family still shows, in the
 * rail along the top, in the icon and in the label, which is three ways of
 * saying what a tile is about without any of them claiming something is wrong.
 */
/* Family first, and always. Every tile carries the colour of the thing it is
 * about, at a strength that says which group it belongs to and nothing more. */
.tile--water, .tile--sky { background: var(--wash-water); }
.tile--route { background: var(--wash-route); }

/* Power has no colour of its own in this scheme, and should not borrow the
 * railway's. A tile saying there is no power cut anywhere near you had a warm
 * tint for no better reason than that orange was the nearest spare colour, and
 * warm reads as something to attend to. It takes the neutral now, and turns
 * orange or red only when there is actually a fault. */
.tile--spark { background: var(--wash-ink); }
.tile--field { background: var(--wash-field); }
.tile--ink   { background: var(--wash-ink); }

/* Status over the top, and it is a different kind of thing rather than a
 * stronger version of the same thing. The tint goes up to the full soft
 * colour, the rail thickens, and the figure itself changes colour. Three
 * signals at once, which is what makes a warning look like a warning next to
 * seven tiles that are merely blue or green.
 *
 * The tints and the icon are set where the state rules are, further up. Only
 * the weight is new, and it is here so that the family backgrounds above
 * cannot quietly win by coming later in the file. */
.tile--raised { background: var(--route-soft); }
.tile--high   { background: var(--flood-soft); }
.tile--off    { background: var(--surface-2); }
.tile--raised, .tile--high { border-top-width: 5px; }

/* The one exception, because here the colour is the information rather than a
 * decoration: which bin goes out. Dorset's recycling bin is the green one and
 * the rubbish bin is not, so the tile is the colour of the bin you are being
 * told to put out. */
.tile--recycling { background: var(--wash-field); }
.tile--rubbish   { background: var(--wash-ink); }

/* The label and the figure both take the family colour, which is what makes a
 * tile read as one object rather than a grey box with a coloured stripe. */
.tile--water .tile__label span, .tile--sky .tile__label span { color: var(--water); }
.tile--route .tile__label span { color: var(--route); }
.tile--spark .tile__label span { color: var(--ink-2); }
.tile--field .tile__label span,
.tile--recycling .tile__label span { color: var(--field); }
.tile--rubbish .tile__label span { color: var(--ink-2); }
.tile--raised .tile__label span { color: var(--route); }
.tile--high .tile__label span { color: var(--flood); }

/* The detail cards take the same treatment at a lighter weight, so the page
 * reads as one system rather than as tiles plus tables. */
/* The cards take the same treatment as the tiles, at the same strength, so
 * that a card and the tile that jumps to it are recognisably the same thing.
 * Against the page ground this is what makes a card read as an object rather
 * than as a ruled box drawn on the paper. */
.dashcard { border-top: 3px solid var(--line); }
.dashcard--trains  { border-top-color: var(--route); background: var(--wash-route); }
.dashcard--weather { border-top-color: var(--water); background: var(--wash-water); }
.dashcard--bins    { border-top-color: var(--field); background: var(--wash-field); }
.dashcard--night   { border-top-color: var(--water); background: var(--wash-water); }
.dashcard--river   { background: var(--wash-water); }
/* The standing facts are the one card that is neither a reading nor a date, so
 * they take the neutral colour rather than borrowing one that means water or
 * rail or field. */
.dashcard--who     { border-top-color: var(--ink-3); background: var(--wash-ink); }.dashcard--feeds   { border-top-color: var(--ink-3); }
/* Power takes the same neutral as its tile, which is spark. */
.dashcard--power   { border-top-color: var(--ink-3); background: var(--wash-ink); }
.dashcard--trains h2 .ico  { color: var(--route); }
.dashcard--weather h2 .ico { color: var(--water); }
.dashcard--bins h2 .ico    { color: var(--field); }
.dashcard--who h2 .ico     { color: var(--ink-3); }
.dashcard--power h2 .ico   { color: var(--ink-3); }
.dashcard--night h2 .ico   { color: var(--water); }

/* The Realtime Trains credit. Required by their token terms to be clearly
 * visible wherever their data is used, so it is sized to be read rather than
 * tucked away, and it sits inside the departure board where the data is. */
.credit {
  margin: 0.7rem 0 0;
  padding-top: 0.6rem;
  border-top: 1px solid var(--board-rule);
  font-size: 0.8125rem;
  color: var(--board-dim);
  max-width: none;
}
.credit a { color: var(--board-warn); }

.dashcard .credit {
  border-top: 1px solid var(--line-soft);
  color: var(--ink-2);
}
.dashcard .credit a { color: var(--water); }

/* ---- village street maps --------------------------------------------------
 *
 * The map is an SVG drawn by bin/village-map.mjs. Its presentation attributes
 * are the light colours, for when map.svg is opened on its own; these rules
 * take over inside the page, so the map follows the theme. Classes rather than
 * inline styles, because the content security policy allows no inline style. */
:root {
  --map-road: #FFFFFF;
  --map-major: #F4E3B8;
}
:root[data-theme="dark"] {
  --map-road: #3A4238;
  --map-major: #5C5134;
}

/* As wide as the map, so a hamlet's small one does not sit in a page wide box. */
.villagemap {
  width: fit-content;
  max-width: 100%;
  margin: 1.5rem 0 2rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}
/* No bigger than it was drawn, so a narrow village is not blown up to fill a
 * wide screen; smaller when the screen is. */
.villagemap svg { display: block; max-width: 100%; height: auto; margin: 0 auto; }

/* On a phone, shrunk to fit, a street name is three pixels high. So there the
 * map keeps the size it was drawn at, and the box scrolls both ways. */
@media (max-width: 52rem) {
  .villagemap { overflow: auto; max-height: 80vh; }
  .villagemap svg { max-width: none; margin: 0; }
}

.vm .vm-paper, .vm .vm-rail-gap { fill: var(--surface); }
.vm .vm-rail-gap { fill: none; stroke: var(--surface); }
.vm .vm-wood { fill: var(--field-soft); }
.vm .vm-water { fill: var(--water-soft); }
.vm .vm-stream { stroke: var(--water); }
.vm .vm-building { fill: var(--line-soft); stroke: var(--line); }
.vm .vm-key { fill: var(--ink-2); }
.vm .vm-casing { stroke: var(--line); }
.vm .vm-road { stroke: var(--map-road); }
.vm .vm-major { stroke: var(--map-major); }
.vm .vm-rail { stroke: var(--ink-2); }
.vm .vm-grid { stroke: var(--line); }
.vm .vm-label { stroke: var(--surface); fill: var(--ink); }
.vm .vm-edge, .vm .vm-minor, .vm .vm-credit { fill: var(--ink-3); }
.vm .vm-station, .vm .vm-town, .vm .vm-town-arrow { fill: var(--route); }
.vm .vm-wood-label { fill: var(--field); }
.vm .vm-water-label { fill: var(--water); }
.vm .vm-legend { fill: var(--ink); }
.vm .vm-scale { stroke: var(--ink); }
.vm .vm-north { fill: var(--ink-3); }
.vm circle.vm-dot { fill: var(--ink-3); stroke: none; }
.vm circle.vm-station, .vm circle.vm-amenity { fill: var(--route); }
.vm .vm-number { fill: var(--ink); stroke: var(--surface); }
.vm .vm-number-text { fill: var(--surface); }

/* County maps: the same box as a street map, and the same trick for the dark
 * theme, a class on every shape overriding its light colour. A county map is
 * drawn small enough to fit, so it shrinks on a phone rather than scrolling. */
@media (max-width: 52rem) {
  .countymap { overflow: hidden; max-height: none; }
  .countymap svg { max-width: 100%; margin: 0 auto; }
}
.cm .cm-paper { fill: var(--surface); }
.cm .cm-rule { stroke: var(--line); }
.cm .cm-home { fill: var(--field-soft); stroke: var(--line); }
.cm .cm-other { fill: var(--surface-2); stroke: var(--line); }
.cm .cm-away { fill: var(--surface); stroke: var(--line); }
.cm .cm-edge { stroke: var(--ink-3); }
.cm .cm-box { stroke: var(--route); }
.cm .cm-dot { fill: var(--water); stroke: var(--surface); }
.cm .cm-dot--away { fill: var(--surface); stroke: var(--water); }
.cm .cm-label, .cm .cm-number { fill: var(--ink); stroke: var(--surface); }
.cm .cm-label--away { fill: var(--ink-2); }
.cm .cm-town { fill: var(--surface); stroke: var(--ink-2); }
.cm .cm-town-label { fill: var(--ink-2); stroke: var(--surface); }
.cm .cm-legend { fill: var(--ink); }
.cm .cm-scale { stroke: var(--ink); }
.cm .cm-north { fill: var(--ink-3); }
.cm a:hover .cm-label, .cm a:focus-visible .cm-label { text-decoration: underline; }
.countymap__villages { columns: 2 12rem; padding-left: 1.25rem; }

/* River maps: the same box again, with the layers the other way up. The
 * catchment is the subject and is tinted; the parishes are drawn over it as
 * outlines, darker where the parish has a board to follow. */
.cm .cm-water { fill: var(--water-soft); stroke: var(--water); }
.cm .cm-parish { stroke: var(--ink-3); }
.cm .cm-parish--board { stroke: var(--ink-2); }
.river__percent { font-family: var(--mono); font-size: 0.8125rem; color: var(--ink-3); }
.river__kind { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-3); }
.river__areas { padding-left: 1.25rem; }
.river__areas li { margin-bottom: 0.5rem; }
.river__areas p { margin: 0; color: var(--ink-2); font-size: 0.9375rem; }
.river__status {
  display: inline-block; font-family: var(--mono); font-size: 0.75rem;
  letter-spacing: 0.04em; padding: 0.05rem 0.4rem; white-space: nowrap;
}
.river__status--bad { background: var(--flood-soft); color: var(--flood); }
.river__status--mid { background: var(--route-soft); color: var(--route); }
.river__status--ok { background: var(--field-soft); color: var(--field); }

.villagemap__index {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}
@media (min-width: 52rem) {
  .villagemap__index { grid-template-columns: 1fr 1fr; align-items: start; }
}
/* The licence statements, set as the footer's legal lines are. */
.villagemap__credits {
  margin-top: 2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  color: var(--ink-3);
  font-size: 0.8125rem;
}
.villagemap__credits p { margin: 0 0 0.5rem; max-width: 80ch; }
.villagemap__credits p:last-child { margin-bottom: 0; }
.villagemap__credits a { color: var(--ink-2); }

.villagemap__index td:last-child { font-family: var(--mono); font-size: 0.9375rem; }
.villagemap__num {
  display: inline-block;
  min-width: 1.5em;
  padding: 0 0.35em;
  border-radius: 1em;
  background: var(--ink);
  color: var(--surface);
  font-family: var(--mono);
  font-size: 0.75em;
  text-align: center;
}

.dashcard--river { border-top-color: var(--water); }
.dashcard--river h2 .ico { color: var(--water); }
.dashcard--river .gauge { margin-bottom: 0.75rem; }

/* Eight tiles, and the count is deliberate: two even rows rather than whatever
 * auto-fit decides at a given width. Auto-fit was making five and three, which
 * leaves a hole and makes the second row look like an afterthought. */
.tiles { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 34rem) { .tiles { grid-template-columns: repeat(4, 1fr); } }
/* Eight tiles and eight columns, so the whole summary is one strip across the
 * top of a wide screen rather than two rows of four. */
@media (min-width: 78rem) { .tiles { grid-template-columns: repeat(8, 1fr); } }

/* A photograph that illustrates rather than opens. Half width and floated on a
 * wide screen, full width and out of the way on a phone: the page is about how
 * to stop a train, and the picture is there to show what the platform looks
 * like, not to be the first thing on it. */
.shot--inline { margin-bottom: 1.25rem; }
@media (min-width: 44rem) {
  .shot--inline {
    float: right;
    width: 19rem;
    margin: 0 0 1rem 1.75rem;
  }
  .shot--inline + h2 { margin-top: 0; }
}
.shot--inline img { aspect-ratio: 4 / 3; object-fit: cover; }

/* The picture at the head of the list.
 *
 * Wide and short, because it is a band across the top of a page rather than a
 * photograph somebody came to look at, and because a tall image on a phone
 * pushes the twelve villages below the fold, which is the one thing the page
 * exists to show. The aspect ratio is held by the box rather than the file, so
 * the layout does not move when the image finally arrives. */
/* The header at the top of the list.
 *
 * It carries the name and a strapline as part of the picture, which changes
 * the rules it has to follow. It cannot be cropped, because cropping it cuts
 * words off: the box therefore holds the picture's own shape at every width
 * and the picture fills it exactly, rather than being covered into a band of
 * whatever depth the design would like. On a phone that leaves the wordmark
 * small, which is the price of not cutting it in half.
 *
 * The ratio is held by the box so the page does not jump when the picture
 * arrives. */
.index__hero {
  margin: 0 0 1.5rem;
  border-radius: 8px;
  overflow: hidden;
  background: var(--wash-field);
}
.index__hero picture { display: block; }

/* No aspect ratio and no cropping.
 *
 * The picture has words in it, so cropping it cuts words in half. It sizes
 * itself from its own width and height attributes instead, which the build
 * reads from the file, so the space is held open before it loads and the two
 * cuts can be different shapes without this stylesheet knowing either. */
.index__heroImage {
  display: block;
  width: 100%;
  height: auto;
}

@media (max-width: 40rem) {
  .index__hero { margin-bottom: 1.25rem; }
}

/* On a phone, a table that has the page to itself fits rather than scrolls.
 *
 * The minimum width on .grid stops columns collapsing to one word per line,
 * which is right on a desktop and wrong on a 375px screen, where it means the
 * table has to be dragged sideways to read its last column.
 *
 * The feeds panel is the one table anywhere that still does not fit on a
 * phone, and it is left to scroll inside its own box: five columns of five
 * different things need about 380px however the spacing is tuned, and it is
 * the one table here whose whole job is to show everything, so nothing is
 * dropped from it to make it narrower. */
@media (max-width: 40rem) {
  .grid { min-width: 0; }
  .grid thead th, .grid tbody th { white-space: normal; }
}

/* The status page: sixteen villages by nine feeds.
 *
 * A grid of marks rather than words, because the question it answers is "is
 * anything wrong" and a wall of the word "current" answers it slowly. The mark
 * is decoration and the word is in the cell for a screen reader. */
.status {
  /* Nine feeds of equal importance get nine columns of equal width, so the eye
   * can run down one without the headings above deciding how wide it is. The
   * village name takes what it needs and the rest is divided. */
  table-layout: fixed;
}
.status th:first-child { width: 8.5rem; }
.status th:last-child { width: 3rem; }
.status thead th {
  font-size: 0.625rem;
  text-align: center;
  vertical-align: bottom;
  padding: 0 0.2rem 0.5rem;
  white-space: normal;

  /* A fixed column is narrower than one word of a mono heading, and a word
   * that cannot wrap spills over the heading beside it. */
  overflow-wrap: anywhere;
  letter-spacing: 0.06em;
}
.status thead th:first-child { text-align: left; }
.status tbody th { white-space: normal; font-weight: 600; }
.status th, .status td { padding-left: 0.2rem; padding-right: 0.2rem; }

/* .grid th, .grid td set text-align left and outrank a single class, so the
 * marks sat left in their columns while the headings above them were centred.
 * The table name goes on the front to win it back. */
.status .status__cell,
.status tbody td {
  text-align: center;
}

.status__cell {

  /* Big enough to read at a glance and to tell a ring from a disc without
   * leaning in, which is the whole job of this page. */
  font-size: 1.375rem;
  line-height: 1.1;

  /* Each cell carries the state as a word for a screen reader, and
   * visually-hidden positions that word absolutely. With nothing positioned
   * between it and the page, it is laid out against the page rather than the
   * cell, escapes the scrolling box the table sits in, and drags the whole
   * document sideways on a phone. This is the ancestor it needed. */
  position: relative;
}
.status__cell--live { color: var(--good, #2e7d32); }
.status__cell--stale { color: var(--warn, #b26a00); }
.status__cell--failing { color: var(--bad, #b3261e); }
.status__cell--off,
.status__cell--missing { color: var(--ink-3); }

/* A row of sixteen is easier to follow across if alternate ones are shaded. */
.status tbody tr:nth-child(even) { background: var(--wash-field); }

/* On a phone the fixed columns would be three characters wide, so the table
 * keeps a width it can be read at and scrolls inside its box instead. */
@media (max-width: 52rem) {
  /* Wide enough for "Timetable" and "Warnings" to stay whole words when the
   * hybrid adds a tenth column. */
  .status { min-width: 54rem; }
  .status th:first-child { width: 7.5rem; }
}
