/* ─────────────────────────────────────────────────────────────────────────────
   Retirement Scenario Modeler — blog stylesheet

   The @font-face block and the two token blocks below are COPIES of the ones in
   `src/index.css`, which is the source of truth. They are duplicated rather than
   shared because src/index.css is a Tailwind v4 entrypoint — `@import
   "tailwindcss"` mid-file, `@custom-variant`, PostCSS — and Jekyll has no build
   step that could resolve any of it. Copying is the honest trade; silent drift
   is the risk it buys, so `src/test/blogThemeParity.test.ts` fails the frontend
   suite the moment a token's value differs between the two files.

   Everything below the tokens is the blog's own, and deliberately reuses the
   app's class names (.icon-btn, .btn-primary, .consent-banner …) with the same
   rules, so the two halves of the site read as one.
   ───────────────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/DMMono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/DMMono-400-latin.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: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/DMMono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/DMMono-500-latin.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: 'Fraunces';
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url('../fonts/Fraunces-300i-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url('../fonts/Fraunces-300i-latin.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: 'Fraunces';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('../fonts/Fraunces-300-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('../fonts/Fraunces-300-latin.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: 'Fraunces';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/Fraunces-300-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/Fraunces-300-latin.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: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/Inter-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/Inter-400-latin.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: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/Inter-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/Inter-400-latin.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: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/Inter-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/Inter-400-latin.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;
}
/* ─── Light theme (default) ────────────────────────── */
:root {
  /* Type. Body is a humanist sans: this app is text-heavy (Insights, How It
     Works, every explainer note) and monospace measurably slows prose reading
     by flattening word shapes. Mono is kept for figures, where the uniform
     advance width is the actual benefit. Fraunces stays on headings. */
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-display: 'Fraunces', Georgia, serif;

  /* Audience skews 45+, where presbyopia is near-universal. The old scale
     bottomed out at 9px; nothing here goes below 11px and body is 15px. */
  --fs-micro: 11px;   /* chips, badges, table headers - never body copy */
  --fs-small: 13px;
  --fs-body:  15px;
  --fs-lead:  16px;

  --bg: #f7f8fa;
  --surface: #ffffff;
  --surface2: #f0f2f5;
  --border: #dde1e7;
  --accent: #3d7a00;
  --accent2: #0070a0;
  --accent3: #c42020;
  --text: #111827;
  --muted: #4b5563;
  --warn: #8f5000;
  /* accent-pill: always bright lime, used for button/tab backgrounds */
  --accent-pill: #c8f060;
  --accent-pill-hover: #b8e050;
  --accent-pill-text: #111827;
}

/* ─── Dark theme ────────────────────────────────────── */
:root.dark {
  --bg: #0e0f11;
  --surface: #16181c;
  --surface2: #1e2128;
  --border: #2a2d35;
  --accent: #c8f060;
  --accent2: #60c8f0;
  --accent3: #f06060;
  --text: #e8eaf0;
  /* Was #6b7280, which measured 3.33-3.97:1 on our dark surfaces and failed
     WCAG AA (4.5:1) everywhere it was used - and it carries KPI sub-labels,
     table headers and every explainer. This clears 7:1. */
  --muted: #9aa3b2;
  --warn: #f0a060;
  --accent-pill: #c8f060;
  --accent-pill-hover: #d8ff70;
  --accent-pill-text: #0e0f11;
}


/* ─── Base reset (mirrors src/index.css) ───────────────────────── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  /* The app is a single screen; the blog scrolls. Anchor links from the contents
     rail should not land flush under the sticky header. */
  scroll-padding-top: 5rem;
}

/* No sticky header at this width (see the responsive block), so nothing has to
   be cleared. */
@media (max-width: 40rem) {
  html { scroll-padding-top: 1rem; }
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

body, .blog-header, .blog-main, .post-card, .btn-primary, .icon-btn {
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

/* Layout variables live here rather than in `:root` on purpose: they are
   layout, not theme, and the two `:root` blocks above are a pinned copy of the
   app's tokens (src/test/themeTokenCopies.test.ts). Nothing belongs in that
   block whose contract is not "identical to the app's".

   Everything on the page hangs off `--blog-inset`: header, text, footer rules. */
.blog-root {
  display: flex;
  flex-direction: column;
  min-height: 100vh;

  --blog-inset: clamp(1.25rem, 5vw, 4.5rem);
  /* ~68 characters at 15px Inter. Wider and the eye loses the line on the
     return sweep, which is the one thing the old centred layout had right. */
  --blog-measure: 40rem;
  --blog-rail: 13.5rem;
  --blog-gap: clamp(2rem, 4.5vw, 4.5rem);
}

/* ─── Header ───────────────────────────────────────────────────── */
.blog-header {
  border-bottom: 1px solid var(--border);
  /* Same inset as the text below it: the wordmark, every headline and the
     footer rule all sit on one left axis. */
  padding: 14px var(--blog-inset);
  display: flex;
  align-items: center;
  gap: 16px;
  flex-shrink: 0;
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 40;
}

.blog-wordmark {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 22px;
  color: var(--accent);
  letter-spacing: -0.5px;
  line-height: 1;
  text-decoration: none;
}

.blog-wordmark:hover { color: var(--accent); text-decoration: none; }

/* The app header uses a bare <span> as the flex spacer; same job here. */
.blog-header-spacer { flex: 1; }

/* Was 11px uppercase with 1.5px of tracking, i.e. the same eyebrow treatment
   as the post metadata. A masthead line wants to read like a masthead line, so
   it is set in the display serif's italic at text size instead. */
.blog-kicker {
  color: var(--muted);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: 15px;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
}

/* Matches .header-account in src/index.css — the "back to the app" pill. */
.blog-app-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--accent2);
  text-decoration: none;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 20px;
  white-space: nowrap;
}

.blog-app-link:hover { border-color: var(--accent2); text-decoration: none; }

/* ─── Buttons ─────────────────────────────────────────────────────
   The app sets these in tracked uppercase and the blog used to copy it. That is
   the same small-caps-label treatment the metadata and the section labels have
   just lost, and it was the last place on the page still using it — a tracked
   uppercase button sitting under a line of Fraunces italic reads as two
   different websites. Sentence case, no tracking.

   Cosmetic only: the consent banner's markup, behaviour and wording are
   untouched, and so is the app's own styling. */
button {
  font-family: var(--font-sans);
  font-size: 14px;
  letter-spacing: 0;
  text-transform: none;
  padding: 9px 16px;
  border-radius: 4px;
  cursor: pointer;
  border: none;
  transition: all 0.15s;
}

.btn-primary {
  background: var(--accent-pill);
  color: var(--accent-pill-text);
  font-weight: 500;
}

.btn-primary:hover { background: var(--accent-pill-hover); }

.icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: var(--muted);
  cursor: pointer;
  line-height: 0;
  font-size: inherit;
  letter-spacing: 0;
  text-transform: none;
  transition: all 0.15s;
}

.icon-btn:hover { border-color: var(--accent); color: var(--accent); }

/* The UA stylesheet's `[hidden] { display: none }` is scoped to the HTML
   namespace, so an <svg hidden> still paints — which showed all three theme
   icons at once. The attribute reflects fine; only the default rule is missing. */
svg[hidden] { display: none; }

/* ─── Focus (mirrors src/index.css) ────────────────────────────── */
:focus-visible {
  outline: 2px solid var(--accent2);
  outline-offset: 2px;
  border-radius: 3px;
}

.btn-primary:focus-visible,
.icon-btn:focus-visible {
  outline: 2px solid var(--accent2);
  outline-offset: 2px;
}

/* ─── Layout ─────────────────────────────────────────────────────
   An asymmetric editorial grid, flushed left, rather than one centred column.

   The blog deliberately parts company with the app here. The app is a single
   screen of controls and a centred panel is right for it; a blog is a reading
   surface, and a symmetric column floating in equal margins is both the least
   interesting thing to do with a page and the most obviously templated. So:
   text on a fixed left axis that the wordmark, the rules and the footer all
   share, a measure sized for reading rather than for the window, and a narrow
   right-hand rail for the things that are ABOUT the writing (topics, dates,
   tags) rather than part of it.

   The rail is also what fixes the eyebrow problem. Metadata set in small wide
   caps above a headline is a house style this site had on every post; moved
   into the margin it stops competing with the title and stops looking like a
   kicker. See `.post-meta` and `.blog-rail` below.
   ───────────────────────────────────────────────────────────────── */

.blog-main {
  flex: 1;
  width: 100%;
  max-width: calc(var(--blog-inset) + var(--blog-measure) + var(--blog-gap) + var(--blog-rail) + 2rem);
  /* `0 auto 0 0`, not `0 auto`. This is the line that stops the page reading as
     a generated one: the content keeps a left axis and the slack collects on
     the right, where the rail already lives. */
  margin: 0 auto 0 0;
  padding: 52px var(--blog-inset) 72px;
  display: grid;
  grid-template-columns: minmax(0, var(--blog-measure)) var(--blog-rail);
  column-gap: var(--blog-gap);
  align-items: start;
}

/* Explicit placement, because the rail sits SECOND in the document and second
   is where it belongs: a filter should be read before the list it filters. On
   one column that order is exactly right, so nothing needs reordering there. */
/* Index: the intro spans both columns and rules off underneath, which gives the
   page one strong horizontal axis; the list and the rail then share row 2. */
.blog-main > .blog-intro { grid-column: 1 / -1; grid-row: 1; }
.blog-main > .blog-flow { grid-column: 1; grid-row: 2; }
.blog-main > .blog-rail { grid-column: 2; grid-row: 2; }

/* Post: the article keeps the reading measure in column 1 and its contents rail
   sits alongside in the same row. Letting the article stretch across both
   columns would throw away the measure this whole grid exists to protect. */
.blog-main > .prose { grid-column: 1; grid-row: 1; }
.blog-main > .blog-rail.is-post { grid-column: 2; grid-row: 1; }

/* A post is one column of prose; the rail on those pages is inside the article
   header, not a sibling of it. */
.blog-main.is-wide { --blog-measure: 52rem; }

/* ─── Right rail ───────────────────────────────────────────────── */
.blog-rail {
  position: sticky;
  /* Clears the sticky header. */
  top: 5.25rem;
  padding-left: 1.5rem;
  border-left: 1px solid var(--border);
}

.blog-rail[hidden] { display: none; }

/* Not an eyebrow: serif, mixed case, italic. The uppercase-and-tracked label is
   the exact motif this restyle is removing, so the rail must not reintroduce
   it one element later. */
.rail-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: 15px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
  margin-bottom: 14px;
}

/* ─── Skip link ────────────────────────────────────────────────── */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: 4px;
  padding: 10px 16px;
  z-index: 100;
}

.skip-link:focus {
  left: 12px;
  top: 12px;
}

/* ─── Index page ───────────────────────────────────────────────── */
.blog-intro {
  /* Spans both columns and rules off across the whole grid. Without this the
     narrow text column and the rail read as two unrelated things adrift on a
     wide page; with it they hang off a shared horizontal edge. */
  padding-bottom: 30px;
  margin-bottom: 34px;
  border-bottom: 1px solid var(--border);
}

.blog-intro > * { max-width: var(--blog-measure); }

.blog-intro h1 {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(32px, 4.4vw, 44px);
  line-height: 1.08;
  letter-spacing: -0.8px;
  margin-bottom: 14px;
  /* Optical alignment: the serif's left sidebearing makes a flush-left heading
     look inset next to the body text underneath it. */
  margin-left: -0.04em;
}

.blog-intro p {
  color: var(--muted);
  font-size: var(--fs-lead);
  max-width: 34rem;
}

/* ─── Post index: a hairline list, not a deck of cards ────────────
   Cards were the wrong container. Five of them, identical, evenly spaced, each
   with a rounded border and an accent rule across the top, turn an archive into
   a product grid and flatten the difference between the posts. A ruled list is
   what an archive has looked like in print for a century, it puts the titles on
   one axis so they can be scanned, and it leaves the date somewhere other than
   directly above the headline. */
.post-list { list-style: none; }

.post-item {
  display: grid;
  grid-template-columns: 6rem minmax(0, 1fr);
  column-gap: 1.75rem;
  padding: 26px 0;
  border-top: 1px solid var(--border);
}

/* The heading above already acts as the opening rule. */
.post-item:first-child { border-top: none; padding-top: 4px; }
.post-item:last-child { padding-bottom: 4px; }

.post-item[hidden] { display: none; }

.post-item h2 {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 26px;
  line-height: 1.18;
  letter-spacing: -0.4px;
  margin-left: -0.03em;
}

.post-item h2 a {
  color: var(--text);
  text-decoration: none;
  /* An underline that appears from below on hover, rather than a colour swap:
     the title is the primary thing on the row and it should not need to change
     colour to prove it is a link. */
  border-bottom: 1px solid transparent;
}

.post-item h2 a:hover {
  color: var(--text);
  border-bottom-color: var(--accent);
}

.post-item p {
  color: var(--muted);
  font-size: var(--fs-small);
  margin-top: 7px;
  max-width: 32rem;
}

/* ─── Metadata ────────────────────────────────────────────────────
   Was: 11px, uppercase, 2px of tracking, sitting directly above the headline.
   That is the "eyebrow", and it is the pattern this restyle exists to remove.
   Now it is mono, mixed case, untracked, and in the left gutter beside the
   title rather than stacked over it. */
.post-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  line-height: 1.5;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}

.post-item .post-meta {
  /* Sits on the title's first baseline rather than its box top. */
  padding-top: 0.5em;
  text-align: right;
}

.post-meta time { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* In the gutter the two facts stack; the separator only earns its space when
   they are inline (post header, narrow screens). */
.post-item .post-meta > * { display: block; }
.post-item .post-meta-sep { display: none; }
.post-meta-sep { opacity: 0.45; }

.post-item .post-reading { opacity: 0.75; }

/* ─── Tags ────────────────────────────────────────────────────────
   Text, not pills. A pill is a control, and these are references; a row of
   bordered capsules under every entry was doing more visual work than the
   titles above them. */
.tag-row {
  display: flex;
  gap: 0 0.9rem;
  flex-wrap: wrap;
  margin-top: 11px;
}

.post-header .tag-row { margin-top: 12px; }

.tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
  padding: 0;
  border: none;
  border-radius: 0;
}

a.tag {
  text-decoration: none;
  border-bottom: 1px solid var(--border);
  transition: color 0.15s, border-color 0.15s;
}

a.tag:hover { color: var(--accent); border-bottom-color: var(--accent); }

.tag-status {
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  color: var(--muted);
  margin: 0 0 18px;
}

.tag-status[hidden] { display: none; }

/* ─── Topic cloud (the right rail) ────────────────────────────────
   The filter used to be a row of equal-weight pills across the top of the page,
   which put a control where the reader expects the first headline and told them
   nothing about the archive. In the rail, sized by how much has been written on
   each topic, it does the opposite: it is a shape of the blog's subject matter
   that happens to be clickable.

   Sizes are five discrete steps, not a continuous scale — with a handful of
   posts a linear map produces near-identical sizes, and past a certain spread
   the biggest word swamps everything. Steps come from Liquid in index.html. */
.topic-cloud {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.3rem 0.75rem;
}

.topic {
  font-family: var(--font-display);
  font-weight: 300;
  line-height: 1.15;
  letter-spacing: -0.2px;
  text-transform: none;
  background: none;
  border: none;
  padding: 0;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.15s;
  /* A long tag at the largest step is wider than the rail, and a single word
     cannot wrap on its own. Breaking is ugly but it is contained; overflowing
     puts text on top of the empty margin beside it. */
  max-width: 100%;
  overflow-wrap: break-word;
  text-align: left;
}

.topic:hover { color: var(--text); }

.topic[aria-pressed="true"] {
  color: var(--accent);
  font-weight: 600;
}

.topic-n {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0;
  /* Rides high like a footnote marker, so it reads as an annotation on the word
     rather than as part of it. */
  vertical-align: 0.45em;
  opacity: 0.55;
  margin-left: 2px;
}

/* The top step is bounded by the rail, not by taste: at 29px "methodology" was
   wider than the column, and `overflow-wrap` then broke the line at the only
   opportunity it had — between the word and its count — stranding a lone "3" on
   the next line. The range still reads as a cloud and the longest tag in use
   fits on one line with its count. */
.topic.is-w1 { font-size: 14px; }
.topic.is-w2 { font-size: 16px; }
.topic.is-w3 { font-size: 19px; }
.topic.is-w4 { font-size: 22px; }
.topic.is-w5 { font-size: 25px; }

/* The reset is not a topic and must not look like one competing for the largest
   size — "all" would always carry the highest count. */
.topic-reset {
  display: inline-block;
  margin-top: 18px;
  padding: 0;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: var(--fs-micro);
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
  cursor: pointer;
}

.topic-reset:hover { color: var(--accent); border-bottom-color: var(--accent); }
.topic-reset[aria-pressed="true"] { color: var(--muted); opacity: 0.5; cursor: default; }

.post-empty {
  color: var(--muted);
  border-top: 1px solid var(--border);
  padding: 28px 0;
}

/* ─── Contents rail (post pages) ──────────────────────────────────
   The grid gives every page a margin column. On the index it holds the topic
   cloud; on a post it would otherwise be empty, and an empty margin column
   makes a left-flushed page look like a mistake rather than a decision.

   Built from the article's own <h2>s by assets/js/toc.js, and only when there
   are at least three of them — a two-item table of contents is furniture. It
   ships hidden, so without JS a post is simply one column and nothing is
   broken. */
.blog-rail.is-post {
  /* Not sticky, unlike the index's cloud, and that is a trade rather than an
     oversight: a wide figure is allowed to run across this column (see
     `.prose figure.wide`), and a contents list pinned to the viewport would be
     scrolled over by every chart in the post. Parked at the top of the article
     it is out of the way after the first screen, which is also where a contents
     list conventionally lives. */
  position: static;
}

.toc-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.toc-list a {
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.35;
  text-decoration: none;
  display: block;
  /* Each item carries its own marginal rule, so the rail drops the one it
     shares with the index (below) rather than doubling it up. */
  border-left: 1px solid var(--border);
  padding-left: 11px;
  transition: color 0.15s, border-color 0.15s;
}

.toc-list a:hover { color: var(--text); border-left-color: var(--accent); }

.blog-rail.is-post {
  padding-left: 0;
  border-left: none;
}

/* Puts "Contents" on the same left axis as the item text rather than 1.5rem
   inside it, which is where the rail's old padding had left it. */
.blog-rail.is-post .rail-title { padding-left: 12px; }

/* ─── Post + page prose ────────────────────────────────────────── */
/* ─── Post header ─────────────────────────────────────────────────
   The title comes first now. The date and reading time follow it as a dateline
   rather than preceding it as a kicker, which is the whole of the eyebrow fix:
   the same two facts, one line lower, and the page stops announcing itself
   before it says anything. */
.post-header {
  margin-bottom: 36px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--border);
}

.post-header h1 {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(30px, 4.2vw, 42px);
  line-height: 1.08;
  letter-spacing: -0.8px;
  margin: 0 0 0 -0.04em;
}

.post-lead {
  font-size: var(--fs-lead);
  color: var(--muted);
  margin-top: 14px;
  max-width: 34rem;
}

/* Date, reading time and tags on one line under the title. */
.post-dateline {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 0.9rem;
  margin-top: 18px;
}

.post-dateline .post-meta { display: flex; gap: 0.55rem; }
.post-dateline .tag-row { margin-top: 0; }

.prose > * + * { margin-top: 1.15em; }

.prose h2 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 23px;
  line-height: 1.25;
  letter-spacing: -0.3px;
  margin-top: 2em;
  margin-bottom: 0.1em;
}

.prose h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: 0.2px;
  margin-top: 1.7em;
  margin-bottom: 0.1em;
}

.prose a {
  color: var(--accent2);
  text-decoration: underline;
  text-underline-offset: 2px;
}

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

.prose ul, .prose ol { padding-left: 1.35rem; }
.prose li + li { margin-top: 0.4em; }
.prose li::marker { color: var(--muted); }

.prose strong { font-weight: 600; }

.prose blockquote {
  border-left: 2px solid var(--accent);
  padding: 2px 0 2px 16px;
  color: var(--muted);
}

.prose hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2.5em 0;
}

.prose img {
  max-width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  display: block;
}

.prose figure { margin: 2em 0; }

/* The app's charts are about 3:1 and their axis labels are unreadable scaled
   down to the 46rem prose measure. A figure marked `wide` breaks out of the
   column and centres on it instead — the text keeps a comfortable line length
   and the chart gets the room it needs. `100vw` overshoots by the scrollbar
   width, which the 48px gutter absorbs. */
/* A chart wider than the text column.
   
   The old rule centred it on the viewport (`margin-left: 50%` plus a -50%
   translate), which worked only because the text column was itself centred.
   Against a left-flushed column the same arithmetic centres the figure on the
   COLUMN's midpoint, which at 1440px put its left edge 168px off the left of
   the screen and its right edge across the rail.
   
   So: same left axis as the text, extending right across the gap and the rail
   into the space the grid already reserves. The figure gets 58rem instead of
   the 40rem measure, and nothing leaves the page. */
.prose figure.wide {
  width: min(
    calc(var(--blog-measure) + var(--blog-gap) + var(--blog-rail)),
    calc(100vw - var(--blog-inset) * 2)
  );
  margin-left: 0;
  transform: none;
}

.prose figure figcaption {
  font-size: var(--fs-small);
  color: var(--muted);
  margin-top: 8px;
}

/* Screenshots of the app ship as a light/dark pair, because a post is read in
   whichever theme the visitor already chose in the calculator and a light
   screenshot glares out of a dark page. Both files are in the markup; this
   picks one. Keyed off `.dark` on the root, the same class theme.js toggles —
   NOT prefers-color-scheme, which would ignore an explicit choice.

   These have to outrank `.prose img { display: block }` above, which is why
   they carry the element as well as the class. */
.prose img.shot-dark { display: none; }
:root.dark .prose img.shot-light { display: none; }
:root.dark .prose img.shot-dark { display: block; }

/* Figures stay monospaced — the same rule the app applies to every table and
   KPI value, for the same reason: columns of dollars line up on the decimal. */
.prose code {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.9em;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
}

.prose pre {
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 14px 16px;
  overflow-x: auto;
}

.prose pre code {
  background: none;
  border: none;
  padding: 0;
  font-size: var(--fs-small);
  line-height: 1.6;
}

.prose-table-wrap { overflow-x: auto; }

.prose table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-small);
}

.prose th, .prose td {
  border-bottom: 1px solid var(--border);
  padding: 8px 12px;
  text-align: left;
}

/* Table headers carried the same caps-and-tracking treatment. Weight and
   colour separate a header row perfectly well without it. */
.prose th {
  font-size: var(--fs-small);
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
  font-weight: 500;
  border-bottom-color: var(--text);
}

.prose td:not(:first-child), .prose th:not(:first-child) { text-align: right; }

/* ─── Callout ─────────────────────────────────────────────────────
   A side rule instead of the app's 2px bar across the top. The top-bar-on-a-
   rounded-box motif was on the post cards, the callouts and the app's KPIs
   alike; with the cards gone it would have been the last survivor of a
   signature this page no longer has. */
.callout {
  border-left: 2px solid var(--warn);
  padding: 4px 0 4px 18px;
  font-size: var(--fs-small);
  color: var(--muted);
}

.callout strong { color: var(--text); }

/* ─── Share row ────────────────────────────────────────────────── */
.post-share {
  margin: 40px 0 8px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}

/* One more eyebrow retired: mixed-case serif italic, matching `.rail-title`. */
.post-share-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: 15px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
  margin-bottom: 12px;
}

.post-share-row { display: flex; flex-wrap: wrap; gap: 6px; }

/* Same name and same rules as the app's popover items, so the two surfaces
   read as one product. `button` above sets uppercase, 18px padding and no
   border, all of which this has to undo. */
.share-item {
  display: inline-flex;
  align-items: center;
  padding: 7px 13px;
  font-family: var(--font-sans);
  font-size: var(--fs-small);
  letter-spacing: 0;
  text-transform: none;
  text-decoration: none;
  color: var(--text);
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.15s;
}

/* The row sits inside <article class="prose">, so `.prose a` (0,1,1) would
   otherwise win over `.share-item` (0,1,0) and render every network target as
   an underlined body link. Two classes outrank a class plus an element. */
.post-share .share-item { color: var(--text); text-decoration: none; }
.post-share .share-item:hover { border-color: var(--accent); color: var(--accent); }

/* An author `display` beats the UA's `[hidden] { display: none }` however
   specific, so the two script-only controls need this or they render for the
   readers they are hidden from. Same trap as `.tag-filter[hidden]`. */
.share-item[hidden] { display: none; }

/* The one route to Instagram and to the phone's own apps, so it leads. */
.share-native {
  background: var(--accent-pill);
  color: var(--accent-pill-text);
  border-color: var(--accent-pill);
  font-weight: 500;
}

.post-share .share-native { color: var(--accent-pill-text); }

.post-share .share-native:hover {
  background: var(--accent-pill-hover);
  border-color: var(--accent-pill-hover);
  color: var(--accent-pill-text);
}

/* ─── Try-the-app call to action ───────────────────────────────── */
.post-cta {
  margin-top: 48px;
  padding: 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.post-cta-copy strong { display: block; font-size: var(--fs-lead); }
.post-cta-copy span { color: var(--muted); font-size: var(--fs-small); }

/* The colour is restated because this button lives inside .prose, where
   `.prose a` (0,1,1) outranks `.btn-primary` (0,1,0) and was painting the label
   link-blue on the lime pill. */
.post-cta .btn-primary,
.post-cta .btn-primary:hover {
  color: var(--accent-pill-text);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* ─── Post navigation ──────────────────────────────────────────── */
.post-nav {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-small);
}

.post-nav a { color: var(--accent2); text-decoration: none; }
.post-nav a:hover { text-decoration: underline; }
.post-nav .is-next { margin-left: auto; text-align: right; }

/* ─── Footer ───────────────────────────────────────────────────── */
.blog-footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  padding: 24px var(--blog-inset);
  font-size: var(--fs-small);
  color: var(--muted);
}

/* Left-flushed, like everything else. Centring this while the text above it
   hangs off a left axis was the giveaway in miniature. */
.blog-footer-inner {
  max-width: calc(var(--blog-measure) + var(--blog-gap) + var(--blog-rail));
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
}

.blog-footer a { color: var(--accent2); text-decoration: none; }
.blog-footer a:hover { text-decoration: underline; }
.blog-footer-spacer { flex: 1; }

.blog-disclaimer {
  flex-basis: 100%;
  font-size: var(--fs-micro);
  line-height: 1.5;
  opacity: 0.85;
}

/* ─── Cookie consent — same markup and rules as the app's banner ── */
.consent-banner {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 60;
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 16px rgb(0 0 0 / 0.12);
  padding: 0.9rem 1rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
}

.consent-inner {
  max-width: 60rem;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
}

.consent-copy { flex: 1 1 22rem; min-width: 0; }

.consent-copy strong {
  display: block;
  color: var(--text);
  font-size: 0.95rem;
  margin-bottom: 0.15rem;
}

.consent-copy p {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.45;
}

.consent-actions { display: flex; gap: 0.6rem; flex: 0 0 auto; }

.consent-btn { min-width: 7.5rem; min-height: 44px; }

.consent-settings-link {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font: inherit;
  font-size: 0.85rem;
  text-decoration: underline;
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
}

@media (max-width: 32rem) {
  .consent-actions { width: 100%; }
  .consent-btn { flex: 1 1 0; }
}

/* ─── Motion (mirrors src/index.css) ───────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ─── Responsive ─────────────────────────────────────────────────
   Two separate collapses, at different widths, because the two columns stop
   working before the reading measure does.

   The rail goes first. Below ~62rem there is not enough width for a 12.5rem
   margin column AND a readable measure, so the grid becomes one column and the
   rail becomes a horizontal band. It keeps its document position — above the
   list — which is where a filter belongs on a phone anyway. */
@media (max-width: 62rem) {
  .blog-main {
    grid-template-columns: minmax(0, 1fr);
    max-width: calc(var(--blog-measure) + var(--blog-inset) * 2);
  }

  .blog-main > .blog-rail,
  .blog-main > .blog-flow,
  .blog-main > .prose {
    grid-column: 1;
    grid-row: auto;
  }

  .blog-rail {
    position: static;
    padding: 0 0 20px;
    margin-bottom: 26px;
    border-left: none;
    border-bottom: 1px solid var(--border);
  }

  /* The topic cloud collapses usefully; a table of contents does not. The rail
     follows the article in the document, so stacked it would land AFTER the
     post — a list of links to sections the reader has just scrolled past. A
     contents list is a thing you have in a margin or not at all. */
  .blog-rail.is-post { display: none; }

  /* Sized for a margin column; at full width the big steps are too loud. */
  .topic.is-w4 { font-size: 20px; }
  .topic.is-w5 { font-size: 23px; }

  .topic-reset { margin-top: 14px; }
}

@media (max-width: 40rem) {
  /* The wordmark was being crushed to 112px by the two controls beside it and
     breaking into three lines, one word each, for an 89px-tall sticky header.
     Three words will not share a phone's width with two controls, so the header
     wraps: the wordmark takes a row of its own and sets on one line, and the
     controls sit beneath it.
     
     It also stops being sticky here. The extra row would otherwise cost a fifth
     of a phone screen on every scroll of what is, on this breakpoint, a
     single-column reading surface. */
  .blog-header {
    position: static;
    flex-wrap: wrap;
    row-gap: 8px;
    padding: 12px var(--blog-inset);
  }

  .blog-wordmark { flex: 0 0 100%; font-size: 19px; }
  .blog-header-spacer { display: none; }
  /* With the spacer gone, this is what pushes the toggle to the right edge. */
  .blog-header .icon-btn { margin-left: auto; }

  .blog-kicker { display: none; }
  .blog-main { padding-top: 30px; padding-bottom: 48px; }
  .prose h2 { font-size: 20px; }

  /* The date gutter goes; metadata sits above the title, inline, in mono. Note
     this is NOT the eyebrow coming back: no caps, no tracking, and it reads as
     a dateline rather than as a label for the headline. */
  .post-item {
    grid-template-columns: minmax(0, 1fr);
    padding: 22px 0;
  }

  .post-item .post-meta {
    padding-top: 0;
    text-align: left;
    margin-bottom: 6px;
    display: flex;
    gap: 0.55rem;
  }

  .post-item .post-meta > * { display: inline; }
  .post-item .post-meta-sep { display: inline; }

  .post-item h2 { font-size: 23px; }

  /* WCAG 2.5.5 target size, same as the app's header controls. */
  .icon-btn { min-width: 44px; min-height: 44px; }
  .blog-app-link { min-height: 44px; }

  /* The cloud is the only touch target on the index that is text-sized; the
     small end needs padding to reach 44px without changing the type scale. */
  .topic { padding: 6px 0; }
}
