/* One stylesheet for the whole site.
   Rules that are load bearing for accessibility are commented as such: they are
   not decoration and should not be "tidied away".
   Sizes are in rem so the browser's own text size setting is respected. */

:root {
  color-scheme: light dark;

  /* Type scale, one ratio (1.25) from the 1rem base, so every size on the site
     is a step on this ladder rather than a number somebody picked. Add a step
     rather than inventing a size between two of them. */
  --text-sm: 0.8rem;
  --text-base: 1rem;
  --text-md: 1.25rem;
  --text-lg: 1.563rem;
  --text-xl: 1.953rem;

  /* Spacing scale. Same idea: every margin, padding and gap comes from here.
     Consistent space is what makes a page read as composed rather than
     assembled, and it is the only layout tool this site uses. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;

  /* The reading measure. 36rem is about 70 characters a line at the base size,
     inside the 65 to 75 that is comfortable to read. This is a READABILITY
     rule, not a decorative one: a longer line makes the eye lose its place
     returning to the left edge. Widening it is a regression. */
  --measure: 36rem;

  --ink: #16181d;
  --paper: #fdfdfc;
  --muted: #4a4f5a;
  --link: #14507d;
  --link-visited: #5b3a86;
  --rule: #c9ccd4;
  --focus: #0b5ed7;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ink: #eceef2;
    --paper: #14161a;
    --muted: #b6bcc7;
    --link: #93c5f0;
    --link-visited: #cbb4ec;
    --rule: #3a3f49;
    --focus: #9ec5fe;
  }
}

body {
  background: var(--paper);
  color: var(--ink);
  font: var(--text-base)/1.6 system-ui, "Segoe UI", sans-serif;
  margin: 0 auto;
  max-width: var(--measure);
  padding: 0 var(--space-5) var(--space-7);
}

/* Prose wrapping. `pretty` stops a paragraph ending on a single stranded word.
   Purely visual: it changes where lines break, never the text, so what is read
   aloud is identical. */
p,
li {
  text-wrap: pretty;
}

p {
  margin: 0 0 var(--space-4);
}

/* A visible focus ring on everything focusable. Removing outlines is how
   keyboard users get lost. The player's slider is an input, so it has to be
   named here too or it is the one control with no ring. */
a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* The download control. An ANCHOR wearing role="button", so the look of a
   button has to be given to it by hand: the note in eleventy.config.cjs says
   why it is not a real <button>. It inherits the focus ring above, being an
   `a`. Never add `outline: none` here to tidy the ring against the border. */
a.button {
  display: inline-block;
  padding: 0.6rem 1.2rem;
  border: 2px solid var(--link);
  border-radius: 6px;
  font-weight: 600;
  text-decoration: none;
}

a.button:hover {
  text-decoration: underline;
}

/* A download is still the thing you came for after you have had it once, so it
   must not fade to the visited colour the way a read article link should. */
a.button:visited {
  color: var(--link);
}

/* Text that is READ but not seen. Load bearing: this is what carries the
   "1 of 3" position into each nav link's accessible name. It must never become
   display:none or visibility:hidden, because both remove it from the
   accessibility tree as well as from the page, which is the opposite of the
   point. */
.visually-hidden {
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

a {
  color: var(--link);
}

a:visited {
  color: var(--link-visited);
}

header {
  border-bottom: 1px solid var(--rule);
  padding-top: var(--space-5);
}

/* Base size on purpose, though it is the site's name. It sits directly above
   the h1, and anything larger competes with the page's own title for first
   place. Weight marks it instead of size. */
.site-name {
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: normal;
  margin: 0 0 var(--space-2);
}

nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
}

/* The current page is marked with aria-current in the markup, which is what a
   screen reader announces. This only makes it visible as well. */
nav [aria-current="page"] {
  font-weight: 700;
}

/* `balance` evens out the lines of a heading instead of leaving one word alone
   on the last line. The slight negative tracking is only for the largest size:
   default spacing is tuned for body text and looks loose blown up. */
h1,
h2,
h3 {
  text-wrap: balance;
}

h1 {
  font-size: var(--text-xl);
  letter-spacing: -0.015em;
  line-height: 1.25;
  margin: var(--space-6) 0 var(--space-4);
}

/* The site tagline, in the header under the site name. Larger than body text
   but NOT a heading, so it never competes with the real headings when jumping
   through the page. */
.site-tagline {
  color: var(--muted);
  font-size: var(--text-md);
  margin: 0 0 var(--space-4);
}

h2 {
  font-size: var(--text-lg);
  margin: var(--space-6) 0 var(--space-3);
}

/* The one-line project facts, directly under the heading. Muted and small
   because it is a label rather than prose, but ordinary text all the same, so
   it is read out in its natural place instead of being decoration. */
.project-meta {
  color: var(--muted);
  font-size: var(--text-sm);
  margin: calc(var(--space-3) * -1) 0 var(--space-4);
}

/* Shares a step with the tagline. They are told apart by weight and colour, an
   h3 being bold ink and a tagline regular and muted, rather than by size. */
h3 {
  font-size: var(--text-md);
  margin: var(--space-5) 0 var(--space-2);
}

li {
  margin-bottom: var(--space-2);
}

/* Audio player. Two controls, a button and a position slider, built by
   Pages\player.js. There is no caption rule and no download rule because there
   is neither: see the note in eleventy.config.cjs. */
.audio {
  align-items: center;
  border: 1px solid var(--rule);
  border-radius: 0.4rem;
  display: flex;
  gap: var(--space-4);
  margin: var(--space-5) 0;
  padding: var(--space-4);
}

/* A real button, sized so it is easy to hit. 44px is the WCAG 2.2 target size
   minimum and this clears it. */
.audio-play {
  background: var(--paper);
  border: 2px solid var(--link);
  border-radius: 0.3rem;
  color: var(--link);
  cursor: pointer;
  flex: none;
  font: inherit;
  min-height: 2.75rem;
  padding: var(--space-2) var(--space-4);
}

/* The slider takes the rest of the width. A squeezed one is hard to aim at,
   though it is always arrowable from the keyboard whatever its size. */
.audio-seek {
  accent-color: var(--link);
  flex: 1 1 auto;
  min-width: 6rem;
}

/* Stated rather than left to the browser default, because a flex item with a
   display rule of its own would otherwise keep occupying the row. The slider is
   hidden until first play: see the note in player.js. */
.audio-seek[hidden] {
  display: none;
}

/* The native player only appears when player.js has not run, so this rule is
   the no-JavaScript fallback's layout, not the normal case. */
.audio audio {
  display: block;
  width: 100%;
}

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

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

th,
td {
  border: 1px solid var(--rule);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  vertical-align: top;
}

th {
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

footer {
  border-top: 1px solid var(--rule);
  color: var(--muted);
  margin-top: var(--space-7);
  padding-top: var(--space-4);
}
