/* ==========================================================================
   Gérard Kieffer — pixel art redesign
   Fonts: Figtree (headings) + Lato (body). All self-hosted. FontAwesome:
   loaded separately in index.html.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Self-hosted fonts (assets/fonts/). Only the "latin" subset (U+0000-00FF)
   is shipped: site copy is ASCII plus "é" (Gérard).
   -------------------------------------------------------------------------- */
/* Variable font — one file covers the whole 300-900 weight axis, so any
   font-weight value (not just a few fixed instances) renders correctly. */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url('/assets/fonts/figtree-latin-variable.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/lato-latin-400-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lato';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/lato-latin-400-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fonts/lato-latin-700-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* Core palette — three blues from darkest to lightest */
  --dark-blue:    #13172d;   /* pixel-dark navy — dark surfaces (nav, footer, header) */
  --medium-blue:  #193877;   /* brand blue — links, accents on light bg */
  --light-blue:   #18549d;   /* hover state, gradient highlight */
  --yellow:       #FFE600;   /* electric yellow — signature accent */
  --white:        #FAFAFA;
  --white-a08:    #FAFAFACC;   /* White 0.8 opacity */
  --warm:         #F0EDE8;   /* off-white for alternate sections */
  --pink:         #FABDFF;   /* Used for highlighting text */
  --text:         #1A1A1A;   /* body text on white: 16.1:1 ✓ */
  --muted:        #5A5A5A;   /* secondary text */
  --border:       #0C0C1D;   /* pixel borders */
  --border-light: #D0CCC8;   /* decorative / dividers */

  /* Fonts */
  --font-heading: 'Figtree', Arial, sans-serif;
  --font-body:    'Lato', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;

  /* 4px pixel grid base */
  --px: 4px;

  /* Pixel shadows */
  --shadow-px:        4px 4px 0 0 var(--border);
  --shadow-px-yellow: 8px 8px 0 0 var(--yellow);

  /* Spacing (4px multiples) */
  --sp-sm:  1rem;    /* 16px */
  --sp-md:  2rem;    /* 32px */
  --sp-lg:  4rem;    /* 64px */
  --sp-xl:  5rem;    /* 80px */
  --gutter: 2rem;

  /* Content widths */
  --max-w:        62rem;
  --max-w-narrow: 46rem;

  /* Height of the sticky site navigation */
  --nav-h: 3.5rem;
}


/* --------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

/* Keep anchor targets clear of the sticky site nav */
:target,
[id] {
  scroll-margin-top: calc(var(--nav-h) + 1rem);
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

ul {
  list-style: none;
}


/* --------------------------------------------------------------------------
   Base typography
   -------------------------------------------------------------------------- */
body {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text);
  background: var(--white);
}

/* All headings, including the hero H1, use Figtree */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: 800;
  line-height: 1.2;
  color: var(--dark-blue);
}

h2 { font-size: clamp(1.5rem, 3vw,  1.875rem); }
h3 { font-size: clamp(1.1rem,  2vw,  1.375rem); }

mark { background-color: var(--pink);}

p {
  margin-bottom: var(--sp-md);
}
p:last-child {
  margin-bottom: 0;
}

a {
  color: var(--medium-blue);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  transition: color 0.1s ease;
}
a:hover {
  color: var(--light-blue);
}

em {
  font-style: italic;
}
blockquote {
  margin-bottom: var(--sp-md);
  
}

/* --------------------------------------------------------------------------
   Focus — keyboard navigation (WCAG 2.4.7)
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 3px solid var(--medium-blue);
  outline-offset: 3px;
  border-radius: 0;
}


/* --------------------------------------------------------------------------
   Skip link
   -------------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  top: -5rem;
  left: 1rem;
  z-index: 9999;
  padding: 0.6em 1.2em;
  background: var(--yellow);
  color: var(--dark-blue);
  font-family: var(--font-heading);
  font-size: 0.55rem;
  font-weight: 400;
  text-decoration: none;
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
  transition: top 0.2s ease;
}
.skip-link:focus {
  top: 1rem;
  outline: 3px solid var(--dark-blue);
  outline-offset: 2px;
}


/* --------------------------------------------------------------------------
   Site navigation — sticky bar, shared by every page (partials/nav.php)
   -------------------------------------------------------------------------- */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 500;
  background: var(--dark-blue);
  border-bottom: 2px solid var(--yellow);
}
.site-nav__inner {
  max-width: var(--max-w);
  margin: 0 auto;
  min-height: var(--nav-h);
  padding: 0.5rem var(--gutter);
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 2rem;
}

/* Grows to fill all the room up to the motion toggle (its sibling in
   .site-nav__inner), then space-between pushes the link list to the right
   edge of that space — i.e. right up against the toggle. */
.site-nav__links {
  display: flex;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 2rem;
}

/* Brand — heading font, matching the hero label idiom */
.site-nav__brand {
  font-family: var(--font-heading);
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.8;
  color: var(--yellow);
  text-decoration: none;
}
.site-nav__brand:hover {
  color: var(--white);
}

.site-nav__list {
  display: flex;
  flex-wrap: wrap;
  padding: 0.5em 0;
  align-items: center;
  gap: 0.25rem 1.4rem;
}
.site-nav__list a {
  display: inline-block;
  font-size: 1.1rem;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.8);
  text-decoration: none;
  border-bottom: var(--px) solid transparent;
}
.site-nav__list a:hover {
  color: var(--white);
  border-bottom-color: rgba(255, 230, 0, 0.5);
}

/* Current page — yellow underline plus a colour change, so the state is
   not conveyed by colour alone (WCAG 1.4.1) */
.site-nav__list a[aria-current="page"] {
  color: var(--yellow);
  border-bottom-color: var(--yellow);
}

/* The site nav is dark, so give the focus ring a visible colour on it */
.site-nav :focus-visible {
  outline-color: var(--yellow);
}

@media (max-width: 640px) {
  /* The bar wraps onto two lines here; keeping it sticky would eat too much
     of a small screen, so it scrolls away with the page instead. */
  .site-nav {
    position: static;
  }
  .site-nav__inner {
    justify-content: center;
    text-align: center;

  }
  /* Stop growing so it shrinks back to its own content width — .site-nav__inner's
     justify-content: center (above) can then centre it as a block, with the
     motion toggle inline alongside it, instead of it claiming the full row. */
  .site-nav__links {
    flex: 0 1 auto;
    justify-content: center;
  }
  .site-nav__list {
    width: 100%;
    justify-content: center;
    gap: 0.25rem 1.1rem;
  }
}

/* Motion toggle — pauses the animated header background (partials/nav.php).
   Hidden by default in the markup; JS (assets/js/motion-toggle.js) reveals
   it once it can actually drive the switch. Its visible label is a
   [data-tooltip] hint rather than text (aria-label carries the accessible
   name instead); font-size here sizes the em-based track/thumb below. */
.motion-toggle {
  display: inline-flex;
  align-items: center;
  background: none;
  border: 0;
  padding: 0;
  font-size: 0.85rem;
  cursor: pointer;
}
.motion-toggle:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}
.motion-toggle__track {
  position: relative;
  flex: 0 0 auto;
  width: 2.6em;
  height: 1.4em;
  background: rgba(255, 255, 255, 0.15);
  border: 2px solid rgba(255, 255, 255, 0.4);
}
.motion-toggle__thumb {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 1em;
  height: 1em;
  background: rgba(255, 255, 255, 0.7);
  transition: transform 0.15s ease, background-color 0.15s ease;
}
.motion-toggle[aria-checked="true"] .motion-toggle__track {
  border-color: var(--yellow);
  background: rgba(255, 230, 0, 0.15);
}
.motion-toggle[aria-checked="true"] .motion-toggle__thumb {
  transform: translateX(1.2em);
  background: var(--yellow);
}


/* --------------------------------------------------------------------------
   Inline SVG icons (partials/icons.php)
   -------------------------------------------------------------------------- */
.icon-svg {
  display: inline-block;
  width: 1em;
  height: 1em;
  fill: currentColor;
  vertical-align: -0.125em;
  flex-shrink: 0;
}
/* Visually hidden but announced by screen readers — the icon labels and,
   generally, any text that only exists to disambiguate a link or control */
.icon > .label,
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}


/* --------------------------------------------------------------------------
   Buttons — pixel shadow press effect
   -------------------------------------------------------------------------- */
.button {
  display: inline-block;
  padding: 0.65em 1.45em;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  border-radius: 0;
  border: 2px solid var(--dark-blue);
  color: var(--dark-blue);
  background: var(--white);
  cursor: pointer;
  box-shadow: var(--shadow-px);
  transition: box-shadow 0.08s ease, transform 0.08s ease;
}
.button:hover {
  box-shadow: 2px 2px 0 0 var(--dark-blue);
  transform: translate(2px, 2px);
  text-decoration: none;
  color: var(--dark-blue);
  background: var(--white);
}

/* Disabled — faded, no press effect, regardless of fill colour */
.button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.button:disabled:hover {
  box-shadow: var(--shadow-px);
  transform: none;
}

/* Primary = yellow fill */
.button.primary {
  background: var(--yellow);
  border-color: var(--dark-blue);
  color: var(--dark-blue);
}
.button.primary:hover {
  background: var(--yellow);
  color: var(--dark-blue);
}

.button.icon .icon-svg {
  margin-right: 0.5em;
  vertical-align: -0.05em;
}

/* Hero buttons on dark background */
#header .button {
  border-color: var(--white);
  color: var(--white);
  background: transparent;
  box-shadow: 4px 4px 0 0 rgba(255,255,255,0.5);
}
#header .button:hover {
  color: var(--white);
  background: transparent;
  box-shadow: 2px 2px 0 0 rgba(255,255,255,0.5);
  transform: translate(2px, 2px);
}
#header .button.primary {
  background: var(--yellow);
  border-color: var(--yellow);
  color: var(--dark-blue);
  box-shadow: 4px 4px 0 0 rgba(255,255,255,0.3);
}
#header .button.primary:hover {
  background: var(--yellow);
  color: var(--dark-blue);
  box-shadow: 2px 2px 0 0 rgba(255,255,255,0.3);
  transform: translate(2px, 2px);
}

ul.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm);
}
ul.actions.special {
  justify-content: center;
}

@media (prefers-reduced-motion: reduce) {
  .button,
  .button:hover {
    transition: none;
    transform: none;
    box-shadow: var(--shadow-px);
  }
  #header .button,
  #header .button:hover {
    transform: none;
    box-shadow: 4px 4px 0 0 rgba(255,255,255,0.5);
  }
  #header::before,
  #header::after,
  .page-header::before,
  .page-header::after {
    content: none;
  }
  #header,
  .page-header {
    background-image: linear-gradient(-60deg,
      var(--dark-blue) 33.33%,
      var(--medium-blue) 33.33% 66.66%,
      var(--light-blue) 66.66%);
  }
  .motion-toggle__thumb {
    transition: none;
  }
}


/* --------------------------------------------------------------------------
   Section wrappers
   -------------------------------------------------------------------------- */
.wrapper {
  padding: var(--sp-lg) var(--gutter);
  background: var(--white);
  border-top: 2px solid var(--dark-blue);
}
.wrapper.style2 {
  background: var(--warm);
  text-align: center;
}
.wrapper > .inner {
  max-width: var(--max-w);
  margin: 0 auto;
}
.wrapper.style2 > .inner {
  max-width: var(--max-w-narrow);
}
.inner.alt > * + * {
  border-top: 2px solid var(--border-light);
  margin-top: var(--sp-lg);
  padding-top: var(--sp-lg);
}
section.special {
  text-align: center;
}

/* Section heading with yellow pixel underline */
header.major {
  margin-bottom: var(--sp-lg);
}
header.major h2 {
  margin-bottom: 0.5em;
}
header.major p {
  font-size: 1.05rem;
}
header.major::after {
  content: '';
  display: block;
  width: 2.5rem;
  height: var(--px);
  background: var(--yellow);
  border: 1px solid var(--dark-blue);
  margin: var(--sp-md) auto 0;
}

/* CTA section — dark variant */
#three.wrapper {
  background: var(--dark-blue);
  border-top: 4px solid var(--yellow);
}
#three h2 {
  color: var(--white);
}
#three p {
  color: rgba(255,255,255,0.75);
}
#three .button {
  border-color: var(--white);
  color: var(--white);
  background: transparent;
  box-shadow: 4px 4px 0 0 rgba(255,255,255,0.4);
}
#three .button:hover {
  box-shadow: 2px 2px 0 0 rgba(255,255,255,0.4);
  transform: translate(2px, 2px);
  background: transparent;
  color: var(--white);
}
#three .button.primary {
  background: var(--yellow);
  border-color: var(--yellow);
  color: var(--dark-blue);
  box-shadow: 4px 4px 0 0 rgba(255,255,255,0.25);
}
#three .button.primary:hover {
  background: var(--yellow);
  color: var(--dark-blue);
  box-shadow: 2px 2px 0 0 rgba(255,255,255,0.25);
}
#three header.major::after {
  background: var(--yellow);
  border-color: rgba(255,255,255,0.3);
}


/* --------------------------------------------------------------------------
   Spotlights — alternating image + text rows
   -------------------------------------------------------------------------- */
.spotlight {
  display: flex;
  align-items: center;
  gap: var(--sp-lg);
}
.spotlight .image {
  flex: 0 0 180px;
}
.spotlight .image img {
  width: 100%;
  border-radius: 0;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
}
.spotlight .content {
  flex: 1;
}
.spotlight .content h3 {
  margin-bottom: 0.6em;
}
.spotlight:nth-child(even) {
  flex-direction: row-reverse;
  text-align: right;
}


/* --------------------------------------------------------------------------
   Icon lists
   -------------------------------------------------------------------------- */
ul.icons {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 2em;
}
ul.icons.major li {
  font-size: 2.5rem;
  color: var(--border-light);
}
ul.icons.major li a {
  color: var(--medium-blue);
  text-decoration: none;
  transition: color 0.1s ease;
}
ul.icons.major li a:hover {
  color: var(--dark-blue);
}
ul.icons.labeled {
  gap: 1em 2.5em;
}
ul.icons.labeled li {
  display: flex;
  align-items: center;
  gap: 0.5em;
  font-size: 0.95rem;
}
ul.icons.labeled .icon .icon-svg {
  font-size: 1.4rem;
  color: var(--medium-blue);
}
ul.icons.labeled .icon > .label {
  position: static;
  width: auto;
  height: auto;
  overflow: visible;
  clip: auto;
  white-space: normal;
}
#footer ul.icons {
  gap: 0 1.5em;
}
#footer ul.icons a {
  font-size: 1.5rem;
  color: rgba(255,255,255,0.55);
  text-decoration: none;
  transition: color 0.15s ease;
}
#footer ul.icons a:hover {
  color: var(--yellow);
}


/* --------------------------------------------------------------------------
   Hero / Header
   -------------------------------------------------------------------------- */
#header {
  /* Fill the viewport minus the sticky nav sitting above it */
  min-height: calc(100svh - var(--nav-h));
  background-color: var(--dark-blue);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-xl) var(--gutter);
  color: var(--white);
  position: relative;
  overflow: hidden;
  border-bottom: var(--px) solid var(--yellow);
}

#header > .inner {
  display: flex;
  align-items: center;
  gap: var(--sp-xl);
  width: 100%;
  max-width: var(--max-w);
  position: relative;
  z-index: 1;
}

/* Slow drifting diagonal gradient — shared by the home hero and the
   sub-page headers. Two offset layers of the same split gradient give
   the illusion of colour slowly churning behind the content. */
#header::before,
#header::after,
.page-header::before,
.page-header::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: -50%;
  right: -50%;
  z-index: 0;
  opacity: .5;
  background-image: linear-gradient(-60deg, var(--dark-blue) 50%, var(--light-blue) 50%);
  animation: header-bg-slide 6s ease-in-out infinite alternate;
}
#header::after,
.page-header::after {
  animation-direction: alternate-reverse;
  animation-duration: 8s;
}

/* Desktop viewports have room for the drift to be slower and more
   subtle; phones and tablets keep the base durations above. Linear
   timing here (instead of the base rule's ease-in-out) is deliberate:
   over the longer desktop duration, ease-in-out's S-curve made the
   middle of each pass noticeably faster than the eased edges — linear
   keeps the drift at one constant speed instead. */
@media (min-width: 981px) {
  #header::before,
  .page-header::before {
    animation-duration: 11s;
    animation-timing-function: linear;
  }
  #header::after,
  .page-header::after {
    animation-duration: 13s;
    animation-timing-function: linear;
  }
}

@keyframes header-bg-slide {
  0%   { transform: translateX(-25%); }
  100% { transform: translateX(25%); }
}

/* Footer switch override (partials/footer.php) — takes effect regardless
   of viewport size. The class is set as early as possible by the inline
   script in partials/head.php so there is no flash of motion on load.
   Stopping the drift removes the two sliding layers outright (rather than
   just freezing them mid-gradient, which only ever shows two colours —
   see the identical treatment in the prefers-reduced-motion block above)
   and replaces them with a static three-colour diagonal split instead. */
html.animations-off #header::before,
html.animations-off #header::after,
html.animations-off .page-header::before,
html.animations-off .page-header::after {
  content: none;
}
html.animations-off #header,
html.animations-off .page-header {
  --dark-blue-rgb: 19 23 45;
  --medium-blue-rgb: 25 56 119;
  --light-blue-rgb: 24 84 157;

  background-image: linear-gradient(
    -60deg,
    rgb(var(--dark-blue-rgb) / 0.5) 33.33%,
    rgb(var(--medium-blue-rgb) / 0.5) 33.33% 66.66%,
    rgb(var(--light-blue-rgb) / 0.5) 66.66%
  );
}

#header .content {
  flex: 1;
  text-align: right;
}

#header h1 {
  font-family: var(--font-heading);
  font-size: clamp(1.8rem, 3vw, 2rem);
  line-height: 1.8;
  color: var(--white);
  margin-bottom: 0.8em;
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.35);
}

#header blockquote {
  font-size: 1rem;
  color: rgba(255, 255, 255, 0.8);
  margin-bottom: var(--sp-md);
  max-width: 28rem;
  margin-left: auto;
}

#header p {
  font-size: 0.9rem;
  margin-bottom: var(--sp-md);
  max-width: 29rem;
  margin-left: auto;
  padding: 0.6em 0.8em;
}

#header a:not(.button) {
  color: var(--yellow);
  text-decoration: underline;
}
#header a:not(.button):hover {
  color: #fff;
}

#header .content ul.actions {
  justify-content: flex-end;
}

/* Decorative label above name */
#header .hero-label {
  font-family: var(--font-heading);
  font-size: 1.2rem;
  font-weight: 800;
  color: var(--warm);
  text-transform: uppercase;
  margin-bottom: 1.5rem;
  line-height: 1.6;
  display: inline-block;
  border: 1px solid var(--warm);
}

/* Portrait — pixel art frame replaces phone mockup */
#header .image.portrait {
  flex: 0 0 auto;
  width: 20rem;
}
#header .image.portrait .inner {
  position: relative;
  padding: 0;
  border: 3px solid var(--yellow);
  border-radius: 0;
  box-shadow: 8px 8px 0 0 var(--yellow), 14px 14px 0 0 rgba(255, 230, 0, 0.38);
}
/* Remove phone chrome */
#header .image.portrait .inner::before,
#header .image.portrait .inner::after {
  display: none;
}
#header .image.portrait img {
  width: 100%;
  height: auto;
  border-radius: 0;
  display: block;
}


/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */
#footer {
  background: var(--dark-blue);
  color: rgba(255, 255, 255, 0.65);
  text-align: center;
  padding: var(--sp-lg) var(--gutter);
  border-top: 4px solid var(--yellow);
}

/* Mascot column + content column, side by side on desktop; the mascot
   comes first in the markup so it stacks above the content on mobile
   (below) without needing to reorder anything. */
.footer__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-lg);
  max-width: var(--max-w);
  margin: 0 auto;
}
.footer__mascot img {
  width: 7rem;
  height: 7rem;
}

#footer ul.icons {
  margin-bottom: var(--sp-md);
}

#footer .copyright {
  font-size: 0.85rem;
}

/* The footer is dark, so give the focus ring a visible colour on it */
#footer :focus-visible {
  outline-color: var(--yellow);
}
#footer .copyright a {
  color: rgba(255, 255, 255, 0.4);
  text-decoration: underline;
}
#footer .copyright a:hover {
  color: var(--yellow);
}


/* --------------------------------------------------------------------------
   Responsive — tablets (≤ 980px)
   -------------------------------------------------------------------------- */
@media (max-width: 980px) {
  :root {
    --sp-lg: 3rem;
    --sp-xl: 3.5rem;
    --gutter: 2.5rem;
  }
  .spotlight .image {
    flex-basis: 140px;
  }
}


/* --------------------------------------------------------------------------
   Responsive — phones (≤ 736px)
   -------------------------------------------------------------------------- */
@media (max-width: 736px) {
  :root {
    --sp-lg: 2.5rem;
    --sp-xl: 2.5rem;
    --gutter: 1.5rem;
  }

  #header > .inner {
    flex-direction: column-reverse;
    gap: var(--sp-md);
    text-align: center;
  }
  #header .content {
    text-align: center;
  }
  #header .hero-label {
    margin-left: 0;
    margin-right: 0;
  }
  #header .content ul.actions {
    justify-content: center;
  }

  .spotlight,
  .spotlight:nth-child(even) {
    flex-direction: column;
    text-align: center;
    gap: var(--sp-md);
  }
  .spotlight .image {
    flex-basis: auto;
    width: 130px;
    margin: 0 auto;
  }

  ul.actions {
    flex-direction: column;
    align-items: center;
  }

  .footer__inner {
    flex-direction: column;
    gap: var(--sp-md);
  }
}


/* --------------------------------------------------------------------------
   Responsive — small phones (≤ 480px)
   -------------------------------------------------------------------------- */
@media (max-width: 480px) {
  :root {
    --gutter: 1rem;
  }
  #header .image.portrait {
    width: 12rem;
  }
  #header h1 {
    font-size: 1.8rem;
    margin-bottom: 0.4em;
  }
}


/* --------------------------------------------------------------------------
   Tooltips — CSS-only, visible on hover and keyboard focus
   The data-tooltip value mirrors aria-label so screen readers use
   aria-label (the accessible name) and never read the tooltip twice.
   -------------------------------------------------------------------------- */
[data-tooltip] {
  position: relative;
}
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--dark-blue);
  color: var(--white);
  font-family: var(--font-body);
  font-size: 0.75rem;
  line-height: 1.4;
  white-space: nowrap;
  padding: 0.3em 0.7em;
  border: 2px solid var(--yellow);
  pointer-events: none;
  opacity: 0;
  z-index: 100;
}
[data-tooltip]:hover::after,
[data-tooltip]:focus::after {
  opacity: 1;
}
@media (prefers-reduced-motion: no-preference) {
  [data-tooltip]::after {
    transition: opacity 0.15s ease;
  }
}

/* The nav bar sits flush against the top of the viewport, so a tooltip
   opening upward (the default above) would run off-screen — open it
   downward there instead. */
.site-nav [data-tooltip]::after {
  bottom: auto;
  top: calc(100% + 8px);
}

/* Avoid horizontal scrolling because of the tooltip offset.
   Scoped to the motion toggle: it sits at the right-hand end of the bar,
   so its centred tooltip would overflow the viewport. Anything else with
   a tooltip in this nav — the blog's language flags — sits mid-bar and
   should stay centred on its own control, which is where these offsets
   would otherwise drag it away from. */
@media (max-width: 1048px) {
  .site-nav .motion-toggle[data-tooltip]::after {
    left: -30px;
    right: auto;
  }
}
@media (max-width: 480px) {
  .site-nav .motion-toggle[data-tooltip]::after {
    left: -50px;
    right: auto;
  }
}



/* --------------------------------------------------------------------------
   Sub-page components
   Reusable across any page that links main.css.
   -------------------------------------------------------------------------- */

/* Page header — compact dark hero for sub-pages */
.page-header {
  background-color: var(--dark-blue);
  color: var(--white);
  padding: var(--sp-lg) var(--gutter);
  border-bottom: var(--px) solid var(--yellow);
  position: relative;
  overflow: hidden;
}
.page-header > .inner {
  max-width: var(--max-w);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-lg);
  align-items: start;
  position: relative;
  z-index: 1;
}
.page-header h1 {
  font-family: var(--font-heading);
  font-size: clamp(1.4rem, 2vw, 1.6rem);
  line-height: 1.8;
  color: var(--white);
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.35);
  margin-bottom: 0.4em;
}
/* Small heading-font caption — shared by the sub-page kicker, the page
   tagline, and the on-page project index heading below */
.page-header h2,
.page-subtitle,
.project-index h2 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: 1.8;
  margin: 0;
}
.page-header h2,
.page-subtitle {
  font-size: clamp(1.1rem, 1.5vw, 1.4rem);
  color: var(--yellow);
}
.page-header h3 {
  color: var(--white);
  margin-bottom: 0.5em;
}
.page-header p {
  color: rgba(255, 255, 255, 0.8);
  margin-bottom: 0;
}
.page-header a {
  color: var(--yellow);
  text-decoration: underline;
}
.page-header a:hover {
  color: var(--white);
}

/* Photo entries */
.photo-entry {
  padding: var(--sp-lg) 0;
  border-bottom: 2px solid var(--border-light);
}
.photo-entry:last-child {
  border-bottom: none;
}
.photo-entry h2 {
  margin-bottom: 0.75em;
  font-size: clamp(1.1rem, 2vw, 1.375rem);
}
.photo-entry p {
  max-width: var(--max-w-narrow);
  margin-bottom: var(--sp-md);
}

/* Side-by-side photo grid */
.photo-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--px);
}
.photo-pair + .photo-pair {
  margin-top: var(--px);
}
.photo-pair figure {
  margin: 0;
}
.photo-pair img {
  width: 100%;
  height: auto;
  display: block;
  border: 2px solid var(--dark-blue);
}
.photo-pair figcaption {
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 0.4em;
  line-height: 1.4;
}

/* Heading-font chip — shared base for the photo year label and the
   project tag list; each gives it its own size/colour below */
.photo-label,
.project__tags li {
  display: inline-block;
  /* font-family: var(--font-heading); */
}

/* Year / era label chip */
.photo-label {
  font-size: 0.5rem;
  color: var(--yellow);
  background: var(--dark-blue);
  padding: 0.3em 0.6em;
  margin-bottom: 0.35em;
}

/* FAQ section */
.faqs ul,
.project__list {
  list-style: disc;
  padding-left: 1.5em;
}
.faqs ul li,
.project__list li {
  margin-bottom: 0.5em;
}


/* --------------------------------------------------------------------------
   Projects page
   Each project is one <article class="project">. The layout is built to
   repeat: add another <article> and one .project-index entry, nothing else.
   -------------------------------------------------------------------------- */

/* On-page index of the projects below */
.project-index {
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
  padding: var(--sp-md);
  margin-bottom: var(--sp-lg);
  background: var(--warm);
}
.project-index h2 {
  font-size: 0.6rem;
  color: var(--dark-blue);
  margin-bottom: 1rem;
}
.project-index ul {
  list-style: none;
}
.project-index li + li {
  margin-top: 0.5em;
}
.project-index li::before {
  content: '▸ ';
  color: var(--medium-blue);
}

/* One project */
.project + .project {
  margin-top: var(--sp-lg);
  padding-top: var(--sp-lg);
  border-top: 2px solid var(--border-light);
}

.project__header {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-md);
  margin-bottom: var(--sp-md);
}
.project__logo {
  flex: 0 0 120px;
  width: 120px;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  background: var(--white);
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
  padding: 0.5rem;
}
.project__intro {
  flex: 1;
  min-width: 0;
}
.project__intro h2 {
  margin-bottom: 0.35em;
}

.project__meta {
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 0.75em;
}

/* Tag chips — decorative labels, styled like the photo-label chip */
.project__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
}
.project__tags li {
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--dark-blue);
  background: var(--pink);
  border: 1px solid var(--dark-blue);
  padding: 0.25em 0.4em;
}

.project__body {
  max-width: var(--max-w-narrow);
}
/* Section headings inside a project. On the listing page the projects are
   h2, so their inner headings are h3; on a project sub-page the project is
   the h1, so the same sections are h2. Both get the same spacing. */
.project__body h2,
.project__body h3 {
  margin-top: var(--sp-md);
  margin-bottom: 0.75em;
}

/* Update / callout note */
.project__note {
  border-left: var(--px) solid var(--yellow);
  background: var(--warm);
  padding: var(--sp-sm);
  font-size: 0.95rem;
}

/* Numbered lists — shared base for the feature list, the video transcript
   and the footnotes; each sets its own spacing below */
.project__features,
.project__transcript ol,
.footnotes ol {
  list-style: decimal;
  padding-left: 1.5em;
}

/* Numbered feature list */
.project__features {
  margin-bottom: var(--sp-md);
}
.project__features li {
  margin-bottom: 0.85em;
}

.project__list {
  margin-bottom: var(--sp-md);
}

/* Demo video */
.project__video {
  margin-bottom: var(--sp-md);
}
.project__video video {
  display: block;
  width: 100%;
  height: auto;
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
  background: var(--dark-blue);
}
.project__video figcaption,
.project__screenshot figcaption {
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 0.6em;
  line-height: 1.4;
}

/* Linked screenshot (click through to a full-size version) */
.project__screenshot {
  margin-bottom: var(--sp-md);
}
.project__screenshot img {
  display: block;
  max-width: 100%;
  height: auto;
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
}

/* Collapsible transcript */
.project__transcript {
  border: 2px solid var(--border-light);
  padding: var(--sp-sm);
  margin-bottom: var(--sp-md);
  margin-top: var(--sp-md);
}
.project__transcript summary {
  font-weight: 700;
  cursor: pointer;
  color: var(--medium-blue);
}
.project__transcript summary:hover {
  color: var(--light-blue);
}
/* The subsection labels are real headings for the accessibility tree (see
   index.php), but keep looking like the plain bold-text labels they
   replace. The summary's own heading role is set directly on its inline
   text via role="heading" (see index.php) — nesting an actual <h2> there
   pushed it onto its own line, below the disclosure marker, in some
   browsers. */
.project__transcript-subheading {
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  color: inherit;
  line-height: inherit;
  margin-bottom: var(--sp-md);
}
.project__transcript ol,
.project__transcript ul {
  margin-top: var(--sp-sm);
}
.project__transcript li {
  margin-bottom: 0.5em;
}

/* Footnotes */
.footnotes {
  margin-top: var(--sp-md);
  padding-top: var(--sp-sm);
  border-top: 2px solid var(--border-light);
  font-size: 0.85rem;
  color: var(--muted);
}
.footnotes h3 {
  font-size: 0.95rem;
  margin-top: 0;
  margin-bottom: 0.5em;
}
.project__actions {
  margin-top: var(--sp-md);
}

@media (max-width: 736px) {
  .project__header {
    flex-direction: column;
  }
  .project__logo {
    flex-basis: auto;
    width: 96px;
  }
}

/* Sub-page responsive */
@media (max-width: 736px) {
  .page-header > .inner {
    grid-template-columns: 1fr;
    gap: var(--sp-md);
  }
  .photo-pair {
    grid-template-columns: 1fr;
  }
}


/* --------------------------------------------------------------------------
   Breadcrumb trail — for any page nested more than one level deep.
   Reusable across any page that links main.css.
   -------------------------------------------------------------------------- */
.breadcrumb-nav ul {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3em;
  list-style: none;
  font-size: 0.85rem;
}
.breadcrumb-nav li::after {
  content: '›';
  margin-left: 0.3em;
  color: var(--white);
}
.breadcrumb-nav a {
  color: var(--white-a08);
}


/* --------------------------------------------------------------------------
   Tile grid — a grid of image cards, each a link with a cover image and an
   optional caption underneath (album covers, photo thumbnails, or any other
   "grid of visual links" elsewhere on the site).
   Reusable across any page that links main.css.
   -------------------------------------------------------------------------- */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--gutter);
  list-style: none;
}
.tile a {
  display: block;
  position: relative;
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
  background: var(--white);
  overflow: hidden;
  transition: box-shadow 0.08s ease, transform 0.08s ease;
}
.tile a:hover {
  box-shadow: 2px 2px 0 0 var(--dark-blue);
  transform: translate(2px, 2px);
}
.tile picture {
  display: block;
}
.tile img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
}
.tile__caption {
  display: block;
  padding: 0.5em 0.15em 0;
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.4;
}

@media (max-width: 480px) {
  .tile-grid {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--sp-sm);
  }
}


/* --------------------------------------------------------------------------
   Dictation tool (LuxASR Dictation project page) — live mic status,
   recording options, controls, level meter and countdown. Only used on
   /projects/luxasr-dictation/, but built from the same tokens/components as
   everything else (.button, .project__note, .project__transcript) rather
   than introducing a parallel design.
   -------------------------------------------------------------------------- */
.dictation {
  margin-top: var(--sp-lg);
}

.dictation__status {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.875rem 1rem;
  margin-bottom: var(--sp-sm);
  border: 2px solid var(--dark-blue);
  background: var(--warm);
  font-weight: 700;
}
.dictation__status--recording {
  background: var(--pink);
}
.dictation__status--transcribing {
  background: var(--white);
  border-color: var(--medium-blue);
  color: var(--medium-blue);
}
.dictation__dot {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: currentColor;
}
.dictation__status--recording .dictation__dot {
  animation: dictation-pulse 1.5s infinite;
}
@keyframes dictation-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.3; }
}

.dictation__error {
  display: none;
  border: 2px solid var(--dark-blue);
  border-left: var(--px) solid var(--yellow);
  background: var(--warm);
  padding: 0.875rem 1rem;
  margin-bottom: var(--sp-sm);
  font-weight: 700;
}
.dictation__error.show {
  display: block;
}

.dictation__options {
  border: 2px solid var(--border-light);
  padding: var(--sp-sm);
  margin-bottom: var(--sp-sm);
}
.dictation__options h2 {
  font-size: clamp(1.1rem, 2vw, 1.375rem); /* matches h3 elsewhere in .project__body */
  margin-bottom: 0.75em;
}
.dictation__options-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-sm);
}
.dictation__field label {
  display: block;
  font-weight: 700;
  margin-bottom: 0.375em;
}
.dictation__field select,
.dictation__field input {
  width: 100%;
  padding: 0.5em 0.75em;
  border: 2px solid var(--dark-blue);
  background: var(--white);
  color: var(--text);
  font-family: inherit;
  font-size: 1rem;
}
.dictation__field small {
  display: block;
  margin-top: 0.3em;
  font-size: 0.8rem;
  color: var(--muted);
}

.dictation__buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  margin-bottom: var(--sp-sm);
}
.dictation__btn--transcribe {
  background: var(--medium-blue);
  color: var(--white);
}
.dictation__btn--transcribe:hover {
  background: var(--light-blue);
  color: var(--white);
}
.dictation__btn--finish {
  background: var(--dark-blue);
  color: var(--white);
}
.dictation__btn--finish:hover {
  background: var(--dark-blue);
  opacity: 0.85;
  color: var(--white);
}

.dictation__level {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: var(--sp-sm);
}
.dictation__level-main {
  flex: 1 1 auto;
  min-width: 0;
  opacity: 0.4;
  transition: opacity 0.2s ease;
}
.dictation__level.active .dictation__level-main {
  opacity: 1;
}
.dictation__level-label {
  display: flex;
  justify-content: space-between;
  margin-bottom: 0.375em;
  font-size: 0.85rem;
  font-weight: 700;
}
.dictation__level-bar {
  width: 100%;
  height: 20px;
  border: 2px solid var(--dark-blue);
  background: var(--white);
  overflow: hidden;
}
.dictation__level-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(to right, var(--light-blue), var(--yellow), var(--pink));
  transition: width 0.1s linear;
}

.dictation__timer {
  flex: 0 0 auto;
  opacity: 0.3;
  transition: opacity 0.3s ease;
}
.dictation__timer.active {
  opacity: 1;
}
.dictation__timer-wrapper {
  position: relative;
  width: 56px;
  height: 56px;
}
.dictation__timer-wrapper svg {
  transform: rotate(-90deg);
  width: 56px;
  height: 56px;
}
.dictation__timer-bg {
  fill: none;
  stroke: var(--border-light);
  stroke-width: 8;
}
.dictation__timer-progress {
  fill: none;
  stroke: var(--yellow);
  stroke-width: 8;
  stroke-linecap: round;
  transition: stroke-dashoffset 0.1s linear;
}
.dictation__timer-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 0.8rem;
  color: var(--dark-blue);
  line-height: 1;
}

.dictation__transcript {
  display: block;
  width: 100%;
  min-height: 280px;
  padding: 1rem;
  border: 2px solid var(--dark-blue);
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text);
  background: var(--white);
  resize: vertical;
}

@media (max-width: 599px) {
  .dictation__buttons {
    flex-direction: column;
  }
  .dictation__btn {
    width: 100%;
    justify-content: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .dictation__dot {
    animation: none;
  }
  .dictation__level-fill,
  .dictation__timer-progress {
    transition: none;
  }
}

/* ==========================================================================
   Blog (blog.gerardkieffer.net)
   ==========================================================================
   The blog shares this stylesheet with the main site by design: it is
   served from the same document root, so restyling the site restyles the
   blog. Everything below reuses the existing tokens and idioms rather
   than introducing a second visual language.

   Two of these blocks are NOT blog-specific and apply site-wide: the
   table styles and the code styles. Neither existed before, and the
   first article ships with a comparison table.
   -------------------------------------------------------------------------- */

   
/* --------------------------------------------------------------------------
   Article body
   -------------------------------------------------------------------------- */

article.post ul {
  list-style: revert;
  margin-block-end: 2em;
  margin-left: 1em;
}

/* --------------------------------------------------------------------------
   Tables — site-wide, first needed by the blog
   -------------------------------------------------------------------------- */

/* A wide table has to be able to scroll on a narrow screen. The wrapper
   that does the scrolling is generated in blog/lib/render.php and carries
   role="region" + tabindex="0", so it is reachable by keyboard (WCAG
   2.1.1) and announced with a name rather than as a mystery tab stop. */
.table-scroll {
  overflow-x: auto;
  margin-bottom: var(--sp-md);
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
  /* Scroll containers are focusable, so they need a visible focus ring
     like any other interactive element. */
  scrollbar-gutter: stable;
}
.table-scroll:focus-visible {
  outline: 3px solid var(--medium-blue);
  outline-offset: 3px;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
  background: var(--white);
}
caption {
  padding: 0.75em var(--sp-sm);
  font-family: var(--font-heading);
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.5;
  color: var(--muted);
  text-align: left;
  border-bottom: 2px solid var(--border-light);
}
th,
td {
  padding: 0.6em 0.9em;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border-light);
}
thead th {
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--white);
  background: var(--dark-blue);
  border-bottom: none;
}
tbody th[scope="row"] {
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--dark-blue);
  background: var(--warm);
}
tbody tr:last-child th,
tbody tr:last-child td {
  border-bottom: none;
}


/* --------------------------------------------------------------------------
   Code — site-wide, first needed by the blog
   -------------------------------------------------------------------------- */

code,
kbd,
samp {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
}
:not(pre) > code {
  padding: 0.15em 0.4em;
  color: var(--dark-blue);
  background: var(--warm);
  border: 1px solid var(--border-light);
  border-radius: 2px;
}
pre {
  /* Also focusable (see render.php): a horizontally scrolling code block
     is otherwise unreachable without a mouse. */
  overflow-x: auto;
  margin-bottom: var(--sp-md);
  padding: var(--sp-sm);
  color: var(--white);
  background: var(--dark-blue);
  border-left: var(--px) solid var(--yellow);
}
pre:focus-visible {
  outline: 3px solid var(--yellow);
  outline-offset: 3px;
}
pre code {
  padding: 0;
  color: inherit;
  background: none;
  border: 0;
}


/* --------------------------------------------------------------------------
   Full-width page header
   --------------------------------------------------------------------------
   The default .page-header lays its inner out as two equal columns,
   which suits project pages: they pair the <h1> with a block of prose
   beside it. A blog article has no such second column — its only aside
   is the occasional "translated from …" note — so the split leaves the
   title crammed into half the width for no reason. This variant drops
   back to a single column and lets the content use the full measure;
   the aside, when there is one, simply stacks underneath. */

.page-header--wide > .inner {
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-md);
}


/* --------------------------------------------------------------------------
   Blog navigation — language switcher
   -------------------------------------------------------------------------- */

/* The visible control is a flag; the language name is carried by a
   visually hidden span (the accessible name) and by [data-tooltip] (the
   sighted equivalent, shown on hover AND on keyboard focus). See the
   docblock in blog/templates/partials/lang-switcher.php. */

.site-nav__lang ul {
  display: flex;
  /* Never wrap: the switcher shares a row with the motion toggle on
     small screens, and a flag dropping to its own line would break that. */
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.6rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.site-nav__lang a {
  display: block;
  /* Flags are small, so pad the hit area out a little — cheap here, and
     it matters on touch. */
  padding: 0.25em 0.15rem 0.3em;
  border-bottom: var(--px) solid transparent;
}

.site-nav__flag {
  display: block;
  width: 1.45rem;
  aspect-ratio: 3 / 2;
  height: auto;
  /* The artwork ratios differ (3:2, 5:3, 2:1); cover keeps them all the
     same size in the bar without distorting any of them. */
  object-fit: cover;
  /* White and light bands would otherwise bleed into the dark bar. */
  border: 1px solid rgba(255, 255, 255, 0.45);
  opacity: 0.7;
}
.site-nav__lang a:hover .site-nav__flag {
  opacity: 1;
  border-color: var(--white);
}

/* Current language. Opacity and colour are both purely visual weight
   cues, so the yellow underline carries the state as well — the same
   idiom as .site-nav__list a[aria-current], and not colour alone
   (WCAG 1.4.1). */

.site-nav__lang a[aria-current="page"] .site-nav__flag {
  opacity: 1;
  outline: solid 0.1em var(--yellow)
}

/* The main site's nav bar is flex-wrap: nowrap. The blog bar carries an
   extra language switcher, so it is allowed to wrap — scoped to the blog
   modifier class so the main site's layout is untouched. */
@media (max-width: 860px) {
  .site-nav--blog .site-nav__inner {
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
  }
}
/* Below this the bar is two rows: brand and menu on the first, then the
   language switcher and the motion toggle side by side on the second.
   Giving .site-nav__links the whole first row is what puts those two on
   a shared line instead of each claiming one of its own. */
@media (max-width: 640px) {
  .site-nav--blog .site-nav__links {
    flex: 0 0 100%;
  }
  .site-nav--blog .site-nav__lang {
    flex: 0 0 auto;
    padding: 0.5em 0;
  }
}


/* --------------------------------------------------------------------------
   Article layout — sticky table of contents beside the body
   -------------------------------------------------------------------------- */

.post-layout {
  display: grid;
  gap: var(--sp-md);
  /* minmax(0, 1fr), not 1fr: a grid item defaults to min-width:auto and
     therefore refuses to shrink below its content's min-content width.
     With a wide table inside, that pushes the whole page wider than the
     viewport and breaks reflow at 320px (WCAG 1.4.10 / RAWeb 10.11).
     The explicit 0 minimum is what lets .table-scroll do its job. */
  grid-template-columns: minmax(0, 1fr);
}
/* Same reasoning one level down, for the article itself. */
.post,
.post__body {
  min-width: 0;
}

/* Sidebar only where there is genuinely room for one. Below this the TOC
   collapses to a <details> disclosure (see .post-toc). */
@media (min-width: 62rem) {
  .post-layout {
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: var(--sp-lg);
    align-items: start;
  }
  .post-layout--no-toc {
    grid-template-columns: minmax(0, 1fr);
  }
}

.post__body {
  max-width: var(--max-w-narrow);
}

/* Keeps the sticky nav from covering a heading you have just jumped to
   from the table of contents. */
.post__body h2[id],
.post__body h3[id],
.post__body h4[id] {
  scroll-margin-top: calc(var(--nav-h) + 1rem);
}

/* Headings carry tabindex="-1" so a TOC link moves focus to them. That
   makes them programmatically focusable, and a focus ring on a heading
   the user deliberately jumped to is helpful, not noise. */
.post__body :is(h2, h3, h4):focus-visible {
  outline: 3px solid var(--medium-blue);
  outline-offset: 4px;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}
/* The site's own motion switch must win over the media query above. */
html.animations-off {
  scroll-behavior: auto;
}


/* --------------------------------------------------------------------------
   Table of contents
   -------------------------------------------------------------------------- */

.post-toc {
  margin-bottom: var(--sp-md);
  padding: var(--sp-sm);
  background: var(--warm);
  border-left: var(--px) solid var(--yellow);
}
.post-toc__summary {
  font-family: var(--font-heading);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--dark-blue);
  cursor: pointer;
}
.post-toc__summary:focus-visible {
  outline: 3px solid var(--medium-blue);
  outline-offset: 3px;
}
.post-toc__list {
  list-style: none;
  margin: var(--sp-sm) 0 0;
  padding: 0;
  font-size: 0.9rem;
}
.post-toc__item {
  margin-bottom: 0.35em;
}
.post-toc__item--h3 {
  padding-left: 1em;
  font-size: 0.85rem;
}
.post-toc__item--h4 {
  padding-left: 2em;
  font-size: 0.85rem;
}
.post-toc__list a {
  display: block;
  padding: 0.2em 0 0.2em 0.6em;
  color: var(--text);
  text-decoration: none;
  border-left: var(--px) solid transparent;
  letter-spacing: 0.01em;
}
.post-toc__list a:hover {
  color: var(--medium-blue);
  text-decoration: underline;
}

/* The section currently being read. Colour, weight AND a left marker —
   three cues, so removing colour still leaves the state legible. Set by
   assets/js/blog-post.js; absent entirely when JavaScript is off, which
   costs nothing because the links work regardless. */
.post-toc__list a[aria-current="true"] {
  font-weight: 700;
  background: var(--yellow);
  letter-spacing: unset;

}

@media (min-width: 62rem) {
  .post-toc {
    position: sticky;
    top: calc(var(--nav-h) + var(--sp-sm));
    max-height: calc(100vh - var(--nav-h) - var(--sp-md));
    overflow-y: auto;
    margin-bottom: 0;
  }
  /* On desktop the sidebar is open, so the disclosure arrow would be a
     control that does nothing useful — hide it and stop it responding.
     Scoped to [open] on purpose: if the TOC is ever closed at this width
     (JavaScript blocked, a script error, a reader who collapsed it), the
     arrow and the click target come back, so it can always be reopened.
     Suppressing them unconditionally is how you strand someone. */
  .post-toc[open] > .post-toc__summary {
    list-style: none;
    cursor: default;
    pointer-events: none;
  }
  .post-toc[open] > .post-toc__summary::-webkit-details-marker {
    display: none;
  }
}


/* --------------------------------------------------------------------------
   Reading progress (small screens only)
   -------------------------------------------------------------------------- */

/* Purely decorative — aria-hidden in the markup. It repeats what the
   scrollbar already says, so exposing it to assistive tech would add
   chatter, not information. */
.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 600;
  height: 3px;
  background: rgba(19, 23, 45, 0.15);
  pointer-events: none;
}
.reading-progress__bar {
  display: block;
  height: 100%;
  background: var(--yellow);
  transform: scaleX(0);
  transform-origin: left center;
  /* No transition: this value is driven directly by scroll position, and
     easing it would add motion the reader did not ask for. */
}

/* Above the breakpoint the sticky TOC does this job, and the nav is
   sticky at the top where the bar would sit. */
@media (min-width: 62rem) {
  .reading-progress {
    display: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .reading-progress {
    display: none;
  }
}


/* --------------------------------------------------------------------------
   Article furniture
   -------------------------------------------------------------------------- */

.post__hero {
  margin: 0 0 var(--sp-md);
  max-width: var(--max-w-narrow);
}
.post__hero img {
  display: block;
  width: 100%;
  height: auto;
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
}

.post__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5em;
  margin-bottom: var(--sp-sm);
  font-family: var(--font-heading);
  font-size: 0.85rem;
  color: var(--muted);
}
.post__meta--updated {
  margin-bottom: var(--sp-md);
  font-style: italic;
}
.post__meta-sep {
  color: var(--border-light);
}

.post__tags {
  margin-top: var(--sp-md);
}

/* Draft banner — an unpublished article reachable only by direct URL. */
.blog-draft-notice {
  margin-bottom: var(--sp-md);
  padding: var(--sp-sm);
  font-weight: 700;
  color: var(--dark-blue);
  background: var(--pink);
  border-left: var(--px) solid var(--dark-blue);
}

/* Front-matter validation report. Only ever rendered on localhost — see
   blog_render_errors() in blog/lib/chrome.php. */
.blog-errors {
  margin-bottom: var(--sp-md);
  padding: var(--sp-sm);
  color: var(--dark-blue);
  background: var(--pink);
  border: 2px solid var(--border);
}
.blog-errors ul {
  list-style: disc;
  padding-left: 1.5em;
}
.blog-errors li {
  margin-bottom: 0.5em;
}
.blog-errors__note {
  font-size: 0.85rem;
  font-style: italic;
}


/* --------------------------------------------------------------------------
   Article index
   -------------------------------------------------------------------------- */

.post-list {
  list-style: none;
  display: grid;
  gap: var(--sp-lg);
  margin: 0;
  padding: 0;
}
.post-card article {
  display: grid;
  gap: var(--sp-sm);
}
@media (min-width: 48rem) {
  .post-card article {
    grid-template-columns: 20rem minmax(0, 1fr);
    gap: var(--sp-md);
    align-items: start;
  }
}
.post-card__media {
  display: block;
  border: 2px solid var(--dark-blue);
  box-shadow: var(--shadow-px);
  line-height: 0;
}
.post-card__media img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
.post-card__title {
  margin-bottom: 0.3em;
}
.post-card__title a {
  color: var(--dark-blue);
  text-decoration: none;
}
.post-card__title a:hover {
  color: var(--medium-blue);
  text-decoration: underline;
}
.post-card__summary {
  max-width: var(--max-w-narrow);
  margin-bottom: var(--sp-sm);
}

.blog-feed-link {
  margin-top: var(--sp-lg);
  padding-top: var(--sp-sm);
  font-size: 0.9rem;
  border-top: 1px solid var(--border-light);
}
