/*
  Learn — /blog, /guides, and every article and guide under them.

  Four routes, one stylesheet, on the same reasoning styles/industries.css gives for
  its thirteen: an index and a page are two shapes, and a blog post and a guide are
  one shape with two labels on it. Scoped by prefix — every selector here starts
  `.learn-`, so nothing in this file can reach a page that does not load it.

  Nothing here restates a colour, a size, a radius or a duration: every value is a
  token from styles/tokens.css, which is what makes both themes and both grounds free.
  The two exceptions are `color-mix()` tints of `--fg` for a ground that has to sit a
  shade off the page, and hairline widths — both of which base.css already establishes
  as the way a tint is written here rather than as a second palette.

  ── The one structural decision ─────────────────────────────────────────────

  These pages open on **paper**, not on the black hero every other page on this site
  opens on. A reading surface that opens dark asks the reader to change eyes twice
  before the first sentence. `PAGES.blog/post/guides/guide` in build.mjs carry
  `bar: 'light'` for the same reason, and it is the only thing separating Learn from
  /industries and /faq.

  ── The two decisions taken on 2026-09-07 ───────────────────────────────────

  Both from the same reading of the built pages, and both are corrections rather than
  changes of mind, so they are recorded here where the values are.

  **The reading column is set from `--learn-*` and not from `--measure`.** The site's
  measure tokens are sized for marketing copy — a paragraph beside a diagram, a
  standfirst under a headline — and `--measure` resolved to 528px inside `.learn-body`.
  528px is right for a page a reader scans and wrong for one they stay inside for four
  thousand words: the column was narrower than the heading above it, every table was
  narrower than the prose it explained, and the whole article read as a sidebar. The four
  widths are declared once, immediately below, and the argument for each number is there.

  **The editorial accent is `--info`, not `--accent`.** Every one of the ten places this
  file marked with the accent token — `#f15a2a` — now takes the blue. Not a repaint of the
  site: `--accent` is still the accent on /product-cost-estimator, on /suppliers and on
  the home page, where it carries human expert judgment, and Learn was borrowing a
  meaning it does not have. `--info` is the semantic blue (`var(--route)`, `#2855F6`),
  documented at 5.7:1 on white at the head of tokens.css, already the blue of this site's
  interactive surfaces, and already in this file on `.learn-callout--note`. It was chosen
  over `--lot-blue` (`#2b8fc2`) on contrast — 3.61:1 fails AA for the 12px label and the
  inline-link underline this has to carry — and over raw `--route`, which is a palette
  entry with no dark-theme rebinding of its own. The swap is one for one: no element that
  was neutral became coloured.
*/

/* ── The widths ──────────────────────────────────────────────────────────────
   Declared on the four Learn blocks rather than on `:root`, so nothing outside a Learn
   page can inherit them — the same scoping the class prefix gives everything else here.

   `--learn-head`    900px. The article's h1, in the hero. It replaced `26em`, which at a
                     46px display size is 1196px and therefore no cap at all inside a
                     1240px shell. 900 is a heading of two lines for a title of ten words
                     in either language, which is what the hero is for.
   `--learn-measure` 720px. The reading column, and the bottom of the 720–800px the brief
                     asks for — chosen at the bottom, not the middle, for the reason in
                     the paragraph below.
   `--learn-wide`    980px. The breakout cap, for a table or a comparison block. Wider
                     than the prose by 260px so the relationship reads as deliberate, and
                     short of the 1240px shell so a row's last cell is still findable
                     from its first. Since 2026-09-07 it is a cap and not the width: on an
                     article with a rail the main track is narrower than 980 and wins.
   `--learn-t-body`  18px. Body copy, up from the site's 16.5px, and the top of the 17–18
                     the brief allows. A column 36% wider at an unchanged size is denser
                     rather than more readable, which is the failure mode this token
                     exists to prevent. Learn-local, not a change to `--t-body`: the
                     marketing pages are set on 528px and 16.5px is right there.
   `--learn-rail`    216–272px. The section-index column added on 2026-09-07. See below.
   `--learn-rail-gap` 28–52px. The gutter between the reading column and that rail. Wide
                     enough that the rail reads as a separate object and not as a hanging
                     indent of the prose.

   ── The measured line, and what it costs ─────────────────────────────────

   Worth writing down rather than leaving for the next person to rediscover. The display
   face averages 0.465em per character here, so the reading column runs about **86
   characters** — measured with a `Range` over the built Dutch article, not estimated. That
   is past the 60–68 `--measure` is documented for and past the 75–80 usually given as the
   ceiling for continuous text.

   It is the best available inside the brief, and the brief is right about the defect it
   was fixing: 528px was narrower than the h1 above it and narrower than every table it
   explained. Getting to 80 characters *and* keeping 720px would take 19.5px type; getting
   there at 18px would take a 620px column, which is the defect again. So the column is
   pinned at the low end of the range and the size at the high end — the pair that
   minimises the line inside what was asked for — and the leading does the rest:
   `line-height: 1.7` on 18px is 30.6px, which is what returns the eye reliably to the
   start of a long line. Moving either number outside its range is a decision for whoever
   asked for this range, not one to take quietly here.

   ── 2026-09-07: the width was used, and the measure was NOT ──────────────

   The request, verbatim: "de blogs en guides moeten iets meer gebruik maken van de breedte
   van de pagina" — the blog and guide pages should make somewhat more use of the page
   width. Looked at on a ~2000px monitor, a 17-minute guide had two defects, both measured
   before anything moved:

     1. Two vertical axes. Breadcrumb, h1, KORT GEZEGD callout and the meta line all began
        at the shell's left edge; the first body paragraph began **260px to their right**,
        because `.learn-body` was a symmetric three-track grid centring a 720px column in
        a 1240px shell. 260px at 2000 and at 1440; 100.8px at 1024.
     2. The right half of the viewport was blank for the whole length of the article.

   Read the first defect as the cause of the second and there is exactly one honest fix:
   the reading column moves to the shell's left edge, and the space it stops wasting
   becomes a real column. `.learn-body` is now two tracks — `min(--learn-measure, 100%)`
   then `minmax(0, 1fr)` — and `.learn-layout` puts a sticky section index in the freed
   track. The axis spread is 0 at every width.

   **The measure did not change, and must not be "fixed" by stretching it.** This is the
   trap: the page now looks like it has room for a wider column, and it does not. 720px
   already runs ~86 characters, which is past the ceiling in the paragraph above — the
   whole point of the two-track grid is to spend the freed width on a *second object*
   rather than on a longer line. Widening `--learn-measure` to fill the main track would
   put body copy at ~109 characters and reintroduce, worse, the defect these tokens exist
   to prevent. If a future brief really wants a wider reading column, it has to buy it
   with type size, and that is a decision for whoever writes the brief.

   The same argument is why the callouts, the numbered step lists, the checklists and the
   formula were *not* given `.learn-wide` even though the layout would let them span both
   tracks: they are prose, and prose at the main track's width is the 109-character line
   again. The formula in particular looks like a candidate and is not — see the `formula`
   arm of `learnBlock` for the measurement that settled it. What did get the breakout is
   what gains from width without carrying a measure: tables, comparison grids and the four
   generated end sections.

   Below `1000px` the rail collapses to a bordered box above the article and `.learn-body`
   is a single column, so the phone layout is what it was. Between 1001 and ~1075px the
   main track is narrower than 720 and the measure narrows with it, to ~655–720px — that is
   `min()` doing its job, it is *fewer* characters per line, and it is the only band where
   the rendered measure is not exactly 720.

   None of these is imposed on a phone. They are all `max-width` or a `min()` track, so
   below ~800px the column is the viewport minus `--gutter` and the type is unchanged —
   the one thing a reading page must not do at 375px is shrink to keep a desktop measure.
*/
.learn-hero,
.learn-top,
.learn-body-band,
.learn-prose {
  --learn-head: 900px;
  --learn-measure: 720px;
  --learn-wide: 980px;
  --learn-t-body: 18px;
  --learn-rail: clamp(216px, 20vw, 272px);
  --learn-rail-gap: clamp(28px, 3vw, 52px);
}

/* ── The index hero ─────────────────────────────────────────────────────────
   Shorter than /industries' and /faq's, and deliberately. Those two open on a
   full screenful of black because the hero *is* the statement; an index whose job
   is to get the reader into a list should spend less of the first screen on itself.
   Same top offset — the bar's height plus air — so the three still read as one site.
*/
.learn-hero {
  padding: calc(var(--topbar-h) + clamp(44px, 6vh, 80px)) var(--gutter)
    clamp(32px, 4vh, 52px);
  background: var(--bg);
  color: var(--fg);
}

.learn-hero .eyebrow {
  margin-bottom: clamp(16px, 2vw, 24px);
}

.learn-hero h1 {
  font-size: var(--t-display);
  max-width: 20em;
  margin-bottom: clamp(16px, 2vw, 26px);
}

.learn-hero .lead {
  max-width: var(--measure-lead);
}

/* ── The filter row ─────────────────────────────────────────────────────────
   Chips, in the canonical order content/learn/validate.mjs declares, with `All`
   first. They are `<button>`s carrying `aria-pressed`, which is the right role for a
   control that switches state rather than navigates: a link would have to have a URL
   to go to, and there is no /blog?category=quality on a static site.

   Which is also why the whole row is hidden when scripting is off. `documentHead`
   sets `data-js` on `<html>` from an inline script before first paint, so this only
   fires for a reader with JavaScript genuinely disabled — and for them the honest
   surface is the complete list with no controls that do nothing.
*/
html:not([data-js]) .learn-filter,
html:not([data-js]) .learn-count {
  display: none;
}

.learn-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-block: clamp(20px, 2.4vw, 30px) 0;
}

.learn-filter-label {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-right: 6px;
}

/*
  Sentence case, and `none` written out rather than left off.

  The label is content, not furniture: it is the same string the taxonomy in
  content/learn/validate.mjs gives the card, and I18N.md §6 fixes its casing per locale.
  Upper-casing it here would have this file overrule that table — and it did, which is
  why `Kosten & landed cost` was reading as `KOSTEN & LANDED COST` on a page whose own
  contract says otherwise. `none` is explicit so the next hand to touch this can see the
  decision instead of an absence.

  The tracking comes down with it. 0.07em is what makes upper-case mono legible; on
  sentence case it opens the word up until it stops being one, so 0.01em — enough to keep
  mono from setting tight, not enough to letterspace lowercase.
*/
.learn-chip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--fg-sub);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.01em;
  text-transform: none;
  cursor: pointer;
  transition:
    border-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease);
}

.learn-chip:hover,
.learn-chip:focus-visible {
  border-color: var(--line-strong);
  color: var(--fg);
}

/*
  The selected chip inverts. Not a border weight and not a colour on the label alone:
  one chip in a row of ten has to be findable at a glance from across the row, and a
  1px difference in a hairline is not.
*/
.learn-chip[aria-pressed='true'] {
  border-color: var(--fg);
  background: var(--fg);
  color: var(--bg);
}

/*
  The result count, announced. `role="status"` on the element and the text rewritten
  by js/learn.js, so a screen-reader user who presses a chip is told what happened —
  a filtered grid that changes silently is a control with no feedback.
*/
.learn-count {
  margin-top: clamp(18px, 2vw, 24px);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-soft);
}

/* ── The card grid ──────────────────────────────────────────────────────────
   Three across on /blog, two on /guides. A guide card carries its topics and a
   longer description, so two columns give it the width to be read rather than
   scanned; a post card is a title and a sentence, and three across is what stops
   thirteen of them running down the page as a list.

   No photographs. AUTHORING.md rules out stock imagery for the pages, and a card set
   where thirteen thumbnails would have to be invented is the same decision one level
   up. The card is type, a hairline and a category.
*/
.learn-body-band {
  padding-block: clamp(36px, 4vw, 56px) var(--section-y);
}

.learn-grid {
  display: grid;
  gap: clamp(16px, 1.8vw, 24px);
  list-style: none;
}

.learn-grid--blog {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.learn-grid--guides {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Filtered out. `hidden` would do it, but a class keeps the attribute free for
   anything that genuinely is not part of the document. */
.learn-card[data-hidden='true'] {
  display: none;
}

.learn-card-link {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  padding: clamp(20px, 2vw, 28px);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-raised);
  color: inherit;
  text-decoration: none;
  transition:
    border-color var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}

.learn-card-link:hover,
.learn-card-link:focus-visible {
  border-color: var(--line-strong);
  transform: translateY(-2px);
}

/* The category, on a card. Sentence case and blue — see `.learn-chip` above for the
   casing and the file header for the colour. Same rule on `.learn-head-cat`, which is
   the same label one level down, and they have to agree: a reader arrives at an article
   from a card and the label is the one thing carried across. */
.learn-card-cat {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--info);
}

/* `text-wrap: balance` for the same reason it is on the article h1 (see the block above
   `.learn-top h1`): a card title is short enough for the browser to balance cheaply, and a
   greedy first line is what pushes a break into the middle of a hard-hyphenated word —
   "elk e-" / "commerce merk". Balancing shortens line one so `e-commerce` stays whole.
   Not `nowrap`: a card is 302px wide inside its padding on a 390px phone, and a title
   that refuses to wrap would overflow it. */
.learn-card-title {
  font-family: var(--font-display);
  font-size: var(--t-h3);
  font-weight: 600;
  line-height: 1.22;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.learn-card-body {
  font-size: var(--t-small);
  line-height: 1.6;
  color: var(--fg-sub);
}

/* The topics row, guides only. Not a tag cloud: they are what the guide covers,
   which is the one thing a reader wants before committing to 4,000 words. */
.learn-card-topics {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
}

.learn-card-topics li {
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  font-family: var(--font-mono);
  font-size: var(--t-mono-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-soft);
}

/*
  The card's last row: read time on the left, the read affordance on the right,
  pushed to the bottom of the card so a three-line title and a one-line title still
  produce a row of cards whose feet line up.
*/
.learn-card-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: auto;
  padding-top: 4px;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--fg-soft);
}

.learn-card-read {
  color: var(--fg);
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 2px;
  transition: border-color var(--dur-fast) var(--ease);
}

.learn-card-link:hover .learn-card-read,
.learn-card-link:focus-visible .learn-card-read {
  border-color: var(--fg);
}

/* ── The article ───────────────────────────────────────────────────────────
   One template for a post and for a guide.
*/
.learn-top {
  padding: calc(var(--topbar-h) + clamp(36px, 4.5vh, 64px)) var(--gutter)
    clamp(28px, 3.4vw, 44px);
  background: var(--bg);
  color: var(--fg);
  border-bottom: 1px solid var(--line);
}

/*
  The breadcrumb. A real `nav` with an ordered list, because it is a trail and the
  order is the meaning. The separator is a `::before` on every item but the first,
  so it is not a character in the accessible name — "Home slash Blog" is not what the
  trail says.
*/
.learn-crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  list-style: none;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.learn-crumbs li + li::before {
  content: '';
  display: inline-block;
  width: 12px;
  height: 1px;
  margin-right: 8px;
  vertical-align: middle;
  background: var(--fg-faint);
}

.learn-crumbs a {
  color: var(--fg-soft);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}

.learn-crumbs a:hover,
.learn-crumbs a:focus-visible {
  color: var(--fg);
}

.learn-crumbs [aria-current] {
  color: var(--fg-sub);
}

.learn-head-cat {
  display: block;
  margin-top: clamp(22px, 2.6vw, 32px);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--info);
}

/*
  The h1 is set smaller than the display sizes elsewhere on the site. An article title is
  a sentence and often a question — 'What Is Landed Cost? How to Calculate the True Cost
  of a Product' — and at --t-display it would take four lines of the first screen before
  the answer.

  `--learn-head` and not `26em`. Two problems with the em: it is relative to the h1's own
  clamped size, so the cap moved with the viewport in the opposite direction to the shell,
  and at the top of the clamp it resolved to 1196px — no cap at all, since the shell's
  content box is 1112px. The h1 was therefore the widest thing on the page above a column
  less than half its width.

  `text-wrap: balance` is what fixes the break the width alone does not. A title of ten
  Dutch words over two lines was leaving one word on the second — and worse, breaking
  inside `e-commerce` at the hyphen, because a hard hyphen is a break opportunity and the
  greedy algorithm takes it. `balance` optimises the set of lines instead of filling each
  one in turn, so it stops splitting a word when there is a whole-word break available at
  a similar cost. It degrades to normal wrapping where it is unsupported, which is the
  only reason it can be used on a heading with no fallback. `nowrap` is not an option
  here: a title that cannot wrap overflows the page on a phone.
*/
.learn-top h1 {
  margin-top: clamp(12px, 1.4vw, 18px);
  font-size: clamp(30px, 3.6vw, 46px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  max-width: var(--learn-head);
  text-wrap: balance;
}

/*
  The direct answer, set apart. This is the one block on the page whose position is
  fixed by the contract rather than by the argument: it is what the page is *for* — a
  reader arriving from a search has their answer before scrolling, whatever the body
  goes on to do with it. So it is larger than body copy, ruled off, and labelled.

  Labelled and not a heading. `In short` over one paragraph is furniture, not a
  section: an `<h2>` here would put a heading in the outline that no reader is
  navigating to and would compete with the first real section below it.
*/
/*
  Unchanged except for two values, and the block is deliberately not redesigned: the rule
  is blue instead of orange, and it is capped on the reading measure instead of
  `--measure-lead` so its left rule and the body column below it are the same object seen
  twice rather than two widths that nearly agree. The label above it stays neutral — the
  colour marks the block, not the word.
*/
.learn-answer {
  margin-top: clamp(26px, 3vw, 38px);
  padding-left: clamp(16px, 1.8vw, 24px);
  border-left: 2px solid var(--info);
  max-width: var(--learn-measure);
}

.learn-answer-label {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-bottom: 8px;
}

.learn-answer p {
  font-size: var(--t-lead);
  line-height: 1.55;
  color: var(--fg);
}

/* Dates and read time. Mono, small, muted — a byline's position without a byline. */
.learn-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 18px;
  margin-top: clamp(24px, 2.8vw, 34px);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--fg-soft);
}

.learn-meta time {
  color: var(--fg-sub);
}

/* ── The article layout: reading column, then rail ─────────────────────────
   Two tracks, and the order matters more than the numbers. The reading column is track
   one and starts at the shell's left edge — the same edge as the breadcrumb, the h1 and
   the KORT GEZEGD callout in the hero above it, which is the whole of the axis fix
   recorded at the top of this file. The rail is track two.

   `minmax(0, 1fr)` on the prose track and a fixed `--learn-rail` on the second: the rail
   holds long section titles, and a `1fr / auto` pair would let a fourteen-word heading
   widen the rail and shove the reading column around from page to page. A fixed rail also
   means the prose track is arithmetic anyone can check — shell − rail − gap.

   `align-items: start` is what makes `position: sticky` on the rail work: the grid area
   stays as tall as the article, the rail's own box is only as tall as its content, and
   sticky slides the box inside the area.

   Single column below the breakpoint, which is the default here rather than an override,
   so the phone case is the one with no media query behind it.
*/
.learn-layout {
  display: grid;
  row-gap: clamp(26px, 3.2vw, 40px);
}

@media (min-width: 1001px) {
  .learn-layout {
    grid-template-columns: minmax(0, 1fr) var(--learn-rail);
    column-gap: var(--learn-rail-gap);
    align-items: start;
  }

  /*
    Both in row 1, explicitly. The rail is FIRST in the source — a keyboard or a screen
    reader should meet the index before the sections it indexes, and the collapsed layout
    below 1000px wants it above the article — so without this it would take column 1.
  */
  .learn-rail {
    grid-area: 1 / 2;
  }

  .learn-main {
    grid-area: 1 / 1;
    min-width: 0;
  }

  /*
    `--bar-h` is published on the root by js/nav.js and is the bar's *visible* height, so
    the rail follows the bar as it hides and returns on scroll. `--topbar-h` is the design
    token and the no-JS fallback — with scripting off the bar never moves and the fallback
    is exactly right, which is why this is a `var(--bar-h, var(--topbar-h))` and not a
    scripted value with a guess behind it.

    The `max-height` and the flex column are for the sixteen-section pages: the rail gets
    its own scroller, the title stays put, and `overscroll-behavior: contain` stops a
    wheel at the end of the list from grabbing the article.
  */
  .learn-rail {
    position: sticky;
    top: calc(var(--bar-h, var(--topbar-h)) + clamp(14px, 1.8vw, 26px));
    max-height: calc(100vh - var(--bar-h, var(--topbar-h)) - clamp(40px, 6vh, 76px));
    display: flex;
    flex-direction: column;
  }

  .learn-toc {
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  .learn-toc-list {
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

/* ── The rail's contents ───────────────────────────────────────────────────
   Two shapes, chosen per page in `learnRail` (build.mjs) from what the page actually has:
   a section index when the article has three or more `h2`s, and the article's own meta
   plus a jump to the related reading when it does not. Never an empty rail, and nothing
   invented to fill one.

   The title is a `p` with a mono label face rather than an `h3`, and for the same reason
   `.learn-answer-label` is: it names a control, it is not a section of the article, and an
   `h3` here would appear in the document outline between the article's own headings.

   The index is deliberately `h2` only. `h3`s are three to six per section on these pages,
   and a two-level index of a 17-minute guide is a second article rather than a way of
   finding your place in the first.
*/
.learn-rail-title {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-bottom: 12px;
}

/*
  The list carries the spine and the links hang a 2px marker on it, pulled back over the
  1px border with a negative margin so the active marker covers the line rather than
  sitting beside it. That is the whole of the active state: no fill, no weight change, so
  nothing reflows when the scroll-spy moves it.
*/
.learn-toc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: 1px solid var(--line);
}

.learn-toc-list > li {
  margin: 0;
}

.learn-toc a {
  display: block;
  margin-left: -1px;
  padding: 5px 0 5px 14px;
  border-left: 2px solid transparent;
  font-size: var(--t-small);
  line-height: 1.42;
  color: var(--fg-soft);
  text-decoration: none;
  transition:
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}

.learn-toc a:hover,
.learn-toc a:focus-visible {
  color: var(--fg);
  border-left-color: var(--line-strong);
}

/*
  `aria-current="location"` is set by js/learn.js. With scripting off it is never set and
  the list is a plain index of links, which is the whole degradation — the entries are in
  the HTML either way.
*/
.learn-toc a[aria-current='location'] {
  color: var(--fg);
  border-left-color: var(--info);
}

/* The fallback shape: a short post's meta, in the rail instead of an index. */
.learn-rail-about {
  border-left: 1px solid var(--line);
  padding-left: 14px;
}

.learn-rail-cat {
  font-size: var(--t-small);
  color: var(--info);
  margin-bottom: 6px;
}

.learn-rail-line {
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--fg-soft);
}

.learn-rail-jump {
  margin-top: 12px;
  font-size: var(--t-small);
}

.learn-rail-jump a {
  color: var(--fg-sub);
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  transition:
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}

.learn-rail-jump a:hover,
.learn-rail-jump a:focus-visible {
  color: var(--fg);
  border-bottom-color: var(--fg);
}

/* ── The prose column ──────────────────────────────────────────────────────
   Two tracks: the reading measure, then whatever is left. A paragraph sits in track one
   at 720px and a table spans both, so neither needs a negative margin or any knowledge of
   the other. It was a symmetric three-track grid until 2026-09-07; centring the measure
   inside the main column is what put the body on a different axis from its own hero.

   `min(var(--learn-measure), 100%)` and not a flat `720px`: below the breakpoint there is
   no rail, the container is the shell, and on a phone the track has to be the viewport.

   `minmax(0, 1fr)` on the second track and not `1fr`: a wide table's content would
   otherwise force the track open and push the prose column off its axis — the grid
   blowout every `1fr` track has by default.
*/
.learn-prose {
  padding-block: clamp(36px, 4.4vw, 60px) var(--section-y);
}

.learn-body {
  display: grid;
  grid-template-columns:
    min(var(--learn-measure), 100%)
    minmax(0, 1fr);
  row-gap: clamp(16px, 1.6vw, 22px);
}

.learn-body > * {
  grid-column: 1;
}

/*
  The breakout — tables, comparison grids, the mono formula, the four end sections.
  `--learn-wide` is a cap and `min()` picks the loser: on an article with a rail the main
  track is ~916px at the 1240px shell and wins, and on one without a rail the cap wins at
  980. Either way `margin-inline: 0`, not `auto` — a centred breakout under a left-aligned
  paragraph is the axis defect again, one element at a time.

  Deliberately NOT applied to callouts, steps or checklists: those are prose, and prose at
  the main track's width runs ~109 characters. The argument is at the top of this file.
*/
.learn-body > .learn-wide {
  grid-column: 1 / -1;
  width: min(100%, var(--learn-wide));
  margin-inline: 0;
}

.learn-body p {
  font-size: var(--learn-t-body);
  line-height: 1.7;
  color: var(--fg-sub);
}

.learn-body > .learn-lead {
  font-size: var(--t-lead);
  line-height: 1.62;
  color: var(--fg);
}

/*
  Headings carry an id, generated from their own text (see `headingId` in build.mjs),
  so every section of every page is linkable without anybody writing an anchor. The
  extra space above is on the heading rather than below the paragraph, so two
  headings in a row do not double it.
*/
.learn-body h2 {
  margin-top: clamp(26px, 3.2vw, 46px);
  font-size: clamp(24px, 2.4vw, 32px);
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--fg);
}

.learn-body h3 {
  margin-top: clamp(18px, 2vw, 28px);
  font-size: var(--t-h3);
  line-height: 1.28;
  color: var(--fg);
}

/* `scroll-margin-top` so a jump from a table of contents or a shared #link does not
   land the heading underneath the pinned bar. */
.learn-body h2,
.learn-body h3 {
  scroll-margin-top: calc(var(--topbar-h) + 16px);
}

/*
  An inline link in the prose. Underlined always — the rule under the word is the link,
  and a colour alone would be the one distinction a reader with a colour deficiency does
  not get. So the blue arrives on hover and on focus, on a word that was already marked.

  `border-bottom` and not `text-decoration`, because the rule has to be able to differ in
  colour from the text above it while the text stays `--fg`; `text-decoration-color` would
  do it now, but this is the same underline the rest of the site draws this way.
*/
.learn-body a {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--info);
  transition:
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}

.learn-body a:hover,
.learn-body a:focus-visible {
  color: var(--info);
  border-bottom-width: 2px;
}

.learn-body strong {
  font-weight: 600;
  color: var(--fg);
}

.learn-body code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  padding: 1px 5px;
  border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--fg) 7%, transparent);
  color: var(--fg);
}

/* ── Lists ─────────────────────────────────────────────────────────────── */

.learn-list {
  display: grid;
  row-gap: 10px;
  padding-left: 1.25em;
  font-size: var(--learn-t-body);
  line-height: 1.65;
  color: var(--fg-sub);
}

.learn-list li::marker {
  color: var(--fg-faint);
}

ol.learn-list li::marker {
  font-family: var(--font-mono);
  font-size: var(--t-small);
}

/*
  A numbered framework. The counter is drawn in the margin in mono rather than left
  to the list marker, because a `steps` item is a title and a paragraph and the
  number has to sit against the title's baseline, not against the block.
*/
.learn-steps {
  display: grid;
  row-gap: clamp(14px, 1.6vw, 20px);
  list-style: none;
  counter-reset: learn-step;
}

.learn-steps > li {
  display: grid;
  grid-template-columns: 2.4em minmax(0, 1fr);
  column-gap: 2px;
  counter-increment: learn-step;
}

.learn-steps > li::before {
  content: counter(learn-step, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.06em;
  color: var(--info);
  padding-top: 0.35em;
}

.learn-step-title {
  font-family: var(--font-display);
  font-size: var(--t-h3);
  font-weight: 600;
  line-height: 1.28;
  color: var(--fg);
  margin-bottom: 4px;
}

/* A decision checklist. Ticked rows, and the tick is drawn from two borders on an
   empty box rather than set as a glyph — a character in `content` is announced by
   some screen readers, and 'tick Verify the certificate' is not the row. */
.learn-check {
  display: grid;
  row-gap: 10px;
  list-style: none;
  padding: clamp(18px, 2vw, 24px);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}

.learn-check-title {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-bottom: 4px;
}

.learn-check > li {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  align-items: start;
  font-size: var(--learn-t-body);
  line-height: 1.6;
  color: var(--fg-sub);
}

.learn-check > li::before {
  content: '';
  width: 11px;
  height: 6px;
  margin-top: 0.5em;
  border-left: 1.6px solid var(--info);
  border-bottom: 1.6px solid var(--info);
  transform: rotate(-45deg);
}

/* ── Tables ────────────────────────────────────────────────────────────────
   Mobile-usable, which on a table means one thing: it scrolls sideways inside its own
   box instead of setting the width of the page. The scroller is focusable and carries
   a role, so it can be reached and panned from a keyboard — a `overflow: auto` div
   that only a trackpad can move is the accessibility failure this pattern exists for.
*/
.learn-table {
  margin: clamp(8px, 1vw, 14px) 0;
}

.learn-table-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  -webkit-overflow-scrolling: touch;
}

.learn-table-scroll:focus-visible {
  outline: 2px solid var(--info);
  outline-offset: 2px;
}

.learn-table table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-small);
  line-height: 1.5;
}

.learn-table th,
.learn-table td {
  padding: 11px 14px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
  /* Enough that a three-word cell does not wrap to three lines inside a scroller
     that is already allowed to be wider than the page. */
  min-width: 8em;
}

.learn-table th {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--fg);
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  white-space: nowrap;
}

.learn-table td {
  color: var(--fg-sub);
}

.learn-table tbody tr:last-child td {
  border-bottom: 0;
}

.learn-table figcaption {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-bottom: 10px;
}

/* The small aside under a table. Not a caption — a caption names the table, this
   qualifies it. */
.learn-note {
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--fg-soft);
}

/* ── Formula, definition, callout ─────────────────────────────────────────── */

.learn-formula {
  padding: clamp(14px, 1.6vw, 20px) clamp(16px, 1.8vw, 22px);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  font-family: var(--font-mono);
  font-size: var(--t-small);
  line-height: 1.6;
  color: var(--fg);
  /* One line, and it is arithmetic: it may be wider than the measure and it must not
     be broken mid-term to fit. */
  overflow-x: auto;
}

.learn-formula-note {
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--fg-soft);
  margin-top: -8px;
}

/* An explicit definition. A real `dl`, because that is what it is, and a reader on a
   screen reader gets the term/description relationship for free. */
.learn-def {
  padding-left: clamp(14px, 1.6vw, 20px);
  border-left: 2px solid var(--line-strong);
}

.learn-def dt {
  font-family: var(--font-display);
  font-size: var(--t-h3);
  font-weight: 600;
  line-height: 1.3;
  color: var(--fg);
  margin-bottom: 6px;
}

.learn-def dd {
  font-size: var(--learn-t-body);
  line-height: 1.7;
  color: var(--fg-sub);
}

/*
  A callout, in two tones. `note` is an aside the argument can spare; `caution` is
  the one AUTHORING.md mandates on anything touching contracts, customs, IP or EU
  market access, and it has to be visibly not-a-note. So the tone is carried by the
  rule down its edge and by nothing else — no icon, no coloured ground, no shout.
*/
.learn-callout {
  padding: clamp(16px, 1.8vw, 22px);
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--fg) 3%, transparent);
}

.learn-callout--note {
  border-left-color: var(--info);
}

.learn-callout--caution {
  border-left-color: var(--state-exception);
}

.learn-callout-title {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg);
  margin-bottom: 6px;
}

.learn-body .learn-callout p {
  font-size: var(--t-small);
  line-height: 1.65;
}

/* ── FAQ, sources, related ─────────────────────────────────────────────────
   Three closing sections, all three optional except the last. Ruled off from the
   body and from each other with the page's own hairline rather than boxed, because
   they are the end of one article and not three new pages.
*/
.learn-end {
  display: grid;
  row-gap: clamp(30px, 3.6vw, 52px);
  margin-top: clamp(40px, 5vw, 72px);
}

.learn-end-section {
  padding-top: clamp(26px, 3vw, 40px);
  border-top: 1px solid var(--line);
}

.learn-end-section > h2 {
  font-size: clamp(22px, 2.2vw, 28px);
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--fg);
  margin-bottom: clamp(16px, 1.8vw, 24px);
}

/*
  The FAQ. `details`/`summary`, so it opens and closes with no script at all — the
  same decision the nav's dropdowns make, and the reason this page needs nothing
  mounted to be usable. Open on the first: a closed accordion of six rows reads as
  a page with nothing on it.
*/
.learn-faq {
  display: grid;
  list-style: none;
}

.learn-faq > li {
  border-bottom: 1px solid var(--line);
}

.learn-faq summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 0;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: var(--t-lead);
  font-weight: 600;
  line-height: 1.35;
  color: var(--fg);
  list-style: none;
}

.learn-faq summary::-webkit-details-marker {
  display: none;
}

/* A plus that becomes a minus. Two rules crossed, so nothing here is a glyph. */
.learn-faq summary::after {
  content: '';
  flex: none;
  width: 12px;
  height: 12px;
  margin-top: 0.45em;
  background:
    linear-gradient(var(--fg-soft), var(--fg-soft)) center / 12px 1.5px no-repeat,
    linear-gradient(var(--fg-soft), var(--fg-soft)) center / 1.5px 12px no-repeat;
  transition: transform var(--dur-fast) var(--ease);
}

.learn-faq details[open] summary::after {
  background: linear-gradient(var(--fg), var(--fg)) center / 12px 1.5px no-repeat;
}

.learn-faq-answer {
  padding-bottom: 18px;
  max-width: var(--learn-measure);
  font-size: var(--learn-t-body);
  line-height: 1.7;
  color: var(--fg-sub);
}

/* Sources. Numbered, external, and the domain is shown — a reader deciding whether
   to trust a figure wants to see whose page it came from before clicking. */
.learn-sources {
  display: grid;
  row-gap: 10px;
  list-style: none;
  counter-reset: learn-source;
}

.learn-sources > li {
  display: grid;
  grid-template-columns: 2.4em minmax(0, 1fr);
  font-size: var(--t-small);
  line-height: 1.6;
  counter-increment: learn-source;
}

.learn-sources > li::before {
  content: counter(learn-source, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  color: var(--fg-faint);
  padding-top: 0.25em;
}

.learn-sources a {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--line-strong);
  transition: border-color var(--dur-fast) var(--ease);
}

.learn-sources a:hover,
.learn-sources a:focus-visible {
  border-color: var(--info);
}

.learn-source-host {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.05em;
  color: var(--fg-soft);
  margin-top: 3px;
}

/* Three related cards, in a row. The same card as the index grid's — a reader who
   has met one on /blog should recognise it here, because it is the same object. */
.learn-related {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 20px);
  list-style: none;
}

/* ── Narrow ────────────────────────────────────────────────────────────────
   Two steps and no more. The card grids collapse at 1000px and again at 640, the
   prose column is already fluid, and the article's own reading measure does not change: a
   reading measure on a phone is the phone's width, which is what `min()` in the grid
   track already gives.

   What does change at 1000px is the rail. It stops being a column and becomes a bordered
   box above the article — which needs no reordering, because the rail is first in the
   source. Nothing here touches type size or the measure.
*/
@media (max-width: 1000px) {
  .learn-grid--blog,
  .learn-related {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .learn-rail {
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--fg) 3%, transparent);
    padding: clamp(15px, 2.6vw, 20px) clamp(16px, 3vw, 22px);
    /* A box the width of the article does not need a 720px cap of its own; it needs to
       not be taller than the thing it introduces. Two columns of titles, read down. */
    max-width: min(100%, var(--learn-wide));
  }

  .learn-toc-list,
  .learn-rail-about {
    border-left: 0;
    padding-left: 0;
  }

  .learn-toc-list {
    columns: 2;
    column-gap: clamp(20px, 3.4vw, 36px);
  }

  .learn-toc a {
    margin-left: 0;
    padding-left: 11px;
  }
}

@media (max-width: 640px) {
  .learn-grid--blog,
  .learn-grid--guides,
  .learn-related {
    grid-template-columns: minmax(0, 1fr);
  }

  /*
    One column of section titles: two would be ~18 characters each here.

    And bounded, because one column of seventeen of them is 707px — measured on the Dutch
    sourcing guide at 390px, a 770px box, which is nine tenths of a screen of navigation
    before the first sentence of the article. Capped at 46vh the same list is 388px in a
    451px box, and the row the cap cuts in half is the affordance. An eleven-section post
    (`wat-zijn-landed-costs`) is 368px and never reaches the cap.

    `columns: auto` and NOT `columns: 1`, which is the trap: `column-count: 1` still makes
    this a multi-column container, and a multi-column container with a bounded height does
    not scroll — it fragments into a further column in the *inline* direction. Measured with
    `columns: 1`: `scrollHeight === clientHeight === 388` with eleven of the seventeen
    entries laid out off to the right of the box, unreachable. `auto` on both `column-width`
    and `column-count` is what takes the element back out of multicol, so the `max-height`
    below produces an ordinary vertical scroller.

    Deliberately no `overscroll-behavior: contain` here, unlike the desktop rail. Chaining
    is what a thumb wants at this size: a swipe that reaches the end of the list should
    carry on down the page rather than stop dead inside a 346px box.

    The affordance is the scrollbar, made permanent with `scrollbar-width`/`scrollbar-color`
    rather than left as an overlay that macOS and iOS hide until a gesture starts, plus the
    row the cap cuts in half. The honest cost of the cap is that a reader who never touches
    the box sees eight of seventeen sections; the honest cost of no cap is 770px of index
    before the first sentence. The third option — a disclosure that starts closed on a phone
    and open on a desktop — cannot be expressed without either script or
    `::details-content`, and a rail that renders empty where that selector is unsupported is
    a worse failure than either.
  */
  .learn-toc-list {
    columns: auto;
    max-height: 46vh;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
  }

  /* The steps counter and the checklist tick both give up their own column: at this
     width 2.4em of margin is a tenth of the line. */
  .learn-steps > li {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 2px;
  }

  .learn-steps > li::before {
    padding-top: 0;
  }

  .learn-table th,
  .learn-table td {
    padding: 9px 11px;
  }
}
