/*
 * ldr theme — web.
 *
 * The visual system of the `ldr` Slidev theme, carried to long-form web
 * pages. Plain CSS on purpose (same reason as the slide theme: it stays
 * easy to edit) and the same `--ldr-*` token names, so both codebases
 * share one vocabulary.
 *
 * Source of truth for the design decisions:
 * ldr-slidev-theme/docs/ldr-style-guide.md
 * Where this file departs from the slide CSS, docs/theme.md says why.
 */

:root {
  /* Palette — identical to the slide theme -------------------------------- */
  --ldr-paper: #f4eddc; /* warm parchment ground             */
  --ldr-paper-deep: #efe7d2; /* deeper ground (hero, callout)     */
  --ldr-panel: #ede4ce; /* code panel / inset surface        */
  --ldr-ink: #2a2925; /* warm near-black body text         */
  --ldr-ink-soft: #5e584c; /* muted ink (subtitles, meta)       */
  --ldr-ink-faint: #8a8270; /* faint ink (decoration only)       */
  --ldr-forest: #38543e; /* primary accent — structural marks */
  --ldr-forest-deep: #2c4632;
  --ldr-sage: #7e9a7c; /* soft secondary accent             */
  --ldr-maple: #9e3b2d; /* second accent — autumn maple red  */
  --ldr-ochre: #b07d2b; /* numbers / tertiary warm accent    */
  --ldr-rule: #d8cdb3; /* faint hairlines                   */

  /* Web-only tokens ------------------------------------------------------- */
  /* Style guide §6 Mode B: warm white is the reading surface, parchment is
     reserved for masthead, hero and callouts. */
  --ldr-paper-white: #fbf8f0;
  --ldr-measure: 65ch;

  /* Type ------------------------------------------------------------------ */
  --ldr-serif: 'Gentium Book Plus', Georgia, 'Times New Roman', serif;
  --ldr-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', monospace;

  /* Spacing --------------------------------------------------------------- */
  --ldr-space-1: 0.4rem;
  --ldr-space-2: 0.8rem;
  --ldr-space-3: 1.4rem;
  --ldr-space-4: 2.2rem;
  --ldr-space-5: 3.5rem;
  --ldr-space-6: 5rem;
  --ldr-gutter: clamp(1.25rem, 5vw, 3.5rem);
}

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

html {
  /* Anchor links from markdown-it-anchor shouldn't land under the header. */
  scroll-padding-top: var(--ldr-space-4);
}

body {
  margin: 0;
  background: var(--ldr-paper-white);
  color: var(--ldr-ink);
  font-family: var(--ldr-serif);
  font-size: 1.125rem;
  line-height: 1.6;
  /* Gentium's default figures are lining; keep them that way in tables. */
  font-variant-numeric: lining-nums;
  -webkit-font-smoothing: antialiased;
}

/* Layout ------------------------------------------------------------------ */

.wrap {
  width: 100%;
  max-width: 46rem;
  margin-inline: auto;
  padding-inline: var(--ldr-gutter);
}

/* Prose is narrower than the wrapper: the style guide asks for a 60–75
   character measure, which is tighter than a comfortable page width. */
.prose {
  max-width: var(--ldr-measure);
}

.band {
  background: var(--ldr-paper);
  border-block: 1px solid var(--ldr-rule);
}

main {
  display: block;
}

/* Every page brings its own wrapper, so a full-bleed band (the home hero)
   can sit outside it. */
.page {
  padding-block: var(--ldr-space-5) var(--ldr-space-6);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ldr-forest);
  color: var(--ldr-paper);
  padding: var(--ldr-space-2) var(--ldr-space-3);
  z-index: 10;
}

.skip-link:focus {
  left: 0;
}

/* Headings ---------------------------------------------------------------- */

/* Colour by level, not by page type — the generalisable rule from the style
   guide. The slide CSS colours by layout, which has no analogue here. */
h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--ldr-serif);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.12;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2rem, 5vw, 2.6rem);
  color: var(--ldr-maple);
  margin: 0 0 var(--ldr-space-2);
}

h2 {
  font-size: clamp(1.5rem, 3.5vw, 1.8rem);
  color: var(--ldr-forest);
  margin: var(--ldr-space-5) 0 var(--ldr-space-2);
}

h3 {
  font-size: 1.35rem;
  color: var(--ldr-maple);
  margin: var(--ldr-space-4) 0 var(--ldr-space-1);
}

h4,
h5,
h6 {
  font-size: 1.1rem;
  color: var(--ldr-ink);
  margin: var(--ldr-space-3) 0 var(--ldr-space-1);
}

/* markdown-it-anchor wraps the heading text in a link; it should read as a
   heading, not as a link, until you hover it. */
h2 > a,
h3 > a,
h4 > a {
  color: inherit;
  text-decoration: none;
}

h2 > a:hover,
h3 > a:hover,
h4 > a:hover {
  text-decoration: underline;
  text-decoration-color: var(--ldr-sage);
  text-underline-offset: 4px;
}

/* The signature mark: a short 40x2 accent rule under a heading. Opt in with
   .ruled so it marks structure rather than decorating every heading. */
.ruled::after {
  content: '';
  display: block;
  width: 40px;
  height: 2px;
  margin: 0.6em 0 1em;
  background: var(--ldr-forest);
}

.ruled--maple::after {
  background: var(--ldr-maple);
}

/* Body copy --------------------------------------------------------------- */

p {
  margin: 0 0 var(--ldr-space-3);
}

/* Style guide §7: links forest, hover forest-deep. The slide theme uses sage,
   which is too low-contrast for body text on the web. */
a {
  color: var(--ldr-forest);
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

a:hover {
  color: var(--ldr-forest-deep);
}

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

strong,
b {
  font-weight: 700;
  color: var(--ldr-forest);
}

/* Deliberately NOT sage. In a slide, italic text is short and large; in an
   article it is a book title inside a paragraph, and sage on warm white
   fails every contrast threshold. Sage italics live on .lead, .caption and
   blockquotes, where the type is display-sized. See docs/theme.md. */
em,
i {
  font-style: italic;
  color: inherit;
}

/* Lists: em-dash markers in sage, maple numerals. */
.prose ul,
.post-body ul {
  list-style: none;
  padding-left: 0;
}

.prose ul > li,
.post-body ul > li {
  position: relative;
  padding-left: 1.6em;
  margin: 0 0 var(--ldr-space-2);
}

.prose ul > li::before,
.post-body ul > li::before {
  content: '—';
  position: absolute;
  left: 0;
  color: var(--ldr-sage);
  font-weight: 700;
}

.prose ol,
.post-body ol {
  padding-left: 1.4em;
}

.prose ol > li,
.post-body ol > li {
  margin: 0 0 var(--ldr-space-2);
}

.prose ol > li::marker,
.post-body ol > li::marker {
  color: var(--ldr-maple);
  font-weight: 700;
}

/* Nested lists sit tighter than top-level ones. */
li > ul,
li > ol {
  margin-top: var(--ldr-space-2);
}

hr {
  border: 0;
  height: 1px;
  background: var(--ldr-rule);
  margin: var(--ldr-space-5) 0;
}

/* Pull-quote: oversized sage quote mark, maple attribution over a forest
   rule. Lifted from the slide theme's quote layout. */
blockquote {
  margin: var(--ldr-space-4) 0;
  padding: 0;
  border: 0;
  font-style: italic;
  color: var(--ldr-ink-soft);
  font-size: 1.25rem;
  line-height: 1.45;
}

blockquote::before {
  content: '\201C';
  display: block;
  font-size: 4.5rem;
  line-height: 0.7;
  color: var(--ldr-sage);
  font-weight: 700;
  margin: 0 0 0.05em;
}

blockquote p:last-child {
  margin-bottom: 0;
}

blockquote cite,
.quote-by {
  display: block;
  margin-top: var(--ldr-space-3);
  font-size: 0.85rem;
  font-style: normal;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ldr-maple);
  font-weight: 700;
}

blockquote cite::before,
.quote-by::before {
  content: '';
  display: block;
  width: 40px;
  height: 2px;
  background: var(--ldr-forest);
  margin: 0 0 0.9em;
}

/* Tables: hairlines only, never a boxed grid. */
table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--ldr-space-3) 0;
  font-size: 0.95em;
}

th,
td {
  text-align: left;
  padding: 0.5em 0.9em 0.5em 0;
  border-bottom: 1px solid var(--ldr-rule);
  vertical-align: top;
}

th {
  color: var(--ldr-forest);
  font-weight: 700;
  border-bottom-color: var(--ldr-sage);
}

/* A table can outgrow the measure; let it scroll rather than break out. */
.table-scroll {
  overflow-x: auto;
}

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

/* A standalone image reads as an inset surface, as on a slide. */
.post-body p > img:only-child {
  display: block;
  margin: var(--ldr-space-3) auto;
  background: var(--ldr-panel);
  border: 1px solid var(--ldr-rule);
  border-radius: 4px;
}

figure {
  margin: var(--ldr-space-4) 0;
}

figcaption {
  margin-top: var(--ldr-space-2);
  padding-left: var(--ldr-space-2);
  border-left: 3px solid var(--ldr-sage);
  font-style: italic;
  color: var(--ldr-ink-soft);
  font-size: 0.9em;
}

/* Text roles -------------------------------------------------------------- */

/* Eyebrow / kicker: the signature label.
   The slide theme has a sage variant for labelling content rather than
   kicking off a section. There is no sage variant here: at 0.8rem the
   eyebrow is not large text, so WCAG asks 4.5:1 and sage on warm white
   gives 2.9:1 — and on a post the eyebrow carries the date, which is
   information. Forest reaches 9.3:1. See docs/theme.md. */
.eyebrow {
  font-size: 0.8rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ldr-forest);
  font-weight: 700;
  margin: 0 0 var(--ldr-space-2);
}

.lead {
  font-style: italic;
  color: var(--ldr-ink-soft);
  font-size: 1.25rem;
  line-height: 1.45;
  margin: 0 0 var(--ldr-space-3);
  max-width: var(--ldr-measure);
}

.caption {
  font-style: italic;
  color: var(--ldr-ink-soft);
  font-size: 0.9rem;
}

/* Meta lines use ink-soft, not ink-faint or sage: those two sit under the
   contrast floor at body size. See docs/theme.md. */
.meta {
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  color: var(--ldr-ink-soft);
  font-variant: small-caps;
}

.meta a {
  color: inherit;
}

/* The separator is drawn by CSS, not written into the markup: it is
   presentational, so a screenreader should never reach it and the
   text-contrast rule does not apply to it. */
.meta__item + .meta__item::before {
  content: '·';
  color: var(--ldr-sage);
  padding-inline: 0.5em;
}

/* Colour utilities, matching the slide theme's MDC helpers. */
.forest {
  color: var(--ldr-forest);
}
.forest-deep {
  color: var(--ldr-forest-deep);
}
.sage {
  color: var(--ldr-sage);
}
.maple {
  color: var(--ldr-maple);
}
.ochre {
  color: var(--ldr-ochre);
}
.ink {
  color: var(--ldr-ink);
}
.ink-soft {
  color: var(--ldr-ink-soft);
}
.ink-faint {
  color: var(--ldr-ink-faint);
}

/* Masthead ---------------------------------------------------------------- */

.masthead {
  background: var(--ldr-paper);
  border-bottom: 1px solid var(--ldr-sage);
}

.masthead__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ldr-space-2) var(--ldr-space-4);
  padding-block: var(--ldr-space-3);
}

.masthead__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--ldr-forest);
  text-decoration: none;
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
}

.masthead__brand:hover {
  color: var(--ldr-forest-deep);
}

.masthead__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ldr-space-3);
  font-size: 0.85rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 700;
}

.masthead__nav a {
  color: var(--ldr-ink-soft);
  text-decoration: none;
}

.masthead__nav a:hover {
  color: var(--ldr-forest);
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* aria-current marks the section you are in; the maple is the one spark. */
.masthead__nav a[aria-current='page'] {
  color: var(--ldr-maple);
}

/* The house mark: the botanical bloom from the slide theme. */
.mark {
  display: block;
  flex: none;
}

/* Hero -------------------------------------------------------------------- */

/* Mode A: the one parchment field on the site, for the landing page only. */
.hero {
  background: var(--ldr-paper-deep);
  border-bottom: 1px solid var(--ldr-rule);
}

.hero__inner {
  display: flex;
  align-items: center;
  gap: var(--ldr-space-5);
  padding-block: var(--ldr-space-6);
}

.hero__text {
  flex: 1 1 auto;
  min-width: 0;
}

.hero__mark {
  flex: 0 0 auto;
  color: var(--ldr-forest);
}

.hero h1 {
  font-size: clamp(2.2rem, 6vw, 3rem);
}

.hero .lead {
  margin-bottom: 0;
}

@media (max-width: 34rem) {
  .hero__mark {
    display: none;
  }
}

/* Post lists -------------------------------------------------------------- */

/* Flat: separated by space and a hairline, never a card with a shadow. */
.post-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.post-list > li {
  padding-block: var(--ldr-space-4);
  border-bottom: 1px solid var(--ldr-rule);
}

.post-list > li:first-child {
  padding-top: var(--ldr-space-2);
}

.post-list > li::before {
  content: none;
}

.post-list h3 {
  margin: var(--ldr-space-1) 0 var(--ldr-space-2);
  font-size: 1.4rem;
}

/* Forest, not the by-level maple. A listing repeats one heading level many
   times, and colouring each entry maple turns "one deliberate red mark per
   view" into a wall of red. These are links first, so they take the link
   colour and keep maple for the hover. */
.post-list h3 a {
  color: var(--ldr-forest);
  text-decoration: none;
}

.post-list h3 a:hover {
  color: var(--ldr-maple);
  text-decoration: underline;
  text-underline-offset: 4px;
}

.post-list p {
  margin: 0 0 var(--ldr-space-2);
  max-width: var(--ldr-measure);
}

/* Tags -------------------------------------------------------------------- */

.taglist {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ldr-space-2) var(--ldr-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.taglist > li::before {
  content: none;
}

/* A tag is a letter-spaced label with a sage hairline under it — a mark,
   not a pill. */
.tag {
  display: inline-block;
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--ldr-forest);
  text-decoration: none;
  border-bottom: 1px solid var(--ldr-sage);
  padding-bottom: 2px;
}

.tag:hover {
  color: var(--ldr-maple);
  border-bottom-color: var(--ldr-maple);
}

.tag-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--ldr-space-4);
}

.tag-cards > li {
  padding-left: var(--ldr-space-3);
  border-left: 3px solid var(--ldr-sage);
}

.tag-cards > li::before {
  content: none;
}

/* Heading level varies by context (h2 on /tags/, h3 under a section
   heading on the home page), so the component sets the colour itself
   rather than inheriting the by-level rule. Same reason as .post-list. */
.tag-cards h2,
.tag-cards h3 {
  margin: 0 0 var(--ldr-space-1);
  font-size: 1.4rem;
  color: var(--ldr-forest);
}

.tag-cards h2 a,
.tag-cards h3 a {
  text-decoration: none;
  color: inherit;
}

.tag-cards h2 a:hover,
.tag-cards h3 a:hover {
  color: var(--ldr-maple);
  text-decoration: underline;
  text-underline-offset: 4px;
}

.tag-cards p {
  margin: 0 0 var(--ldr-space-1);
  color: var(--ldr-ink-soft);
  font-style: italic;
}

/* Post page --------------------------------------------------------------- */

.post-header {
  margin-bottom: var(--ldr-space-4);
}

/* The measure applies to the text, not to the column: constraining
   .post-body itself would squeeze code blocks and figures to 65ch too, and
   a wide line of code would then scroll inside a needlessly narrow panel. */
.post-body > p,
.post-body > ul,
.post-body > ol,
.post-body > h2,
.post-body > h3,
.post-body > h4,
.post-body > h5,
.post-body > h6,
.post-body > blockquote,
.post-body > hr {
  max-width: var(--ldr-measure);
}

.post-body > h2:first-child,
.post-body > h3:first-child {
  margin-top: 0;
}

.post-footer {
  max-width: var(--ldr-measure);
  margin-top: var(--ldr-space-5);
  padding-top: var(--ldr-space-3);
  border-top: 1px solid var(--ldr-sage);
}

/* Previous / next reads as two quiet columns, not as buttons. */
.post-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--ldr-space-3);
  margin-top: var(--ldr-space-4);
}

.post-nav__item {
  max-width: 20rem;
}

.post-nav__item--next {
  margin-left: auto;
  text-align: right;
}

/* Forest, not sage: a 0.75rem label is not large text, and this one says
   which way the link goes. Same reasoning as .eyebrow. */
.post-nav__label {
  display: block;
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ldr-forest);
  font-weight: 700;
  margin-bottom: 0.2em;
}

/* Callout ----------------------------------------------------------------- */

.callout {
  background: var(--ldr-paper);
  border: 1px solid var(--ldr-rule);
  border-left: 3px solid var(--ldr-sage);
  border-radius: 4px;
  padding: var(--ldr-space-3);
  margin: var(--ldr-space-4) 0;
  max-width: var(--ldr-measure);
}

.callout > :last-child {
  margin-bottom: 0;
}

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

.site-footer {
  background: var(--ldr-paper);
  border-top: 1px solid var(--ldr-sage);
  padding-block: var(--ldr-space-4);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ldr-space-2) var(--ldr-space-3);
  font-variant: small-caps;
  letter-spacing: 0.1em;
  font-size: 0.85rem;
  color: var(--ldr-ink-soft);
}

.site-footer__inner a {
  color: var(--ldr-ink-soft);
  text-decoration-color: var(--ldr-sage);
}

.site-footer__inner a:hover {
  color: var(--ldr-forest);
}

.site-footer__mark {
  color: var(--ldr-forest);
}

/* Empty state ------------------------------------------------------------- */

.empty {
  font-style: italic;
  color: var(--ldr-ink-soft);
  max-width: var(--ldr-measure);
}

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

/* Print: the parchment bands are ink-heavy, so drop them. */
@media print {
  .masthead__nav,
  .post-nav,
  .site-footer {
    display: none;
  }

  body {
    background: #fff;
    font-size: 11pt;
  }

  .hero,
  .band,
  .callout {
    background: none;
  }
}
