/* Shared tokens and site chrome. Palette: Silver, Glacier, Cosmic Orange, Deep Blue, Burgundy, Black (hex values approximated by eye from the iPhone Pro finishes). */
:root {
  --bg: #E8EAEE;            /* Silver */
  --panel: #F6F7F9;
  --paper: #EDEFF2;         /* blog page background */
  --grid: #D0DCE5;          /* Glacier tint, graph-paper lines */
  --ink: #17171A;           /* Black */
  --soft: #555B6A;
  --rule: #C9CDD6;
  --primary: #26324F;       /* Deep Blue */
  --on-primary: #FFFFFF;
  --accent: #E5702C;        /* Cosmic Orange: shapes and highlights only, never body text */
  /* The same orange, darkened until a line of it carries 3:1 against all three page
     backgrounds. Cosmic Orange itself measures 2.6 to 2.9 there, which is fine under
     dark text on an orange fill and not fine as an edge, an underline or a selected
     state, where that ratio is the whole signal. So a fill keeps --accent and
     anything drawn as a line uses this. The dark page is dark enough that Cosmic
     Orange clears 3:1 by itself, so there the two are the same colour. */
  --accent-line: #C75716;
  --on-accent: #17171A;
  --burgundy: #6A1F36;      /* Burgundy: headings, margin notes */
  --mark: #F2A064;          /* highlighter */
  --tok-str: #B4551B;       /* strings in code, warm enough to read against the keywords */
  /* Numbers in code are text, so they owe 4.5:1 against the panel the block is set
     on, which Cosmic Orange does not pay on a near-white panel. This is the orange
     taken down to 4.6:1 and pushed red, away from the strings' brown, so the two
     warm tokens still read as two things. In dark mode the orange already pays. */
  --tok-num: #CC3D14;
  --wall: #26324F;          /* simulation */
  --heat-lo: 203, 221, 232; /* simulation: Glacier */
  --heat-hi: 70, 100, 150;
  --sans: "Albert Sans", "Avenir Next", "Segoe UI", system-ui, sans-serif;
  --display: "Sora", "Avenir Next", "Segoe UI", system-ui, sans-serif;
  --serif: "Spectral", "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  /* The hand. Three things on this site are written rather than typeset: a margin
     note, a callout's title and the date on a post's row in the Blogs list. They are
     the three places where somebody is annotating the page rather than writing it,
     which is exactly what a hand means. Nothing else uses it: the body stays serif,
     the interface sans, code and maths as they were. Patrick Hand has one weight and
     no italic, so weight and slope are not available here; size and the tilt below
     are. The fallbacks are the hands that ship with macOS and Windows, then the
     generic, so the three places still read as written when the web font is blocked. */
  --hand: "Patrick Hand", "Bradley Hand", "Segoe Print", "Chalkboard SE", cursive;
  /* How much bigger the hand has to be set to look the same size as the text beside
     it. Measured, not guessed: at a common size Patrick Hand's x-height is 46.8 to
     Spectral's 45 and Albert Sans's 50, so on x-height alone it needs 0.96 and 1.07.
     What makes it read small is width and weight, not height: the same sentence runs
     0.84 of Spectral's length and 0.78 of Albert Sans's, in a lighter stroke. These
     two numbers are those corrections, and they are multipliers rather than sizes so
     that each place keeps the size of its own context. */
  --hand-grow: 1.12;        /* beside the serif: margin notes */
  --hand-grow-ui: 1.18;     /* beside the sans: callout titles, the list dates */
  /* A note in the margin is written at a slight angle, because a hand holding a pen
     over a page already is. Under a degree: enough to see, not enough to read as a
     mistake or to cost the line any legibility. */
  --hand-tilt: -0.6deg;

  /* The pink line down the left of the page, as a notebook has. In dark mode the
     same 0.7 is a bright stripe against a near-black page, so it is turned down
     here rather than in the rule that draws it. */
  --margin-line-opacity: 0.7;

  /* Tracking is a function of size, never one value for every size. Letters read
     too far apart as type grows, and too close together as it shrinks, so large
     display text tightens and small interface text opens up. Leading moves the
     other way: tight on the big sizes, comfortable on body copy. */
  --track-name: -0.04em;    /* the name, 2.75rem and up */
  --track-display: -0.02em; /* large headings, roughly 1.75rem to 2.75rem */
  --track-head: -0.015em;   /* section headings, around 1.125rem */
  --track-body: 0em;        /* body copy, 1rem to 1.1875rem */
  --track-small: 0.01em;    /* captions and controls, 0.9375rem and below */

  /* ---------- Layout ----------
     One set of sizes for every page, so the home page, the Blogs list and the posts
     share a measure instead of each carrying its own numbers. Everything is in rem,
     so the whole layout grows with the root size below and with the reader's own
     text-size setting.

       --page-max    the outer shell on the home page and the Blogs list, padding
                     included (box-sizing: border-box)
       --page-post   the same shell on a post, which is wider because a post lays
                     out three columns where those two pages lay out one
       --pad-l       the page's left padding at its widest, which has the pink
                     margin line to clear
       --pad-r       its right padding, which has nothing to clear
       --toc-col     the contents list, in a column of its own left of the article
       --article     the reading column on a post: maths, code, tables and charts
                     may use all of it
       --margin-col  the strip right of the article, where the margin notes go
       --gap         the space between one column and the next
       --measure     how long a line of serif body copy is allowed to get, which is
                     shorter than the article column on purpose: 70 to 75 characters
       --measure-ui  the same line length in the interface sans, whose zero is wider
                     than the serif's, so the same ch count would run much longer

     A post is three columns, and this is the arithmetic that says where it stops
     being three:

       --toc-col  +  --gap  +  --article  +  --gap  +  --margin-col
           12     +    3    +     46      +    3    +      16        =  80rem

     of content box. The page's own padding is --pad-l + --pad-r = 8rem on top of
     that, and the pink margin line is drawn inside the left padding, at 3rem of the
     5.5rem, so it asks for nothing of its own. 80 + 8 = 88rem is therefore both the
     shell a post needs, --page-post, and the narrowest viewport that can hold the
     three columns. So 88rem is the breakpoint: at or above it the contents list is a
     sticky column on the left and the notes have the right; below it the list is a
     disclosure above the article and the notes drop inline, in one column of at
     most --article.

     The breakpoint is in rem and not in pixels, and the root size below has a floor
     of 1rem, so a reader who has asked for larger text gets the one column layout
     at the width where three columns would stop fitting for them rather than at
     somebody else's 1408px. A classic scrollbar is not part of a media query's
     width, so on a desktop that has one the switch happens at a window about 1423px
     wide. The media queries in blog.css spell 88rem out, because a media query
     cannot read a custom property.

     74rem, the old two column breakpoint, is still in blog.css for the one thing
     that was never about the columns: the pink line moves in towards the edge of a
     narrow page. */
  --page-max: 84rem;
  --page-post: 88rem;
  --pad-l: 5.5rem;
  --pad-r: 2.5rem;
  --toc-col: 12rem;
  --article: 46rem;
  --margin-col: 16rem;
  --gap: 3rem;
  --measure: 64ch;
  --measure-ui: 50ch;
  color-scheme: light;
}

/* ---------- Root size ----------
   Up to 1440px this is the browser's own text size, whatever the reader has set it
   to. Past that the page has more room than a line of prose should use, so the root
   grows smoothly to 118 percent by 2560px and the rem-based layout grows with it.
   The floor and the ceiling are rem, which on the root element means the browser's
   setting, so a reader who has asked for larger text still gets it; only the slope
   in between is measured in pixels. 0.18rem of growth spread over 70rem of viewport
   is 0.0025714 of a rem per pixel, which at a 16px default is the 0.0411px per
   pixel below. */
html { font-size: clamp(1rem, calc(1rem + (100vw - 90rem) * 0.0025714), 1.18rem); }
/* ---------- Dark ----------
   The graph paper is the one thing that cannot simply be inverted. Ruled lines are
   meant to be felt rather than read, and a line that is clearly visible on white
   becomes a lit grid on black: at #1F2430 on this paper the lines carried a contrast
   ratio of 1.19, which is more than the text in some interfaces. #1A1D26 is 1.09,
   so the squares are still there when looked for and gone when read past. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #101114; --panel: #181A21; --paper: #12141A; --grid: #1A1D26;
    --ink: #E8E9EC; --soft: #9AA1B0; --rule: #2A2D38;
    --primary: #CBDDE8; --on-primary: #101114; --on-accent: #17171A;
    --accent-line: #E5702C; --tok-num: #E5702C;
    --burgundy: #E39BB0; --mark: #8F4B1F; --tok-str: #F0A070;
    --wall: #34456F; --heat-lo: 42, 58, 92; --heat-hi: 203, 221, 232;
    --margin-line-opacity: 0.4;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #101114; --panel: #181A21; --paper: #12141A; --grid: #1A1D26;
  --ink: #E8E9EC; --soft: #9AA1B0; --rule: #2A2D38;
  --primary: #CBDDE8; --on-primary: #101114; --on-accent: #17171A;
    --accent-line: #E5702C; --tok-num: #E5702C;
  --burgundy: #E39BB0; --mark: #8F4B1F; --tok-str: #F0A070;
  --wall: #34456F; --heat-lo: 42, 58, 92; --heat-hi: 203, 221, 232;
  --margin-line-opacity: 0.4;
  color-scheme: dark;
}

* { box-sizing: border-box; }
html { background: var(--bg); }
body { margin: 0; background: var(--bg); color: var(--ink); font: 400 1.0625rem/1.6 var(--sans); letter-spacing: var(--track-body); }
a { color: var(--primary); text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
a:hover { color: var(--burgundy); }
:focus-visible { outline: 3px solid var(--burgundy); outline-offset: 3px; }

/* ---------- Skip link ----------
   First in the tab order, and nowhere on the page until it is tabbed to. Clipped
   rather than display: none, so it is still in the accessibility tree; nothing
   slides or fades, it is simply not there and then it is, which is what a reader
   who has asked for less motion would get anyway. */
.skip { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip:focus { position: fixed; z-index: 20; top: 0.5rem; left: 0.5rem; width: auto; height: auto; margin: 0; clip-path: none; padding: 0.7rem 1rem; background: var(--primary); color: var(--on-primary); font: 600 0.9375rem/1 var(--sans); letter-spacing: var(--track-small); text-decoration: none; }
/* The main element is only focusable so that the skip link can land on it. Arriving
   there is shown by the page having scrolled, not by a ring round everything. */
main[tabindex="-1"]:focus { outline: none; }
.wrap { max-width: var(--page-max); margin: 0 auto; padding: 0 clamp(1rem, 4vw, 2.5rem); }

.top { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; padding: 1.5rem 0; }
.brand { font: 700 1.0625rem/1.2 var(--display); letter-spacing: var(--track-body); text-decoration: none; color: var(--ink); }
.top nav { display: flex; gap: 1.25rem; align-items: baseline; }
.top nav a { color: var(--ink); }
.top nav a[aria-current] { text-decoration: underline; text-decoration-thickness: 2px; text-decoration-color: var(--accent-line); }
.theme-btn { font: 500 0.9375rem/1.4 var(--sans); letter-spacing: var(--track-small); color: var(--ink); background: transparent; border: 2px solid var(--ink); padding: 0.2rem 0.6rem; cursor: pointer; white-space: nowrap; }
.theme-btn:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.foot { padding: 2rem 0 3rem; color: var(--soft); font-size: 0.9375rem; line-height: 1.5; letter-spacing: var(--track-small); }

/* ---------- Press feedback ----------
   The highlight belongs on the press, not on the release: a pointer-down that waits
   for the click to show anything feels dead, and a touch screen never hovers. :active
   fires the moment the pointer goes down, so a tap gets the same accent the mouse gets
   on hover, plus a small scale. 100ms, because it has to be gone before the finger is. */
.theme-btn, .chip { transition: transform 100ms ease-out, background-color 100ms ease-out, border-color 100ms ease-out, color 100ms ease-out; }
.theme-btn:active { transform: scale(0.97); background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.chip:active { transform: scale(0.97); border-color: var(--burgundy); }

/* ---------- Theme switch ----------
   theme.js puts this class on <html> for the length of the change and takes it off
   again, so light and dark cross over instead of snapping. Colours only: nothing moves. */
html.theme-anim, html.theme-anim *, html.theme-anim *::before, html.theme-anim *::after {
  transition-property: background-color, border-color, color, fill, stroke, outline-color, text-decoration-color;
  transition-duration: 200ms;
  transition-timing-function: ease;
}

/* ---------- Reduced motion ----------
   Nothing moves and nothing eases. The press still answers, because the accent
   highlight arrives on pointer-down either way: feedback without motion. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  .theme-btn:active, .chip:active { transform: none; }
}

/* ---------- More contrast ----------
   Secondary text and hairline rules carry the least contrast on the page, so they
   are the ones to firm up when the reader has asked for more. */
@media (prefers-contrast: more) {
  .foot { color: var(--ink); }
  a { text-decoration-thickness: 2px; }
}
