/*
 * closeai-site — public blog theme.
 *
 * Design intent: an editorial reading surface. Warm paper tone, one accent, a
 * single column of text at a comfortable measure (~68ch), generous vertical
 * rhythm. Nothing here imitates a WordPress theme; the goal is that long Chinese
 * and Latin text both read well on a phone at 320px and on a 1440px display.
 *
 * Constraints this file honours:
 *  - no layout that overflows horizontally at 320px (N-01)
 *  - body text >= 16px, no user-scalable=no (N-03)
 *  - visible focus everywhere, never `outline: none` alone (N-05)
 *  - contrast: text tokens are chosen to clear 4.5:1 on their background (N-08)
 *  - touch targets >= 44px for primary controls (N-02)
 *  - dark mode by preference, same tokens
 */

/* ---------- tokens ---------- */

:root {
  color-scheme: light dark;

  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
  --font-serif: ui-serif, Georgia, "Songti SC", "Noto Serif CJK SC", "Source Han Serif SC", serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --paper: #fbfaf7;
  --surface: #ffffff;
  --surface-sunken: #f3f1eb;
  --ink: #191b1f;
  --ink-soft: #454a52;
  --ink-faint: #5f6672;
  --rule: #e2ded4;
  --rule-strong: #cbc5b7;
  --accent: #1f5f4f;
  --accent-soft: #e8f1ed;
  --accent-ink: #14453a;
  --danger: #9a3412;
  --danger-soft: #fdf1ec;
  --warning: #6b3f02;
  --warning-soft: #fdf5e6;

  --measure: 68ch;
  --wrap: 72rem;
  --gap: clamp(1rem, 2.5vw, 1.75rem);
  --radius: 10px;
  --radius-sm: 6px;
  --shadow-sm: 0 1px 2px rgb(25 27 31 / 6%);
  --shadow-md: 0 6px 24px rgb(25 27 31 / 8%);

  --step--1: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
  --step-0: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  --step-1: clamp(1.125rem, 1.08rem + 0.24vw, 1.25rem);
  --step-2: clamp(1.3125rem, 1.24rem + 0.38vw, 1.5rem);
  --step-3: clamp(1.5rem, 1.38rem + 0.6vw, 1.875rem);
  --step-4: clamp(1.75rem, 1.54rem + 1.05vw, 2.5rem);

  /* Pinned header height, and the air an in-page target leaves under it.
     `scroll-padding-top` and `.site-header__inner` both derive from `--header-h`,
     so the height is written once. 3.8125rem = 3.75rem row + 1px bottom rule
     (61px measured at >= 46rem); the narrow branch raises it. */
  --header-h: 3.8125rem;
  --anchor-gap: 0.75rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #14161a;
    --surface: #1b1e23;
    --surface-sunken: #23272d;
    --ink: #eef0f2;
    --ink-soft: #c2c8d0;
    --ink-faint: #a4acb6;
    --rule: #2c3138;
    --rule-strong: #3d444d;
    --accent: #6fcaa8;
    --accent-soft: #1c3b32;
    --accent-ink: #a8e5cd;
    --danger: #ffab91;
    --danger-soft: #3a211a;
    --warning: #f0c07a;
    --warning-soft: #33280f;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%);
    --shadow-md: 0 6px 24px rgb(0 0 0 / 45%);
  }
}

/* ---------- reset ---------- */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* In-page targets land *below* the pinned header. A fragment scroll puts the
     target at y = 0 — the strip `.site-header` covers. This sits on the scroll
     container rather than on selectors because every jump (fragment load,
     in-page link, `:target`, `focus()`) scrolls the document, including heading
     ids written inside a post body. No script, fragments and SSR unchanged. */
  scroll-padding-top: calc(var(--header-h) + var(--anchor-gap));
}

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

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.7;
  /* CJK needs slightly looser leading and allows breaking between characters. */
  line-break: strict;
  /*
   * `anywhere`, not `break-word`. Both break a long token that cannot fit its
   * line, but only `anywhere` also shrinks the element's min-content width — and
   * min-content is what decides how narrow a flex/grid item may get. With
   * `break-word` an unbreakable 46-character handle inside `.card__meta`, a
   * comment byline or a `.state` heading sets a min-content width the 320px
   * layout cannot honour, and the whole page scrolls sideways (N-01).
   */
  overflow-wrap: anywhere;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* Long URLs, code, and unbreakable latin strings must not force a scrollbar. */
img,
svg,
video,
canvas {
  max-width: 100%;
  height: auto;
  display: block;
}

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

a:hover {
  color: var(--accent-ink);
  text-decoration-thickness: 2px;
}

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

button,
input,
textarea,
select {
  font: inherit;
  color: inherit;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 0.4em;
  line-height: 1.25;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

p {
  margin: 0 0 1em;
}

/* ---------- utilities ---------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gap);
}

.wrap--narrow {
  max-width: calc(var(--measure) + var(--gap) * 2);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  inset-inline-start: 0.5rem;
  inset-block-start: -100%;
  z-index: 50;
  padding: 0.6rem 1rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
}

.skip-link:focus {
  inset-block-start: 0.5rem;
}

/* ---------- header ---------- */

.site-header {
  border-bottom: 1px solid var(--rule);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  position: sticky;
  top: 0;
  z-index: 20;
  backdrop-filter: blur(8px);
}

/*
 * The header is a **height contract**, not a content-driven box.
 *
 * `--header-h` is what the anchor offset reserves, so the header has to stay
 * under it whatever the site title or the navigation contains. It used to be
 * content-driven: a 30-character title plus five links pushed the narrow header
 * from two rows to three (186.63px measured at 360px) while the offset still
 * reserved 124px, and every anchor in the document landed 63px under the header.
 *
 * A promise that content can break is not a promise. So the row count is fixed
 * here — one row above 46rem, two below, never more — and each row is bounded at
 * its source: the brand is a single line that truncates, the nav is a single row
 * that scrolls inside its own box. Both are paint/border-box effects, so the
 * accessible names, the tab order and the SSR output are untouched.
 */
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  /* The same number the anchor offset uses, so the two cannot drift. */
  min-height: calc(var(--header-h) - 1px);
  /* Wrapping is what let a long title push the nav onto a second row. */
  flex-wrap: nowrap;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 650;
  font-size: var(--step-1);
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.02em;
  padding-block: 0.5rem;
  /*
   * The title gives way before the nav does. A large shrink factor is how that is
   * said in flexbox: shrink is shared in proportion to `base × factor`, so the
   * brand absorbs the shortfall and the links keep their labels. It stops at 8rem
   * (about eight CJK characters) — below that the nav scrolls instead, because a
   * title cut to four characters tells a reader nothing.
   */
  flex: 0 999 auto;
  min-width: 8rem;
  max-width: 100%;
}

.brand:hover {
  color: var(--accent-ink);
}

.brand__logo {
  border-radius: 6px;
  /* A logo is 28px or it is squashed; the title is the part that gives. */
  flex: none;
}

/*
 * Truncation is a paint effect: the link's accessible name is still the whole
 * title, and a screen reader in browse mode reads it in full. What changes is
 * that the header's height stops depending on how long the title is.
 */
.brand__name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

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

.site-nav__list {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
  /* One row, always: rows are what the height budget counts. When the links do
     not fit they scroll sideways in place instead of growing the header. */
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.site-nav__list::-webkit-scrollbar {
  display: none;
}

.site-nav__item {
  position: relative;
  /* The item is a row: a link and its sublist share one line, so a nav entry
     with children costs width, never height. */
  display: flex;
  align-items: center;
}

.site-nav__list a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0.35rem 0.7rem;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  text-decoration: none;
  font-size: var(--step--1);
  font-weight: 550;
  /* A wrapped label is a second row, whatever the title is doing. */
  white-space: nowrap;
}

.site-nav__list a:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}

.site-nav__list a[aria-current="page"] {
  color: var(--accent-ink);
  background: var(--accent-soft);
}

/*
 * The list is a scroll container, so its edges clip anything painted outside its
 * padding box — including an `outline-offset: 2px` focus ring. Inset by 2px: the
 * ring is still 2px of accent on every side and it can never be cut in half.
 * Keyboard users tabbing to an off-screen link get it scrolled into view by the
 * browser, so the ring is where the focus is.
 */
.site-nav__list a:focus-visible {
  outline-offset: -2px;
}

.site-nav__sublist {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.25rem;
  margin: 0;
  padding: 0 0 0 0.5rem;
  list-style: none;
}

/*
 * Narrow screens: the nav wraps under the brand instead of collapsing behind a
 * disclosure. Wrapping keeps a single landmark and keeps every link reachable
 * with no JavaScript. Two rows is the whole budget: 0.35rem of padding, one
 * brand line, one nav row, so `--header-h: 7rem` stays a ceiling rather than a
 * guess.
 */
@media (max-width: 46rem) {
  :root {
    --header-h: 7rem;
  }

  .site-header__inner {
    /* A floor for the brand row only — the wrapped block is content-driven. */
    min-height: 3.25rem;
    padding-block: 0.35rem;
    gap: 0.25rem var(--gap);
    /* Two rows here, and exactly two: the brand row and the nav row. */
    flex-wrap: wrap;
  }

  .site-nav {
    flex-basis: 100%;
  }

  .site-nav__list {
    gap: 0.15rem;
  }

  .site-nav__list a {
    padding-inline: 0.55rem;
  }
}

/* ---------- main ---------- */

.site-main {
  flex: 1 0 auto;
  padding-block: clamp(1.5rem, 3vw, 2.5rem) clamp(2.5rem, 5vw, 4rem);
}

.site-main:focus {
  outline: none;
}

/* ---------- home ---------- */

.home-intro {
  font-size: var(--step-1);
  color: var(--ink-soft);
  max-width: var(--measure);
  margin-block: 0 1.75rem;
  text-wrap: pretty;
}

.archive__header {
  margin-block: 0 clamp(1.25rem, 3vw, 2rem);
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--rule);
}

.archive__kind {
  margin: 0 0 0.35rem;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-ink);
}

.archive__title {
  font-size: var(--step-3);
  margin-bottom: 0.25rem;
}

.archive__description {
  color: var(--ink-soft);
  max-width: var(--measure);
  margin-bottom: 0.5rem;
  text-wrap: pretty;
}

.archive__count {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* ---------- post list & cards ---------- */

.post-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: clamp(1rem, 2.2vw, 1.5rem);
}

.post-list--compact {
  gap: 0.75rem;
}

.post-list__item {
  display: block;
}

.card {
  display: grid;
  gap: 1rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: clamp(1rem, 2.2vw, 1.35rem);
  box-shadow: var(--shadow-sm);
  transition: border-color 120ms ease, box-shadow 120ms ease;
  height: 100%;
}

.card:hover {
  border-color: var(--rule-strong);
  box-shadow: var(--shadow-md);
}

.card__body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}

.card__eyebrow {
  margin: 0;
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

.card__title {
  font-size: var(--step-2);
  margin: 0;
  line-height: 1.3;
}

.card__title a {
  color: var(--ink);
  text-decoration: none;
}

.card__title a:hover {
  color: var(--accent-ink);
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

.card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.meta-sep {
  color: var(--rule-strong);
}

.card__excerpt {
  margin: 0;
  color: var(--ink-soft);
  text-wrap: pretty;
  overflow-wrap: anywhere;
}

.card__cover {
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-sunken);
  aspect-ratio: 16 / 9;
}

.card__cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.card--with-cover {
  grid-template-rows: auto 1fr;
}

/* Featured card: image left, text right on wider screens. */
@media (min-width: 48rem) {
  .card--featured.card--with-cover {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    grid-template-rows: auto;
    align-items: stretch;
    gap: clamp(1rem, 2vw, 1.5rem);
  }

  .card--featured .card__title {
    font-size: var(--step-3);
  }

  .card--featured .card__cover {
    aspect-ratio: 4 / 3;
    height: 100%;
  }
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: var(--surface-sunken);
  color: var(--ink-soft);
  font-size: var(--step--1);
  text-decoration: none;
  border: 1px solid transparent;
}

.chip:hover {
  background: var(--accent-soft);
  color: var(--accent-ink);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}

.chip--category {
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-weight: 550;
}

/* ---------- article ---------- */

.post__header {
  margin-block: 0 1.5rem;
}

.post__title {
  font-family: var(--font-serif);
  font-size: var(--step-4);
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin-bottom: 0.5rem;
  text-wrap: balance;
}

.post__lead {
  font-size: var(--step-1);
  color: var(--ink-soft);
  max-width: var(--measure);
  margin-bottom: 0.5rem;
  text-wrap: pretty;
}

.post__reading {
  font-size: var(--step--1);
  color: var(--ink-faint);
  margin: 0;
}

.post-cover {
  margin: 0 0 clamp(1.5rem, 3vw, 2rem);
}

.post-cover img {
  width: 100%;
  border-radius: var(--radius);
  background: var(--surface-sunken);
}

.post-cover figcaption {
  margin-top: 0.5rem;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.post__body {
  margin-bottom: 2rem;
}

/* Prose: the reading column. Everything here is sanitized HTML output. */
.prose {
  font-family: var(--font-serif);
  font-size: clamp(1.0625rem, 1rem + 0.25vw, 1.1875rem);
  line-height: 1.85;
  color: var(--ink);
  overflow-wrap: break-word;
}

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

.prose h2,
.prose h3,
.prose h4 {
  font-family: var(--font-sans);
  margin-top: 2em;
  margin-bottom: 0.6em;
  line-height: 1.3;
}

.prose h2 {
  font-size: var(--step-3);
}

.prose h3 {
  font-size: var(--step-2);
}

.prose h4 {
  font-size: var(--step-1);
}

.prose a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.prose blockquote {
  margin-inline: 0;
  padding: 0.25rem 0 0.25rem 1.1rem;
  border-inline-start: 3px solid var(--accent);
  color: var(--ink-soft);
  font-style: italic;
}

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

.prose ul,
.prose ol {
  padding-inline-start: 1.4em;
}

.prose li + li {
  margin-top: 0.35em;
}

.prose img {
  border-radius: var(--radius-sm);
  margin-block: 1.5em;
}

.prose figure {
  margin-inline: 0;
}

.prose figcaption {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--ink-faint);
  text-align: center;
}

/* Code must scroll inside its own box, never widen the page. */
.prose pre {
  font-family: var(--font-mono);
  font-size: 0.875em;
  line-height: 1.6;
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  padding: 0.9rem 1rem;
  overflow-x: auto;
  tab-size: 2;
  -webkit-overflow-scrolling: touch;
}

.prose :not(pre) > code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  background: var(--surface-sunken);
  padding: 0.12em 0.38em;
  border-radius: 4px;
  overflow-wrap: anywhere;
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-sans);
  font-size: 0.9375em;
  display: block;
  overflow-x: auto;
}

.prose th,
.prose td {
  border: 1px solid var(--rule);
  padding: 0.5rem 0.7rem;
  text-align: start;
}

.prose th {
  background: var(--surface-sunken);
  font-weight: 600;
}

.prose hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin-block: 2em;
}

.prose kbd {
  font-family: var(--font-mono);
  font-size: 0.85em;
  border: 1px solid var(--rule-strong);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 0.1em 0.35em;
}

/* ---------- post meta ---------- */

.post-meta {
  display: grid;
  gap: 0.85rem;
  padding: 1.1rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  margin-bottom: 1.5rem;
}

.post-meta__author {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.avatar {
  border-radius: 999px;
  object-fit: cover;
  flex: none;
  background: var(--surface-sunken);
}

.post-meta__author-name {
  margin: 0;
  font-weight: 600;
}

.post-meta__author-bio {
  margin: 0.15rem 0 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.post-meta__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.5rem;
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.post-meta__facts div {
  display: flex;
  gap: 0.4rem;
}

.post-meta__facts dt {
  margin: 0;
}

.post-meta__facts dd {
  margin: 0;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.post-meta__updated {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

/* ---------- breadcrumbs ---------- */

.breadcrumbs {
  margin-bottom: 1rem;
}

.breadcrumbs__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.breadcrumbs__list li + li::before {
  content: "/";
  margin-inline-end: 0.35rem;
  color: var(--rule-strong);
}

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

.breadcrumbs a:hover {
  color: var(--accent-ink);
  text-decoration: underline;
}

/* ---------- related ---------- */

.related {
  margin-block: 2rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
}

.related__title {
  font-size: var(--step-1);
}

.related__list {
  margin: 0;
  padding-inline-start: 1.2em;
  color: var(--ink-soft);
}

.related__list li + li {
  margin-top: 0.35rem;
}

.related__date {
  margin-inline-start: 0.4rem;
  font-size: var(--step--1);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* ---------- comments ---------- */

.comments {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
}

.comments__title {
  font-size: var(--step-2);
  margin-bottom: 1rem;
}

.comment-list {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: grid;
  gap: 1rem;
}

.comment-list--nested {
  margin: 0.85rem 0 0;
  padding-inline-start: clamp(0.75rem, 3vw, 1.5rem);
  border-inline-start: 2px solid var(--rule);
}

.comment__inner {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
}

.comment__header {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}

.comment__identity {
  min-width: 0;
  flex: 1;
}

.comment__author-name {
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}

a.comment__author-name:hover {
  color: var(--accent-ink);
  text-decoration: underline;
}

.comment__date {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.comment__permalink {
  color: var(--ink-faint);
  text-decoration: none;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
}

.comment__permalink:hover {
  background: var(--surface-sunken);
  color: var(--accent-ink);
}

/* Comment text is untrusted plain text: preserve breaks, allow breaking anywhere. */
.comment__body {
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

.comment__body p {
  margin: 0 0 0.65em;
}

.comment__body p:last-child {
  margin-bottom: 0;
}

.comment-form {
  margin-top: 1.5rem;
  padding: clamp(1rem, 2.5vw, 1.5rem);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.comment-form__title {
  font-size: var(--step-1);
  margin-bottom: 0.35rem;
}

.comment-form__policy {
  font-size: var(--step--1);
  color: var(--ink-faint);
  margin-bottom: 1rem;
}

.comment-form__form {
  display: grid;
  gap: 1rem;
}

.field-grid {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

.field {
  display: grid;
  gap: 0.3rem;
  min-width: 0;
}

.field__label {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--ink-soft);
}

.field__required {
  color: var(--danger);
  font-weight: 700;
}

.field__input {
  width: 100%;
  min-height: 2.75rem;
  padding: 0.55rem 0.7rem;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  font-size: var(--step-0);
}

.field__input:hover {
  border-color: var(--ink-faint);
}

.field__input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

.field__textarea {
  min-height: 8rem;
  resize: vertical;
  line-height: 1.6;
}

.field__hint {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.field__error {
  margin: 0;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--danger);
}

/*
 * Invalid fields: two selectors, one state.
 *
 * SSR keys the wrapper class when the server already knows which field failed
 * (`.field--invalid`); the enhancement script keys the attribute on the control
 * it just marked (`aria-invalid="true"`). Styling only the class is why the live
 * form's "check the highlighted fields" pointed at fields that were not
 * highlighted — the state existed in the accessibility tree and nowhere on
 * screen. Both paths are listed here so there is one appearance to reason about.
 *
 * The inset bar on the leading edge carries the state a second time, so it is
 * not colour alone; it also cannot shift the layout the way a thicker border
 * would, which keeps the textarea from jogging on every failed submit.
 */
.field--invalid .field__input,
.field__input[aria-invalid="true"] {
  border-color: var(--danger);
  background: var(--danger-soft);
  box-shadow: inset 2px 0 0 var(--danger);
}

/* The focus ring must sit outside the error border rather than replace it: a
   field whose error styling vanishes on focus reads as "already fixed". */
.field--invalid .field__input:focus-visible,
.field__input[aria-invalid="true"]:focus-visible {
  outline-color: var(--danger);
  border-color: var(--danger);
}

.comment-form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}

.comment-form__status {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

/* Failed submits are announced separately from the neutral progress line, so a
   stale "Submitting…" can never sit above an error. */
.comment-form__status--error {
  flex-basis: 100%;
  color: var(--danger);
  font-weight: 600;
  white-space: pre-line;
}

.comment-form__status[hidden] {
  display: none;
}

/* ---------- buttons ---------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 2.75rem;
  min-width: 2.75rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: #ffffff;
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: filter 120ms ease;
}

.button:hover {
  filter: brightness(1.08);
  color: #ffffff;
}

.button:disabled,
.button[aria-disabled="true"] {
  opacity: 0.6;
  cursor: progress;
}

.button--ghost {
  background: transparent;
  color: var(--accent-ink);
  border-color: var(--rule-strong);
}

.button--ghost:hover {
  background: var(--accent-soft);
  filter: none;
}

@media (prefers-color-scheme: dark) {
  .button {
    color: #0d1512;
  }

  .button:hover {
    color: #0d1512;
  }
}

/* ---------- search ---------- */

.search-form {
  margin-bottom: clamp(1.25rem, 3vw, 2rem);
  max-width: 34rem;
}

.search-form__label {
  display: block;
  margin-bottom: 0.35rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--ink-soft);
}

.search-form__row {
  display: flex;
  gap: 0.5rem;
}

.search-form__input {
  flex: 1;
  min-width: 0;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  font-size: var(--step-0);
}

.search-form__input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.search-form__button {
  flex: none;
}

.search-results__summary {
  font-size: var(--step--1);
  color: var(--ink-faint);
  margin-bottom: 1rem;
}

/* ---------- terms ---------- */

.term-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.65rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
}

.term {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 0.85rem 1rem;
}

.term__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: 2.75rem;
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
}

.term__link:hover {
  color: var(--accent-ink);
}

.term__count {
  font-size: var(--step--1);
  color: var(--ink-faint);
  background: var(--surface-sunken);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
  font-variant-numeric: tabular-nums;
}

.term__description {
  margin: 0.35rem 0 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

/* ---------- pagination ---------- */

.pagination {
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
  display: grid;
  gap: 0.6rem;
  justify-items: center;
}

.pagination__summary {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.pagination__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0;
  padding: 0;
  list-style: none;
  align-items: center;
  justify-content: center;
}

.pagination__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink-soft);
  font-size: var(--step--1);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}

a.pagination__link:hover {
  border-color: var(--accent);
  color: var(--accent-ink);
}

.pagination__link.is-current {
  background: var(--accent);
  border-color: var(--accent);
  color: #ffffff;
  font-weight: 650;
}

@media (prefers-color-scheme: dark) {
  .pagination__link.is-current {
    color: #0d1512;
  }
}

.pagination__link.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.pagination__gap {
  padding-inline: 0.25rem;
  color: var(--ink-faint);
}

/* ---------- states ---------- */

.state {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 0.5rem;
  padding: clamp(2rem, 6vw, 3.5rem) 1rem;
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface);
  max-width: 32rem;
  margin-inline: auto;
}

.state--error {
  border-style: solid;
  border-color: color-mix(in srgb, var(--danger) 40%, var(--rule));
  background: var(--danger-soft);
}

.state__icon {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 999px;
  background: var(--surface-sunken);
  color: var(--ink-soft);
  font-weight: 700;
  margin: 0;
}

.state--error .state__icon {
  background: var(--danger);
  color: #ffffff;
}

.state__code {
  font-family: var(--font-mono);
  font-size: var(--step-3);
  font-weight: 700;
  color: var(--ink-faint);
  margin: 0;
  letter-spacing: 0.1em;
}

.state__title {
  font-size: var(--step-2);
  margin: 0;
}

.state__message {
  margin: 0;
  color: var(--ink-soft);
  max-width: 40ch;
}

.state__meta {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.state__action {
  margin: 0.5rem 0 0;
}

.skeleton {
  display: grid;
  gap: 0.5rem;
  width: min(100%, 28rem);
  margin-top: 0.75rem;
}

.skeleton span {
  display: block;
  height: 0.7rem;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--surface-sunken), var(--rule), var(--surface-sunken));
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
}

.skeleton span:nth-child(2) {
  width: 85%;
}

.skeleton span:nth-child(3) {
  width: 60%;
}

@keyframes skeleton-shimmer {
  0% {
    background-position: 200% 0;
  }

  100% {
    background-position: -200% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton span {
    animation: none;
  }
}

.notice {
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--rule);
  background: var(--surface-sunken);
  color: var(--ink-soft);
  font-size: var(--step--1);
  margin: 0 0 1rem;
}

.notice--warning {
  border-color: color-mix(in srgb, var(--warning) 45%, var(--rule));
  background: var(--warning-soft);
  color: var(--warning);
}

.not-found__suggestions {
  margin-top: 2rem;
  text-align: start;
  max-width: 32rem;
  margin-inline: auto;
}

.not-found__suggestions-title {
  font-size: var(--step-1);
}

/* ---------- footer ---------- */

.site-footer {
  border-top: 1px solid var(--rule);
  background: var(--surface);
  margin-top: auto;
  padding-block: clamp(1.5rem, 4vw, 2.5rem);
}

.site-footer__inner {
  display: grid;
  gap: 0.75rem;
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.site-footer__title {
  margin: 0;
  font-weight: 650;
  color: var(--ink);
  font-size: var(--step-0);
}

.site-footer__tagline {
  margin: 0.15rem 0 0;
  max-width: 40ch;
}

.site-footer__note {
  margin: 0;
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer__links a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  color: var(--ink-soft);
  text-decoration: none;
}

.site-footer__links a:hover {
  color: var(--accent-ink);
  text-decoration: underline;
}

@media (min-width: 48rem) {
  .site-footer__inner {
    grid-template-columns: 1fr auto;
    align-items: start;
  }

  .site-footer__note {
    grid-column: 1;
  }

  .site-footer__inner nav {
    grid-column: 2;
    grid-row: 1 / span 2;
    justify-self: end;
  }
}

/* ---------- print ---------- */

@media print {
  .site-header,
  .site-footer,
  .comment-form,
  .pagination,
  .related,
  .skip-link {
    display: none;
  }

  body {
    background: #fff;
    color: #000;
  }

  .prose {
    font-size: 11pt;
  }

  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.8em;
    color: #555;
  }
}

/* ---------- forced colors ---------- */

@media (forced-colors: active) {
  .card,
  .comment__inner,
  .field__input,
  .button,
  .pagination__link {
    border: 1px solid ButtonBorder;
  }

  /* Forced colours throw away background and border colour, which would flatten
     a marked field back into an ordinary one. A thick border is what survives. */
  .field__input[aria-invalid="true"],
  .field--invalid .field__input {
    border-width: 2px;
    border-color: Mark;
  }

  .pagination__link.is-current {
    outline: 2px solid Highlight;
  }
}
