/*
 * Site styles. Replaces w3.css.
 *
 * Everything visual goes through the custom properties below. Adding a dark
 * mode or a second theme should mean overriding tokens here, not editing rules
 * throughout the file.
 */

:root {
  color-scheme: light;

  /* Ink and paper. Slightly warm rather than pure black on pure white, which
     is harsh for long reading. */
  --paper: #fdfcfa;
  --paper-raised: #ffffff;
  --ink: #1c1b19;
  --ink-muted: #5f5b54;
  --rule: #e2ded6;
  --rule-strong: #cfc9be;

  --accent: #3a4a7a;
  --accent-hover: #26325a;
  --accent-wash: #eef0f7;

  /* Prose is set in a serif so it sits with KaTeX, which renders maths in a
     Computer Modern-like serif. On the maths-heavy pages a sans body made the
     equations look pasted in. */
  --font-prose:
    Charter, "Bitstream Charter", "Sitka Text", Cambria, Georgia, serif;
  --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono:
    ui-monospace, SFMono-Regular, "JetBrains Mono", Menlo, Consolas, monospace;

  --measure: 68ch;
  --step: 0.5rem;
  --radius: 6px;

  --shadow: 0 1px 2px rgb(28 27 25 / 6%), 0 4px 12px rgb(28 27 25 / 4%);
  --shadow-raised: 0 2px 4px rgb(28 27 25 / 8%), 0 8px 24px rgb(28 27 25 / 8%);
}

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

html {
  /* Fluid, but bounded: never smaller than comfortable, never sprawling. */
  font-size: clamp(1.0625rem, 0.98rem + 0.4vw, 1.1875rem);
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-prose);
  line-height: 1.65;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* --- Base elements ------------------------------------------------------
 * Kept before the component rules below so specificity ascends down the
 * file and the cascade reads in source order.
 */

h1,
h2,
h3,
h4 {
  font-family: var(--font-ui);
  line-height: 1.25;
  letter-spacing: -0.015em;
  text-wrap: balance;
  margin: calc(var(--step) * 5) 0 var(--step);
}

h1 {
  font-size: clamp(1.9rem, 1.5rem + 1.7vw, 2.6rem);
  margin-top: 0;
  font-weight: 650;
}

h2 {
  font-size: 1.45rem;
  font-weight: 600;
}

h3 {
  font-size: 1.15rem;
  font-weight: 600;
}

/* The header is sticky, so anything scrolled to by a fragment link would land
   underneath it. Nothing emits heading ids yet; this keeps a future table of
   contents, footnote or `#anchor` from arriving hidden. Affects scrolling only,
   never layout. */
:target,
h1,
h2,
h3,
h4 {
  scroll-margin-top: calc(var(--step) * 9);
}

p,
ul,
ol {
  margin: 0 0 calc(var(--step) * 2.5);
}

li {
  margin-bottom: calc(var(--step) * 0.5);
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

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

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

hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: calc(var(--step) * 6) 0;
}

/* Containers should not inherit their last child's trailing margin. :where()
   contributes no specificity, so this never competes with the rules below. */
:where(.card, blockquote, figure, .site-footer__inner) > :last-child {
  margin-bottom: 0;
}

:where(.card) > :first-child {
  margin-top: 0;
}

/* --- Site header ---------------------------------------------------------
 * A bar at every width, sticky at the top of the viewport.
 */

/* Sticky, so the four destinations stay reachable in a long post. The
   background is opaque paper rather than translucent, so scrolled text never
   shows through and no backdrop-filter is needed. `header` is a direct child of
   `body`, so no ancestor overflow can break the sticking. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}

/* A ~56px bar is a large share of a short viewport -- a phone in landscape, or
   a window at 400% zoom, where reflow already leaves little room. Give the
   space back there rather than pinning the bar over the text. */
@media (max-height: 30rem) {
  .site-header {
    position: static;
  }
}

/* The nav is short enough to fit at every width, so there is no disclosure and
   no toggle script. Centred with the content rather than pushed to one side,
   which reads as a deliberate bar rather than a leftover. */
.site-nav {
  max-width: var(--measure);
  margin-inline: auto;
  padding: calc(var(--step) * 1.25) calc(var(--step) * 2);
  display: flex;
  align-items: center;
  gap: calc(var(--step) * 0.5);
  font-family: var(--font-ui);
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--step) * 0.5);
  font-size: 0.95rem;
  line-height: 1;
  color: var(--ink-muted);
  text-decoration: none;
  padding: calc(var(--step) * 0.9) calc(var(--step) * 1.5);
  border-radius: 999px;
  transition:
    background 120ms ease,
    color 120ms ease;
}

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

/* The current page is filled rather than merely underlined, so it is obvious
   at a glance and does not rely on colour alone: the shape changes too. */
.site-nav a[aria-current="page"] {
  background: var(--accent);
  color: var(--paper-raised);
  font-weight: 600;
}

.site-nav a[aria-current="page"]:hover {
  background: var(--accent-hover);
  color: var(--paper-raised);
}

/* The four items are about 327px wide at the default padding, which overflows a
   320px screen. Tighten rather than wrap, so the bar stays one line. */
@media (max-width: 24rem) {
  .site-nav {
    padding-inline: calc(var(--step) * 1.25);
    gap: calc(var(--step) * 0.25);
  }

  .site-nav a {
    padding-inline: var(--step);
    font-size: 0.9rem;
  }
}

/* --- Page body ----------------------------------------------------------- */

main {
  max-width: var(--measure);
  margin-inline: auto;
  padding: calc(var(--step) * 5) calc(var(--step) * 2) calc(var(--step) * 8);
  /* Opaque, so a page backdrop can never appear behind running text: it is
     confined to the margins beside this column. */
  background: var(--paper);
}

.muted {
  color: var(--ink-muted);
  font-family: var(--font-ui);
  font-size: 0.85rem;
}

.center {
  text-align: center;
}

.no-wrap {
  white-space: nowrap;
}

.pre-wrap {
  white-space: pre-wrap;
}

/* The date under a post title. */
.post-date {
  display: block;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--ink-muted);
  margin: calc(var(--step) * -0.5) 0 calc(var(--step) * 4);
}

/* --- Cards ---------------------------------------------------------------
 * Used for post teasers, publications, lectures and comments, so the eventual
 * landing page can mix all three without new styles.
 */

.card {
  display: block;
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: calc(var(--step) * 2.5) calc(var(--step) * 3);
  margin-bottom: calc(var(--step) * 2.5);
}

/* Teasers wrap the card in a link, so the hover target is the anchor. */
.card-link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.card-link .card {
  transition:
    box-shadow 120ms ease,
    border-color 120ms ease,
    transform 120ms ease;
}

.card-link:hover .card {
  border-color: var(--rule-strong);
  box-shadow: var(--shadow-raised);
  transform: translateY(-1px);
}

.card-link:hover h2,
.card-link:hover h3 {
  color: var(--accent);
}

.card-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--radius);
}

.card h2,
.card h3 {
  margin-top: 0;
  margin-bottom: calc(var(--step) * 0.5);
  font-size: 1.2rem;
  /* `balance` shortens every line to even them up, which on a narrow card
     turns a two-word title into two lines. */
  text-wrap: pretty;
}

/* An illustrated card floats its picture, so the text wraps beside it and then
   continues full width below. A fixed side column left short teasers sitting
   next to a tall empty gap, and squeezed long ones into a narrow ribbon. */
.card--illustrated {
  /* Contain the float, or it escapes the card's border. */
  display: flow-root;
}

.card--illustrated .card-image {
  float: right;
  margin: 0 0 var(--step) calc(var(--step) * 2.5);
}

.card--illustrated .card-image--left {
  float: left;
  margin: 0 calc(var(--step) * 2.5) var(--step) 0;
}

.card--illustrated h3 {
  /* Above the float, so the title always gets the full width. */
  clear: both;
}

@media (max-width: 34rem) {
  /* Narrow cards read better with the picture on the left, under the title. */
  .card--illustrated .card-image {
    float: left;
    margin: 0 calc(var(--step) * 1.5) calc(var(--step) * 0.5) 0;
  }
}

.card__teaser {
  margin-top: var(--step);
  color: var(--ink-muted);
}

/* Comments are rendered by public/scripts/comments.js, which builds the same
   card markup used elsewhere. Replies nest, so the indent has to come from the
   nesting rather than a modifier class. */
.comment {
  font-size: 0.95rem;
}

.comment .comment {
  margin-top: calc(var(--step) * 2);
  margin-bottom: 0;
  margin-left: calc(var(--step) * 2);
  box-shadow: none;
  background: var(--paper);
}

.comment__author {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 0.9rem;
  margin-bottom: 0;
}

.comment--deleted {
  background: var(--paper);
}

/* --- Reference links ----------------------------------------------------
 * These sit between paragraphs, sometimes mid-sentence, so they must read as
 * an aside rather than compete with the prose. A full card was far too loud.
 */

.reference-link {
  display: block;
  margin: calc(var(--step) * 2) 0;
  padding: calc(var(--step) * 1.25) calc(var(--step) * 2);
  border-left: 3px solid var(--accent);
  background: var(--accent-wash);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-family: var(--font-ui);
  font-size: 0.95rem;
  color: var(--accent);
  text-decoration: none;
}

.reference-link::after {
  content: " \2192";
}

.reference-link:hover {
  color: var(--accent-hover);
  background: color-mix(in srgb, var(--accent-wash) 70%, var(--accent) 8%);
}

/* --- Quotations ----------------------------------------------------------- */

blockquote {
  margin: calc(var(--step) * 3) 0;
  padding: calc(var(--step) * 0.5) 0 calc(var(--step) * 0.5)
    calc(var(--step) * 3);
  border-left: 3px solid var(--rule-strong);
  color: var(--ink-muted);
  font-style: italic;
}

/* Wraps an image in a link: block-level, and no underline under the picture. */
.media-link {
  display: block;
  text-decoration: none;
}

/* --- Media ---------------------------------------------------------------- */

figure {
  margin: calc(var(--step) * 4) 0;
  text-align: center;
}

img,
video,
picture {
  max-width: 100%;
  height: auto;
}

.figure-media {
  display: block;
  margin-inline: auto;
  border-radius: var(--radius);
}

figcaption {
  display: block;
  margin-top: var(--step);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--ink-muted);
  text-align: center;
}

/* A <caption> must keep display:table-caption; setting display:block detaches
   it from the table and silently disables caption-side. */
caption,
.caption {
  caption-side: bottom;
  margin-top: var(--step);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--ink-muted);
  text-align: center;
}

/* --- Code ----------------------------------------------------------------- */

code,
kbd,
samp {
  font-family: var(--font-mono);
  font-size: 0.875em;
}

.code-inline {
  /* Not --accent-wash: the highlight.js palette is calibrated against a white
     background, and its #aa5d00 fell to 4.31 contrast on a blue-tinted one. */
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 0.1em 0.35em;
  white-space: nowrap;
}

.code-block {
  margin: calc(var(--step) * 3) 0;
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow-x: auto;
}

.code-block pre {
  margin: 0;
  padding: calc(var(--step) * 2) calc(var(--step) * 2.5);
  line-height: 1.55;
}

.code-block code {
  font-size: 0.85rem;
}

/* --- Tables --------------------------------------------------------------- */

table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  margin: calc(var(--step) * 3) 0;
}

td,
th {
  padding: calc(var(--step) * 0.75) var(--step);
  border-bottom: 1px solid var(--rule);
  text-align: left;
}

/* --- Maths ---------------------------------------------------------------- */

.katex-display {
  margin: calc(var(--step) * 3.5) 0;
  overflow-x: auto;
  overflow-y: hidden;
  padding-block: 2px;
}

/* --- Footer --------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--rule);
  margin-top: calc(var(--step) * 8);
  background: var(--paper);
}

.site-footer__inner {
  max-width: var(--measure);
  margin-inline: auto;
  padding: calc(var(--step) * 3) calc(var(--step) * 2);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--ink-muted);
}
