:root {
  color-scheme: light dark;
  --paper: #f6f5f0;
  --ink: #24251f;
  --muted: #626458;
  --rule: #d2d3c8;
  /* Kit colours from the race photo: Doncaster green shorts, yellow singlet.
     Green carries text; yellow is for marks and highlights, since it is too
     pale to read on paper. */
  --accent: #176b50;
  --yellow: #f2c42b;
  /* Deepens the photo's colours against paper; cleared in dark mode. */
  --tint: rgba(36, 37, 31, 0.45);
  --mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
  font-family: Arial, Helvetica, sans-serif;
  color: var(--ink);
  background: var(--paper);
}

* { box-sizing: border-box; }

body {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 16px;
  font-size: 1rem;
  line-height: 1.6;
  overflow-wrap: anywhere;
}

a { color: inherit; text-underline-offset: 0.2em; }
a:hover { color: var(--accent); }
a:focus-visible { outline: 2px solid var(--accent); outline-offset: 5px; }

h1, h2, h3, p { margin: 0; }
h1, h2, h3 { line-height: 1.08; }
h1 { font-size: clamp(3.5rem, 8vw, 7rem); letter-spacing: -0.065em; color: var(--accent); }
h2, h3 { font-size: clamp(1.5rem, 3vw, 2rem); letter-spacing: -0.035em; }

::selection { background: var(--yellow); color: #24251f; }

.skip-link {
  position: absolute;
  top: 8px;
  left: 16px;
  padding: 8px 16px;
  transform: translateY(-200%);
  background: var(--paper);
  z-index: 1;
}
.skip-link:focus { transform: translateY(0); }

.site-header, .site-footer, nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.site-header {
  min-height: 88px;
  border-bottom: 1px solid var(--rule);
}
.wordmark {
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -0.07em;
  text-decoration: none;
}
.wordmark { color: var(--accent); }
.wordmark span { color: var(--yellow); }
nav, .site-footer, figcaption, .entry-number, .eyebrow, .group-label {
  font-family: var(--mono);
  font-size: 0.8125rem;
}
nav a { padding: 10px 0; }
nav a[aria-current="page"] { text-decoration-thickness: 2px; text-decoration-color: var(--yellow); }

.hero { padding: 48px 0; }
.intro { min-width: 0; }
.bio {
  max-width: 38ch;
  margin-top: 24px;
  font-size: 1.1875rem;
  line-height: 1.55;
  text-wrap: pretty;
}
.race { min-width: 0; margin: 40px 0 0; }

/* ASCII portrait. Four static variants; the stylesheet picks one by width and
   colour scheme. Size scales with this column, not the viewport. */
.ascii-frame {
  container-type: inline-size;
}
.ascii {
  display: none;
  width: fit-content;
  margin: 0;
  font-family: "Courier New", "Liberation Mono", Courier, monospace;
  font-weight: 700;
  /* 58 columns at 0.6em per glyph, with a little rounding headroom. */
  font-size: calc((100vw - 32px) / 35);
  font-size: 2.86cqi;
  line-height: 1;
  letter-spacing: 0;
  white-space: pre;
  color: var(--ink);
}
.ascii--narrow.ascii--light { display: block; }

/* Colour. main.js clones each portrait into a colour layer laid over the ink
   one, with the photo clipped to the glyphs, and shows it through a soft
   radial mask: a lens that follows the cursor or finger and fades out to ink
   at its edge. main.js eases --x, --y (centre) and --d (diameter) in px; a
   click grows the lens past the corners. At --d: 0 the mask hides the whole
   layer, so any failure leaves plain ink. Without JS the portrait is plain
   ink; with reduced motion it is static full colour. */
.ascii-frame { position: relative; -webkit-tap-highlight-color: transparent; }
.ascii--colour {
  --lens: radial-gradient(closest-side, #000 30%, rgba(0, 0, 0, 0.82) 46%, rgba(0, 0, 0, 0.5) 64%, rgba(0, 0, 0, 0.2) 82%, transparent);
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  color: transparent;
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(var(--tint), var(--tint)), url("/ascii-crop.jpg");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-mask-image: var(--lens);
  mask-image: var(--lens);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: var(--d, 0px) var(--d, 0px);
  mask-size: var(--d, 0px) var(--d, 0px);
  -webkit-mask-position: calc(var(--x, 0px) - var(--d, 0px) / 2) calc(var(--y, 0px) - var(--d, 0px) / 2);
  mask-position: calc(var(--x, 0px) - var(--d, 0px) / 2) calc(var(--y, 0px) - var(--d, 0px) / 2);
}
@media (prefers-reduced-motion: reduce) {
  .ascii--colour { -webkit-mask-image: none; mask-image: none; }
}

/* Page lens. Behind all the text sits a field of kit greens,
   fixed to the page; a soft hole in each element's normal colour lets it
   through around the cursor or finger. main.js tags the text elements .ink,
   gives each its page offset (--ox, --oy) so every element's backgrounds line
   up as one page-sized sheet, and eases the hole's centre (--mx, --my, page
   px) and radius (--r) on the root. The glyphs are painted by the
   backgrounds; color itself is untouched, so underlines and the SVG arrows
   keep it. The radius never reaches 0px (iOS Safari paints a zero-radius
   radial gradient as transparent, which would show the field everywhere).
   Without JS, or with reduced motion, text is its plain colour. */
:root {
  --mx: -1000px;
  --my: -1000px;
  --r: 1px;
  --field: repeating-linear-gradient(115deg, #176b50 0, #2f8a3e 300px, #176b50 600px);
}
.ink {
  -webkit-text-fill-color: transparent;
  background-image:
    radial-gradient(circle var(--r) at calc(var(--mx) - var(--ox, 0px)) calc(var(--my) - var(--oy, 0px)),
      transparent 0%, transparent 28%,
      color-mix(in srgb, currentColor 35%, transparent) 55%,
      color-mix(in srgb, currentColor 75%, transparent) 80%,
      currentColor 100%),
    var(--field);
  background-size: 100% 100%, var(--pw, 100vw) var(--ph, 100vh);
  background-position: 0 0, calc(0px - var(--ox, 0px)) calc(0px - var(--oy, 0px));
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
}
.ink ::selection, .ink::selection { -webkit-text-fill-color: #24251f; }
.ink .flags { -webkit-text-fill-color: currentColor; } /* emoji keep their own colours */

figcaption {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
  color: var(--muted);
}
.entry {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  column-gap: 12px;
  row-gap: 12px;
  padding: 28px 0;
  border-top: 1px solid var(--rule);
}
.entry-number { padding-top: 3px; color: var(--accent); }
.entry > p, .entry-copy { grid-column: 2; color: var(--muted); }
.entry-copy p + p { margin-top: 16px; }
.entry-copy .org {
  display: inline-block;
  margin-bottom: 2px;
  color: var(--ink);
  font-weight: 700;
  text-decoration: none;
}
.entry-copy .org:hover { text-decoration: underline; text-decoration-thickness: 2px; text-decoration-color: var(--yellow); }
.entry h2 a, .entry h3 a { text-decoration: none; }
.entry h2 a:hover, .entry h3 a:hover { text-decoration: underline; text-decoration-thickness: 2px; text-decoration-color: var(--yellow); }

/* Arrows. Each is drawn pointing east; --true is the link's real direction,
   --rest a resting angle set per arrow in the markup, --a the tracked angle
   set by main.js. Hovering the link snaps it true with a small wobble. */
.arrow {
  display: inline-block;
  margin-left: 0.18em;
  vertical-align: 0.05em;
  --true: 0deg;
}
.arrow--out { --true: -45deg; }
.arrow svg {
  display: block;
  transform: rotate(var(--a, var(--rest, var(--true))));
  transition: transform 140ms ease-out;
}
a:hover .arrow svg, a:focus-visible .arrow svg {
  transform: rotate(var(--true));
  animation: wobble 520ms cubic-bezier(0.3, 0.8, 0.4, 1);
}
@keyframes wobble {
  0% { transform: rotate(calc(var(--true) + 22deg)); }
  40% { transform: rotate(calc(var(--true) - 12deg)); }
  70% { transform: rotate(calc(var(--true) + 5deg)); }
  100% { transform: rotate(var(--true)); }
}

.slash { color: var(--muted); font-weight: 400; }
.flags { display: inline-block; white-space: nowrap; }
.not-found { padding: 80px 0; min-height: 65vh; }
.eyebrow { margin-bottom: 24px; color: var(--muted); }
.group-label { margin: 0; padding: 40px 0 12px; color: var(--muted); letter-spacing: 0.08em; text-transform: uppercase; }
.group-label + .entry { border-top-color: var(--ink); }
.not-found h1 { max-width: 10ch; }
.back-link { display: inline-block; margin-top: 32px; padding: 8px 0; }
.site-footer {
  flex-wrap: wrap;
  border-top: 1px solid var(--rule);
  margin-top: 32px;
  padding: 24px 0 32px;
  color: var(--muted);
}

@media (min-width: 600px) {
  .ascii--narrow.ascii--light { display: none; }
  .ascii--wide.ascii--light {
    display: block;
    /* 76 columns at 0.6em per glyph. */
    font-size: 8px; /* Safe fallback for browsers without container units. */
    font-size: 2.18cqi;
  }
  .race { max-width: 480px; }
  .hero { padding: 64px 0; }
  .entry { column-gap: 24px; }
}

@media (min-width: 900px) {
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
    gap: 64px;
    align-items: center;
    padding: 72px 0;
  }
  .hero h1 { max-width: 7ch; }
  .bio { font-size: 1.25rem; }
  .race { max-width: none; margin: 0; }
  .entry {
    grid-template-columns: 24px minmax(0, 1fr) minmax(0, 1fr);
    align-items: baseline;
    padding: 32px 0;
  }
  .entry > p, .entry-copy { grid-column: 3; }
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #191b17;
    --ink: #eeeee5;
    --muted: #b0b3a5;
    --rule: #44483e;
    --accent: #72c9a2;
    --yellow: #fdd643;
    --tint: transparent;
    --field: repeating-linear-gradient(115deg, #72c9a2 0, #8fd48a 300px, #72c9a2 600px);
  }
  .ascii.ascii--light { display: none; }
  .ascii--narrow.ascii--dark { display: block; }
}
@media (prefers-color-scheme: dark) and (min-width: 600px) {
  .ascii--narrow.ascii--dark { display: none; }
  .ascii--wide.ascii--dark { display: block; font-size: 8px; font-size: 2.18cqi; }
}

@media (prefers-reduced-motion: reduce) {
  .arrow svg, a:hover .arrow svg, a:focus-visible .arrow svg {
    transform: rotate(var(--true));
    transition: none;
    animation: none;
  }
}
