@charset "utf-8";
/* ==========================================================================
   Joanot — design system
   --------------------------------------------------------------------------
   One stylesheet, no framework, no build step (task 009's own constraint:
   `catalit build-site` must run from a clean checkout with one command).

   Structure
     1. Fonts
     2. Tokens        — semantic; light, dark and an explicit user choice
     3. Base
     4. Layout
     5. Header
     6. Hero
     7. Search
     8. Collections
     9. Toolbar & filters
    10. Cards & results
    11. Pagination
    12. Nota Joanot
    13. Work page
    14. Author pages
    15. Prose pages
    16. Footer
    17. Dialog, states, utilities
    18. Small-screen overrides

   Contrast: every text/ground pair below was computed against WCAG 2.2
   1.4.3, and every interactive border against 1.4.11, before being written
   here — see `tests/test_site_contrast.py`, which recomputes them from
   this file's own token values so a future edit cannot quietly break AA.
   ========================================================================== */

/* --------------------------------------------------------------- 1. Fonts */
/* Self-hosted, not fetched from a CDN: the site promises no third-party
   requests, and a font served from fonts.gstatic.com is exactly that. Both
   families are SIL OFL 1.1. Subsets are unicode-range gated, so a typical
   Catalan page downloads only the two `latin` files. */
@font-face {
  font-family: "Newsreader"; font-style: normal; font-weight: 400 700;
  font-display: swap; src: url("../fonts/newsreader-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader"; font-style: normal; font-weight: 400 700;
  font-display: swap; src: url("../fonts/newsreader-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader"; font-style: italic; font-weight: 400 600;
  font-display: swap; src: url("../fonts/newsreader-latin-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2212, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "InterVar"; font-style: normal; font-weight: 400 700;
  font-display: swap; src: url("../fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "InterVar"; font-style: normal; font-weight: 400 700;
  font-display: swap; src: url("../fonts/inter-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* -------------------------------------------------------------- 2. Tokens */
:root {
  color-scheme: light;

  /* Ground and ink */
  --paper: #f5f2ea;
  --surface: #fffdf8;
  --sunken: #ebe7db;
  --ink: #14201a;
  --ink-soft: #4f5d54;
  --brand: #17352a;
  --on-brand: #f3efe4;
  --accent: #8c3d1e;
  --accent-quiet: #f0e3da;
  --gold: #7a5a12;

  /* Lines: `--line` is decorative (a card edge, a rule); `--line-ui` is the
     boundary of an actual control and clears 3:1 on every ground here. */
  --line: #ded9cc;
  --line-ui: #7c8779;
  --focus: #a8480f;

  --shadow-sm: 0 1px 2px rgb(20 32 26 / 6%), 0 4px 14px rgb(20 32 26 / 5%);
  --shadow-md: 0 2px 6px rgb(20 32 26 / 7%), 0 18px 40px rgb(20 32 26 / 9%);
  --cover-shadow: 0 2px 4px rgb(20 32 26 / 10%), 0 12px 26px rgb(20 32 26 / 14%);

  /* Type */
  --font-display: "Newsreader", Georgia, "Times New Roman", serif;
  --font-ui: "InterVar", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --step--2: 0.75rem;
  --step--1: 0.8125rem;
  --step-0: 0.9375rem;
  --step-1: 1.0625rem;
  --step-2: clamp(1.125rem, 1.05rem + 0.35vw, 1.3125rem);
  --step-3: clamp(1.375rem, 1.24rem + 0.6vw, 1.75rem);
  --step-4: clamp(1.75rem, 1.5rem + 1.1vw, 2.5rem);
  --step-5: clamp(1.9rem, 1.4rem + 1.7vw, 2.9rem);

  /* Space — 4px scale */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 20px;
  --s6: 24px; --s8: 32px; --s10: 40px; --s12: 48px; --s16: 64px; --s20: 80px;

  --radius-sm: 4px;
  --radius: 8px;
  --radius-lg: 14px;

  --content: 1320px;
  --prose: 68ch;
  --gutter: clamp(16px, 4vw, 48px);
  --header-h: 68px;
  --tap: 44px;
  --control-h: 48px;
  --speed: 180ms;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --paper: #121612;
    --surface: #1b211c;
    --sunken: #161b17;
    --ink: #eae6db;
    --ink-soft: #a6b1a7;
    --brand: #dce6da;
    --on-brand: #12190f;
    --accent: #e9a882;
    --accent-quiet: #2b2420;
    --gold: #d7b872;
    --line: #2c352e;
    --line-ui: #687468;
    --focus: #f0a868;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%), 0 4px 14px rgb(0 0 0 / 26%);
    --shadow-md: 0 2px 6px rgb(0 0 0 / 44%), 0 18px 40px rgb(0 0 0 / 40%);
    --cover-shadow: 0 2px 4px rgb(0 0 0 / 46%), 0 12px 26px rgb(0 0 0 / 44%);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --paper: #121612;
  --surface: #1b211c;
  --sunken: #161b17;
  --ink: #eae6db;
  --ink-soft: #a6b1a7;
  --brand: #dce6da;
  --on-brand: #12190f;
  --accent: #e9a882;
  --accent-quiet: #2b2420;
  --gold: #d7b872;
  --line: #2c352e;
  --line-ui: #687468;
  --focus: #f0a868;
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%), 0 4px 14px rgb(0 0 0 / 26%);
  --shadow-md: 0 2px 6px rgb(0 0 0 / 44%), 0 18px 40px rgb(0 0 0 / 40%);
  --cover-shadow: 0 2px 4px rgb(0 0 0 / 46%), 0 12px 26px rgb(0 0 0 / 44%);
}

/* ---------------------------------------------------------------- 3. Base */
*, *::before, *::after { box-sizing: border-box; }

/* `hidden` outranks any `display` a component class sets. Without this the
   empty state (`.empty{display:grid}`) renders underneath a full page of
   results — found in the browser, not in a test. */
[hidden] { display: none !important; }

/* #163: a control that needs joanot.js is visible from first paint when the
   inline head script has marked `<html class="js">`, so it never pops in
   and shifts the page; without JavaScript it is not shown at all. */
html:not(.js) [data-author-search] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

/* The page's ground, with a tooth to it. Every generated cover and every
   thematic band already carries `feTurbulence` grain, so a perfectly flat
   page between them read as the one surface on the site that was not
   printed on anything. The noise is an inline SVG data URI rather than a
   file: one fewer request, no binary in the repository, and it cannot go
   missing the way `assets/fonts/` once did. `background-attachment: fixed`
   is deliberately *not* used — it forces a repaint on every scroll frame
   for a texture nobody would notice moving. */
body {
  margin: 0;
  background-color: var(--paper);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
  background-repeat: repeat;
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--step-1);
  line-height: 1.55;
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.012em;
  margin: 0;
  text-wrap: balance;
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
p { margin: 0; }

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

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

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

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* A sticky header must never sit on top of the thing that just took focus. */
:target, [id] { scroll-margin-top: calc(var(--header-h) + var(--s6)); }

::selection { background: var(--accent-quiet); color: var(--ink); }

.skip-link {
  position: absolute; left: var(--s4); top: var(--s2); z-index: 100;
  transform: translateY(-200%);
  background: var(--brand); color: var(--on-brand);
  padding: var(--s3) var(--s5); border-radius: var(--radius);
  font-weight: 600; text-decoration: none;
}
.skip-link:focus { transform: translateY(0); }

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

@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; scroll-behavior: auto !important;
  }
}

/* -------------------------------------------------------------- 4. Layout */
.wrap { width: 100%; max-width: var(--content); margin-inline: auto; padding-inline: var(--gutter); }
.prose-wrap { max-width: var(--prose); }

.eyebrow {
  font-size: var(--step--2); font-weight: 600; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--ink-soft); margin: 0;
}

.section { padding-block: var(--s12); }
.section--catalog { padding-block: var(--s5) var(--s16); }
.section--tight { padding-block: var(--s5) var(--s3); }
.section-head { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: var(--s4); margin-bottom: var(--s6); }
.section-head p { color: var(--ink-soft); font-size: var(--step-0); max-width: 56ch; }
.rule { border: 0; border-top: 1px solid var(--line); margin: 0; }
.ornament { display: flex; align-items: center; justify-content: center; gap: var(--s4); }
.ornament::before, .ornament::after { content: ""; height: 1px; flex: 1; background: var(--line); }
.ornament span { font-family: var(--font-display); font-size: var(--step-1); color: var(--accent); letter-spacing: .4em; }

/* Buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: var(--tap); padding: var(--s3) var(--s5);
  border: 1px solid transparent; border-radius: var(--radius);
  font-size: var(--step-0); font-weight: 600; line-height: 1.2;
  text-decoration: none; cursor: pointer; background: none;
  transition: background var(--speed), border-color var(--speed), color var(--speed);
}
.btn--primary { background: var(--brand); color: var(--on-brand); }
.btn--primary:hover { background: color-mix(in oklab, var(--brand) 88%, var(--ink)); }
.btn--ghost { border-color: var(--line-ui); color: var(--ink); background: var(--surface); }
.btn--ghost:hover { border-color: var(--ink-soft); background: var(--sunken); }
.btn--quiet { color: var(--ink-soft); padding-inline: var(--s3); }
.btn--quiet:hover { color: var(--ink); background: var(--sunken); }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn .icon { flex: none; }

.icon { width: 1.15em; height: 1.15em; display: inline-block; vertical-align: -0.16em; }

.text-link {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: 24px;
  font-weight: 600; font-size: var(--step-0); color: var(--accent);
  text-decoration: underline; text-underline-offset: 0.24em;
}
.text-link .icon { transition: transform var(--speed); }
.text-link:hover .icon { transform: translateX(3px); }

/* -------------------------------------------------------------- 5. Header */
.header {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.header__row {
  display: flex; align-items: center; gap: var(--s5);
  min-height: var(--header-h);
  /* #271: at 320px, and at 200% text size, the tools used to run past the
     viewport. Wrapping beats overflowing: the tools drop to a second line. */
  flex-wrap: wrap; row-gap: 0;
}
.brand { display: inline-flex; align-items: baseline; gap: var(--s3); text-decoration: none; color: var(--ink); }
.brand__name {
  font-family: var(--font-display); font-weight: 600; font-size: 1.6rem;
  letter-spacing: -0.02em; line-height: 1;
}
.brand__slogan {
  font-family: var(--font-display); font-style: italic; font-size: var(--step-0);
  color: var(--ink-soft);
}
.header nav { margin-inline-start: auto; }
.header__nav { display: flex; align-items: center; gap: var(--s1); list-style: none; margin: 0; padding: 0; }
.header__nav a {
  display: inline-flex; align-items: center; min-height: var(--tap);
  padding: 0 var(--s3); border-radius: var(--radius);
  color: var(--ink); text-decoration: none; font-size: var(--step-0); font-weight: 500;
}
.header__nav a:hover { background: var(--sunken); }
.header__nav a[aria-current="page"] { color: var(--accent); text-decoration: underline; text-underline-offset: 0.3em; }
.header__tools { display: flex; align-items: center; gap: var(--s2); }
.icon-btn {
  display: inline-grid; place-items: center;
  width: var(--tap); height: var(--tap);
  border: 1px solid transparent; border-radius: var(--radius);
  background: transparent; color: var(--ink); cursor: pointer;
}
.icon-btn:hover { background: var(--sunken); }
.icon-btn .icon { width: 20px; height: 20px; }
/* #167: the header theme switch shows what a click will give — a moon in
   light mode, a sun in dark mode, following the system when no choice is
   stored. */
.header__theme-btn .icon--sun { display: none; }
:root[data-theme="dark"] .header__theme-btn .icon--moon { display: none; }
:root[data-theme="dark"] .header__theme-btn .icon--sun { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .header__theme-btn .icon--moon { display: none; }
  :root:not([data-theme="light"]) .header__theme-btn .icon--sun { display: block; }
}

@media (max-width: 1080px) { .brand__slogan { font-size: var(--step--1); } }
/* #201: on a 360px phone the slogan broke onto a second line and made the
   header two rows tall. One line, always. */
.brand__slogan { white-space: nowrap; }
/* #271: on a 320px phone the slogan plus the two icon buttons are wider than the
   screen; the name alone carries the brand there. */
@media (max-width: 420px) { .brand__slogan { display: none; } }
.header__nav { flex-wrap: wrap; }
.header__menu-btn { display: none; }
.menu-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s1); }
.menu-list a {
  display: flex; align-items: center; min-height: 52px; padding: 0 var(--s3);
  border-radius: var(--radius); font-family: var(--font-display);
  font-size: var(--step-2); color: var(--ink); text-decoration: none;
}
.menu-list a:hover { background: var(--sunken); }
.menu-list a[aria-current="page"] { color: var(--accent); }
.appearance { border: 0; border-top: 1px solid var(--line); margin: var(--s5) 0 0; padding: var(--s4) 0 0; display: grid; gap: var(--s2); }
.appearance label { min-height: 44px; display: flex; align-items: center; gap: var(--s2); }

@media (max-width: 900px) {
  :root { --header-h: 60px; }
  .header__nav { display: none; }
  .header nav { margin-inline-start: auto; }
  .header__menu-btn { display: inline-grid; }
  .header__theme-btn { display: none; }
  .searchbar__hint { display: none; }
}

/* ---------------------------------------------------------------- 6. Hero */
/* #32, selected review variant A: the illustration is the upper-page
   surface, not a small adjacent ornament. A controlled scrim is what lets
   it carry real content without asking the artwork to be a flat backdrop. */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  border-bottom: 1px solid var(--line); color: #f7f3e9; background: #16241d;
}
.hero::before {
  content: ""; position: absolute; z-index: -2; inset: 0;
  background: url("../hero-joanot-v1.jpg") center 42% / cover no-repeat;
}
.hero::after {
  content: ""; position: absolute; z-index: -1; inset: 0;
  background: linear-gradient(90deg, rgb(12 24 17 / 92%) 0%, rgb(12 24 17 / 68%) 48%, rgb(12 24 17 / 15%) 100%);
}
.hero__grid {
  display: grid; gap: var(--s8);
  grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); align-items: center;
  padding-block: var(--s6) var(--s4);
}
/* #201: no fixed height. The hero is as tall as its title, lede and search
   field and no taller, so the first row of books is on the first screen. */
.hero__title { font-size: var(--step-5); letter-spacing: -0.025em; max-width: 26ch; overflow-wrap: anywhere; }
.hero__title em { display: block; }
.hero__title em { font-style: italic; color: #f2c96d; }
.hero .eyebrow { color: #d8dfd4; }
.hero__lede {
  font-family: var(--font-display); font-size: var(--step-1);
  color: #d8dfd4; max-width: 46ch; margin-top: var(--s3); overflow-wrap: anywhere;
}
.hero__art { display: none; }
.hero__stats {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s6);
  margin-top: var(--s5); padding: 0; list-style: none;
}
.hero__stats div { display: flex; flex-direction: column; }
.hero__stats dt { order: 2; font-size: var(--step--1); color: #d8dfd4; }
.hero__stats dd {
  order: 1; margin: 0; font-family: var(--font-display);
  font-size: var(--step-2); font-weight: 600; line-height: 1;
}

@media (max-width: 860px) {
  /* Text sits in the upper half on a phone. The earlier bottom-heavy scrim
     made the most important words depend on the lightest part of the art. */
  .hero::after {
    background: linear-gradient(180deg, rgb(12 24 17 / 91%) 0%, rgb(12 24 17 / 73%) 56%, rgb(12 24 17 / 88%) 100%);
  }
  .hero__grid { grid-template-columns: minmax(0, 1fr); gap: var(--s3); padding-block: var(--s5) var(--s4); }
  .hero__title { max-width: none; }
}


/* -------------------------------------------------------------- 7. Search */
.searchbar { padding-bottom: var(--s5); }
.searchbar__form { display: flex; gap: var(--s3); align-items: stretch; flex-wrap: wrap; }
.searchbar__field { position: relative; flex: 1 1 340px; display: flex; align-items: center; }
.searchbar__field .icon {
  position: absolute; inset-inline-start: var(--s4); width: 20px; height: 20px;
  color: var(--ink-soft); pointer-events: none;
}
.searchbar input[type="search"] {
  width: 100%; min-height: 52px;
  padding: var(--s3) var(--s5) var(--s3) calc(var(--s4) * 2 + 20px);
  border: 1px solid var(--line-ui); border-radius: var(--radius);
  background: var(--surface); color: var(--ink); font-size: var(--step-1);
  -webkit-appearance: none; appearance: none;
}
.searchbar input[type="search"]::placeholder { color: var(--ink-soft); opacity: 1; }
.searchbar input[type="search"]::-webkit-search-cancel-button { cursor: pointer; }
.searchbar .btn { min-height: 52px; padding-inline: var(--s8); flex: 0 0 auto; }
.searchbar__hint { margin-top: var(--s2); font-size: var(--step--2); color: var(--ink-soft); }
/* #163: the author search's live count ("12 autors trobats") keeps its line
   even while empty, so the first keystroke does not push the list down. */
.author-search .searchbar__hint:empty::before { content: "\00a0"; }
/* #159: author filters. Rendered at build time, so they hold their place
   from first paint; hidden with the search when JavaScript is off. */
html:not(.js) [data-author-filters] { display: none !important; }
.author-filters {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--s3) var(--s6);
  padding-bottom: var(--s5); font-size: var(--step--1);
}
.author-filters__group { margin: 0; padding: 0; border: 0; min-width: 0; }
.author-filters fieldset.author-filters__group {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
}
.author-filters legend {
  float: left; margin-inline-end: var(--s2); padding: 0;
  font-size: var(--step--1); color: var(--ink-soft);
}
.author-filters__option {
  display: inline-flex; align-items: center; gap: var(--s1); min-height: var(--tap);
  padding: 0 var(--s3); border: 1px solid var(--line-ui); border-radius: 999px;
  background: var(--surface); cursor: pointer;
}
.author-filters__option:has(input:checked) {
  background: var(--accent-quiet); border-color: var(--accent); color: var(--accent);
}
.author-filters__option input { margin: 0; accent-color: var(--accent); }
.author-filters__option:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; }
.author-filters__check {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: var(--tap); cursor: pointer;
}
.author-filters__check input { accent-color: var(--accent); }
.author-filters__count { color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.author-filters__hint { flex-basis: 100%; margin: 0; font-size: var(--step--2); color: var(--ink-soft); }
.searchbar__hint kbd {
  font-family: var(--font-ui); font-size: 0.85em; border: 1px solid var(--line-ui);
  border-radius: var(--radius-sm); padding: 1px 5px; background: var(--surface);
}

/* --------------------------------------------------------- 8. Collections */
.rail { position: relative; overflow-x: clip; }
.rail__track {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(max-content, 1fr);
  gap: var(--s3); overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: var(--s3); margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter); scrollbar-width: thin;
  /* Snap to the content edge, not the padding edge: without it the first
     card snapped flush against the viewport on a phone (#201). */
  scroll-padding-inline: var(--gutter);
}
.rail__track > * { scroll-snap-align: start; }

/* Gallery controls (#206). The markup ships them `hidden`; joanot.js
   unhides them only when the track actually overflows, so with no JS — or
   with every card fitting — the rail is exactly what it was. Once paged,
   the dots replace the scrollbar as the "there is more" signal. */
.rail__frame { position: relative; }
.rail.is-paged .rail__track { scrollbar-width: none; padding-bottom: 0; }
.rail.is-paged .rail__track::-webkit-scrollbar { display: none; }
.rail.has-before .rail__track {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 calc(var(--gutter) + 28px));
  mask-image: linear-gradient(90deg, transparent 0, #000 calc(var(--gutter) + 28px));
}
.rail.has-after .rail__track {
  -webkit-mask-image: linear-gradient(270deg, transparent 0, #000 calc(var(--gutter) + 28px));
  mask-image: linear-gradient(270deg, transparent 0, #000 calc(var(--gutter) + 28px));
}
.rail.has-before.has-after .rail__track {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 calc(var(--gutter) + 28px),
    #000 calc(100% - var(--gutter) - 28px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 calc(var(--gutter) + 28px),
    #000 calc(100% - var(--gutter) - 28px), transparent 100%);
}
/* Arrows only where there is a mouse to aim them; a finger swipes. They
   sit in the gutter, centred on it, and never further out than the
   viewport edge on a narrow desktop window. */
.rail__arrow { display: none; }
@media (hover: hover) and (pointer: fine) {
  .rail__arrow:not([hidden]) {
    display: grid; place-items: center; position: absolute; top: 50%; z-index: 1;
    width: var(--tap); height: var(--tap); margin-top: calc(var(--tap) / -2);
    padding: 0; border: 1px solid var(--line-ui); border-radius: 999px;
    background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm);
    cursor: pointer; transition: opacity var(--speed), background var(--speed);
  }
  .rail__arrow:hover { background: var(--sunken); }
  .rail__arrow--prev { left: max(calc(var(--gutter) / -2 - var(--tap) / 2), calc(var(--gutter) * -1)); }
  .rail__arrow--next { right: max(calc(var(--gutter) / -2 - var(--tap) / 2), calc(var(--gutter) * -1)); }
  .rail__arrow--prev .icon { transform: rotate(90deg); }
  .rail__arrow--next .icon { transform: rotate(-90deg); }
  .rail__arrow:disabled { opacity: 0; visibility: hidden; }
}
.rail__arrow:focus-visible, .rail__dot:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.rail__dots:not([hidden]) {
  display: flex; justify-content: center; margin-top: calc(var(--s1) * -1);
}
.rail__dot {
  display: grid; place-items: center; width: var(--tap); height: var(--tap);
  padding: 0; border: 0; background: none; cursor: pointer;
}
.rail__dot::before {
  content: ""; width: 8px; height: 8px; border-radius: 999px;
  background: var(--line-ui); transition: width var(--speed), background var(--speed);
}
.rail__dot:hover::before { background: var(--ink-soft); }
.rail__dot[aria-current="true"]::before { width: 22px; background: var(--accent); }
.collection {
  display: grid; grid-template-columns: auto 1fr; align-items: center;
  align-content: center; min-height: 58px;
  gap: 2px var(--s3); padding: var(--s3) var(--s4);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); text-decoration: none; color: var(--ink);
  transition: background var(--speed), border-color var(--speed);
}
.collection:hover { border-color: var(--ink-soft); background: var(--sunken); }
.collection__text { display: grid; gap: 2px; min-width: 0; }
.collection__count {
  font-family: var(--font-display); font-size: var(--step-1);
  font-weight: 600; line-height: 1;
}
.collection__label { font-size: var(--step--1); color: var(--ink-soft); }
.collection__mark { color: var(--accent); display: grid; place-items: center;
  width: 28px; height: 28px; border-radius: 999px; background: var(--accent-quiet); }
.collection__mark .icon { width: 15px; height: 15px; }
.collection[data-kind="award"] .collection__mark { color: var(--gold); }

/* The art variant is one compact, clickable surface: count over label,
   no taller than those two lines (#201). The illustration shows through a
   veil of the page's own paper colour rather than under a dark scrim, so
   the rail sits quietly on the page in both schemes instead of reading as
   a row of dark blocks. The veil never drops below 72%: `ink` keeps
   ≥ 5:1 over a pure black or white pixel in either scheme
   (`tests/test_site_contrast.py`). */
.collection--art {
  position: relative; isolation: isolate; display: flex; align-items: center;
  min-width: 132px; padding: 0; overflow: hidden; border-color: var(--line);
  background: var(--paper); color: var(--ink);
}
.collection--art .collection__art {
  position: absolute; z-index: -2; inset: 0; display: block; aspect-ratio: auto;
  overflow: hidden; background: var(--sunken);
}
.collection--art .collection__art img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: grayscale(.2);
}
.collection--art::after {
  content: ""; position: absolute; z-index: -1; inset: 0;
  background: linear-gradient(90deg, color-mix(in srgb, var(--paper) 88%, transparent) 0%, color-mix(in srgb, var(--paper) 72%, transparent) 100%);
}
.collection--art .collection__text { position: relative; padding: var(--s3); }
.collection--art .collection__count { color: var(--ink); }
.collection--art .collection__label { color: var(--ink); font-weight: 500;
  white-space: nowrap; }
.collection--art:hover { border-color: var(--ink-soft); background: var(--paper); }

/* ------------------------------------------------- 8b. Thematic bands */
/* The band is a full-bleed strip *above* a heading, never a backdrop with
   type on top: text over a generated image cannot be guaranteed to pass
   contrast in both colour schemes, and `scripts/site_check.py` measures
   it. See `render.band_header`. */
.band {
  margin: 0 0 var(--s6); height: clamp(120px, 19vw, 200px);
  overflow: hidden; background: var(--sunken);
  border-bottom: 1px solid var(--line);
}
.band img { width: 100%; height: 100%; object-fit: cover; display: block; }
.page-head--banded { padding-top: var(--s6); }
.page-head__actions { margin-top: var(--s4); }

/* ------------------------------------------------- 8c. The hero band */
/* The heading over the artwork, with a scrim under the text. The scrim is
   the only reason this is safe: its left end is the page's own `--paper`
   at 94%, so the type sits on nearly the ground it sat on when the band
   was merely a strip above it, and `scripts/site_check.py`'s contrast
   measurement barely moves. Take the scrim away and the heading is type
   over an arbitrary generated image, which cannot be guaranteed to pass
   in either colour scheme.

   `--paper` is a token that changes with the scheme, so one gradient
   serves light and dark without a second hard-coded copy. */
.hero-band {
  position: relative; isolation: isolate;
  min-height: clamp(220px, 24vw, 310px);
  display: grid; align-items: end;
  background: var(--sunken);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}
.hero-band__art {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center right; z-index: -2;
}
.hero-band__scrim {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(
    100deg,
    color-mix(in srgb, var(--paper) 94%, transparent) 0%,
    color-mix(in srgb, var(--paper) 90%, transparent) 38%,
    color-mix(in srgb, var(--paper) 55%, transparent) 62%,
    color-mix(in srgb, var(--paper) 0%, transparent) 88%);
}
.hero-band__inner { padding-block: var(--s10) var(--s8); }
.hero-band__text { max-width: 46ch; }
.hero-band__text h1 { font-size: var(--step-5); }
.hero-band__text > p:not(.eyebrow) {
  font-family: var(--font-display); font-size: var(--step-2);
  color: var(--ink-soft); max-width: 40ch; margin-top: var(--s4);
}
.hero-band__text .page-head__actions { margin-top: var(--s5); }

/* A band keeps its own palette in dark mode — it is artwork, like a cover,
   and the covers below it stay light too, which reads as objects on a dark
   page rather than as a theme that failed to apply. But a *full-bleed*
   band is a hundred times the area of a cover, and at full brightness one
   sage-coloured strip lit up the whole viewport. Damped, not recoloured:
   the identity survives and the glare does not. The rail thumbnails and
   the inline filter band are small enough to leave alone. */
:root[data-theme="dark"] .band > img,
:root:not([data-theme="light"]) .band > img { filter: brightness(0.82) saturate(0.92); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .band > img { filter: none; }
}
/* The hero's artwork needs more damping than a strip did, because the type
   now sits on it: an ivory-ground ink drawing at full brightness lights up
   a dark page and fights the scrim it is meant to sit behind. */
:root[data-theme="dark"] .hero-band__art,
:root:not([data-theme="light"]) .hero-band__art { filter: brightness(0.55) saturate(0.85); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .hero-band__art { filter: none; }
}

/* A band swapped in on a filtered catalogue view. Narrower than a shelf
   page's, because here it is an annotation on a list the reader is already
   looking at rather than the head of a page. */
.band--inline {
  height: clamp(84px, 11vw, 132px); border: 1px solid var(--line);
  border-radius: var(--radius-lg); margin-bottom: var(--s5);
}
.chips--links .chip { text-decoration: none; }
.chip__count {
  margin-left: var(--s2); font-variant-numeric: tabular-nums;
  color: var(--ink-soft); font-size: var(--step--2);
}

/* --------------------------------------------------- 9. Toolbar & filters */
.origin-switch {
  display: inline-flex; padding: 3px; gap: 2px; margin-bottom: var(--s4);
  border: 1px solid var(--line-ui); border-radius: 999px; background: var(--surface);
}
.origin-switch a {
  display: inline-flex; align-items: baseline; gap: var(--s2);
  min-height: 38px; padding: 0 var(--s5); border-radius: 999px;
  font-size: var(--step-0); font-weight: 600; color: var(--ink);
  text-decoration: none; white-space: nowrap;
}
.origin-switch a:hover { background: var(--sunken); }
.origin-switch a[aria-current="page"] { background: var(--brand); color: var(--on-brand); }
.origin-switch .count { font-size: var(--step--1); font-weight: 500; opacity: .75; font-variant-numeric: tabular-nums; }
@media (max-width: 560px) {
  .origin-switch { display: flex; width: 100%; }
  .origin-switch a {
    flex: 1 1 0; min-width: 0; justify-content: center; padding-inline: var(--s2);
    flex-direction: column; align-items: center; gap: 0;
    white-space: normal; text-align: center; font-size: var(--step--1); line-height: 1.25;
  }
  .origin-switch .count { font-size: var(--step--2); }
}

.catalog-head {
  display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between;
  gap: var(--s4); margin-bottom: var(--s4);
}
.catalog-head__title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s3); }
.catalog-head__title h2 { font-size: var(--step-3); }
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); }
.toolbar__group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.toolbar__group--end { margin-inline-start: auto; }

.select {
  display: inline-flex; align-items: center; min-height: var(--tap);
  border: 1px solid var(--line-ui); border-radius: var(--radius); background: var(--surface);
}
.select select {
  min-height: var(--tap); padding: 0 var(--s8) 0 var(--s4);
  border: 0; background: transparent; font-size: var(--step-0); cursor: pointer;
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234f5d54' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s3) center; background-size: 16px;
}
.select label { padding-inline-start: var(--s4); font-size: var(--step--1); color: var(--ink-soft); }
.select label + select { padding-inline-start: var(--s2); }

.viewtoggle { display: inline-flex; border: 1px solid var(--line-ui); border-radius: var(--radius); overflow: hidden; }
.viewtoggle button {
  display: grid; place-items: center; width: var(--tap); height: var(--tap);
  border: 0; background: var(--surface); color: var(--ink-soft); cursor: pointer;
}
.viewtoggle button + button { border-inline-start: 1px solid var(--line-ui); }
.viewtoggle button[aria-pressed="true"] { background: var(--brand); color: var(--on-brand); }

.filter-btn { position: relative; }
.filter-btn__short { display: none; }
.filter-btn__badge {
  display: inline-grid; place-items: center; min-width: 20px; height: 20px;
  padding-inline: 5px; border-radius: 999px;
  background: var(--accent); color: var(--surface);
  font-size: var(--step--2); font-weight: 700; font-variant-numeric: tabular-nums;
}
:root[data-theme="dark"] .filter-btn__badge { color: #1b1310; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .filter-btn__badge { color: #1b1310; }
}

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); list-style: none; margin: 0; padding: 0; }
.chip {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 32px; padding: var(--s1) var(--s3);
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--sunken); color: var(--ink); font-size: var(--step--1);
}
.chip--removable { padding-inline-end: var(--s1); }
.chip__remove {
  display: grid; place-items: center; width: 28px; height: 28px;
  border: 0; border-radius: 999px; background: none; color: var(--ink-soft); cursor: pointer;
}
.chip__remove:hover { background: var(--accent-quiet); color: var(--accent); }
.chip__remove .icon { width: 14px; height: 14px; }
.chip--award { border-color: color-mix(in srgb, var(--gold) 45%, transparent); color: var(--gold); background: transparent; }

.results-count { font-size: var(--step-0); color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.results-count strong { color: var(--ink); font-weight: 600; }

/* Filter form (inside the dialog on small screens, inline on wide ones) */
.filter-form { display: grid; gap: var(--s6); }
.filter-form fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.filter-form legend {
  font-size: var(--step--2); font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--ink-soft); padding: 0; margin-bottom: var(--s3);
}
.optionlist { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.option {
  display: flex; align-items: center; gap: var(--s3);
  min-height: var(--tap); padding: var(--s1) var(--s2);
  border-radius: var(--radius); cursor: pointer; font-size: var(--step-0);
}
.option:hover { background: var(--sunken); }
.option input { width: 18px; height: 18px; accent-color: var(--brand); flex: none; }
.option__label { flex: 1; }
.option__count { color: var(--ink-soft); font-size: var(--step--1); font-variant-numeric: tabular-nums; }
/* Task 163 (#146): «Any de publicació», from/to. */
.year-range__inputs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
.year-range__field { display: grid; gap: var(--s1); font-size: var(--step--1); color: var(--ink-soft); }
.year-range__field input {
  min-height: var(--tap); padding: var(--s1) var(--s3); width: 100%;
  border: 1px solid var(--line-ui); border-radius: var(--radius);
  background: var(--surface); color: var(--ink); font: inherit; font-size: var(--step-0);
  font-variant-numeric: tabular-nums;
}
.year-range__field input:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.year-range__hint { margin: var(--s2) 0 0; font-size: var(--step--2); color: var(--ink-soft); }

/* --------------------------------------------------- 10. Cards & results */
.results { list-style: none; margin: 0; padding: 0; }
.results--grid {
  display: grid; gap: var(--s6) var(--s5);
  grid-template-columns: repeat(auto-fill, minmax(min(158px, 100%), 1fr));
}
@media (max-width: 560px) { .results--grid { gap: var(--s5) var(--s4); grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.card { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.card__cover {
  position: relative; display: block; aspect-ratio: 2 / 3; border-radius: var(--radius-sm);
  overflow: hidden; background: var(--sunken); box-shadow: var(--cover-shadow);
  transition: transform var(--speed), box-shadow var(--speed);
}
.card__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card__origin-badge {
  position: absolute; inset: auto var(--s2) var(--s2) auto; padding: 3px 6px;
  border-radius: 999px; background: rgb(15 23 19 / 84%); color: #fff;
  font-size: 0.625rem; font-weight: 650; line-height: 1.15; letter-spacing: .01em;
}
.card:hover .card__cover { transform: translateY(-3px); box-shadow: var(--shadow-md), var(--cover-shadow); }
.card__title { font-size: var(--step-0); line-height: 1.3; }
.card__title a { color: var(--ink); text-decoration: none; }
.card__title a:hover { text-decoration: underline; text-underline-offset: 0.2em; text-decoration-color: var(--accent); }
/* The whole card is clickable, but the anchor stays the only interactive
   element — no click handler swallowing the card, per the handover's rule. */
.card__title a::after { content: ""; position: absolute; inset: 0; }
.card { position: relative; }
.card__byline { font-size: var(--step--1); color: var(--ink-soft); }
.card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); font-size: var(--step--2); color: var(--ink-soft); }
.card__meta .dot { opacity: 0.5; }
.card__origin { font-size: var(--step--2); color: var(--ink-soft); }
.card__foot { display: flex; align-items: baseline; gap: var(--s2); margin-top: auto; padding-top: var(--s1); }

/* #93/task 093: pending-origin card whose generated cover is not published
   yet (restored from before #72, until the full rebuild). */
.card__cover--pending {
  background: linear-gradient(145deg, #28362f, #697466 52%, #b89d68);
}
.card__cover--pending::before {
  content: "JOANOT"; position: absolute; inset: auto var(--s4) var(--s4);
  color: #f7f3e9; font-family: var(--font-display); font-size: var(--step-2);
  letter-spacing: .08em;
}
/* Worker-rendered cards have no cover/body wrapper.  Make their three text
   rows participate in the parent grid, so a long title aligns every byline
   and origin in that visual row without clipping the title. */
.results--grid .card--worker {
  display: grid; grid-row: span 4; grid-template-rows: subgrid;
  /* Let the inherited word wrapping work instead of growing an implicit
     min-content column beyond the card at large text sizes (#308). */
  grid-template-columns: minmax(0, 1fr);
}
.results--list .card--worker {
  display: grid; grid-template-columns: 64px minmax(0, 1fr); column-gap: var(--s4);
}
.results--list .card--worker .card__cover { grid-row: 1 / span 3; width: 64px; }
.results--list .card--worker > :not(.card__cover) { grid-column: 2; }
@media (min-width: 700px) {
  .results--list .card--worker { grid-template-columns: 80px minmax(0, 1fr); column-gap: var(--s5); }
  .results--list .card--worker .card__cover { width: 80px; }
}

.awardmark { display: inline-flex; align-items: center; gap: 4px; color: var(--gold); font-size: var(--step--1); font-weight: 600; }
.awardmark .icon { width: 15px; height: 15px; }

/* List view: cover left, text right — the mobile default. */
.results--list { display: grid; gap: 0; }
.results--list .card {
  flex-direction: row; align-items: start; gap: var(--s4);
  padding-block: var(--s4); border-bottom: 1px solid var(--line);
}
.results--list .card--compact { display: grid; grid-template-columns: minmax(0, 1fr); }
.results--list .card__cover { flex: 0 0 64px; width: 64px; }
/* The list row already says «Original en català» in words, and on a 64px
   thumbnail the pill wrapped to three lines over the cover (#201). */
.results--list .card__origin-badge { display: none; }
.results--list .card__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--s1); }
.results--list .card__foot { margin-top: var(--s2); padding-top: 0; }
@media (min-width: 700px) {
  .results--list .card { gap: var(--s5); padding-block: var(--s5); }
  .results--list .card__cover { flex-basis: 80px; width: 80px; }
  .results--list .card__body { flex-direction: row; align-items: center; gap: var(--s6); }
  .results--list .card__main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .results--list .card__foot { margin: 0; flex: 0 0 auto; }
}

/* ---------------------------------------------------------- 11. Pagination */
.pagination { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--s4); margin-top: var(--s10); }
/* #313: at 320-375 px with 200% text each button is ~190 px; wrap, never overflow. */
.pagination .btn { max-width: 100%; white-space: normal; }
.pagination__status { font-size: var(--step-0); color: var(--ink-soft); font-variant-numeric: tabular-nums; }
/* #74: a crawlable, always-present way into the static alphabetical lists;
   the JS catalogue only links to them as a fallback. */
.catalogue-browse { margin-top: var(--s6); text-align: center; font-size: var(--step--1); color: var(--ink-soft); }

/* -------------------------------------------------------- 12. Nota Joanot */
.nota { display: inline-flex; align-items: baseline; gap: var(--s1); font-family: var(--font-display); }
.nota__value { font-size: var(--step-2); font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.nota__scale { font-size: var(--step--1); color: var(--ink-soft); font-family: var(--font-ui); }
.nota--none { font-family: var(--font-ui); font-size: var(--step--1); color: var(--ink-soft); }

.nota-panel {
  display: grid; grid-template-columns: auto 1fr; gap: var(--s5) var(--s6);
  align-items: center; padding: var(--s6);
  background: var(--sunken); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.nota-panel__figure { text-align: center; padding-inline-end: var(--s6); border-inline-end: 1px solid var(--line); }

/* The arc sits *above* the number, not behind it. Overlapping them with a
   negative margin looked tighter and cannot be made safe: the value's
   font-size is a `clamp()`, so it grows with the viewport while any fixed
   offset stays put, and at some width the arc always ends up drawn through
   the digits. It did, at 1440. */
.gauge { display: block; width: 108px; height: 58px; margin: 0 auto var(--s2);
  color: var(--ink); }

/* Where a work sits in the span the catalogue actually covers. */
.span { margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--line); }
.span__label { font-size: var(--step--2); letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: var(--s3); }
.span__track { position: relative; height: 3px; border-radius: 999px; background: var(--line-ui); }
.span__mark { position: absolute; top: 50%; width: 9px; height: 9px; border-radius: 999px;
  background: var(--accent); transform: translate(-50%, -50%); }
.span__ends { display: flex; justify-content: space-between; margin-top: var(--s2);
  font-size: var(--step--2); color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.nota-panel__value { font-family: var(--font-display); font-size: clamp(2.5rem, 2rem + 2vw, 3.4rem); font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.nota-panel__scale { font-size: var(--step--1); color: var(--ink-soft); margin-top: var(--s1); }
.nota-panel__name { font-size: var(--step--2); font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-soft); margin-top: var(--s2); }
.nota-panel__body { display: grid; gap: var(--s3); }
.nota-panel__claim { font-family: var(--font-display); font-size: var(--step-2); font-style: italic; }
.nota-panel__note { font-size: var(--step-0); color: var(--ink-soft); }
.nota-panel--empty {
  grid-template-columns: auto 1fr; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); background: transparent;
  border-style: dashed; border-color: var(--line-ui); border-radius: var(--radius);
}
.nota-panel--empty .nota-panel__body { gap: 2px; }
.nota-panel--empty .nota-panel__claim {
  font-family: var(--font-ui); font-size: var(--step-0); font-style: normal; font-weight: 600;
}
.nota-panel--empty .nota-panel__note { font-size: var(--step--1); }
.nota-panel--empty > .icon { width: 22px; height: 22px; color: var(--ink-soft); }

.meter { display: grid; gap: var(--s2); max-width: 340px; }
.meter__head { display: flex; justify-content: space-between; gap: var(--s3); font-size: var(--step--1); }
.meter__label { color: var(--ink-soft); }
.meter__value { font-weight: 600; font-variant-numeric: tabular-nums; }
.meter__track { height: 8px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 12%, transparent); overflow: hidden; }
.meter__fill { height: 100%; border-radius: 999px; background: var(--brand); }

@media (max-width: 620px) {
  .nota-panel { grid-template-columns: 1fr; gap: var(--s4); }
  .nota-panel__figure { text-align: start; padding: 0 0 var(--s4); border-inline-end: 0; border-bottom: 1px solid var(--line); }
}

/* ------------------------------------------------------------ 13. Work page */
.breadcrumb { font-size: var(--step--1); color: var(--ink-soft); padding-block: var(--s5) 0; }
.breadcrumb ol { display: flex; flex-wrap: wrap; gap: var(--s2); list-style: none; margin: 0; padding: 0; }
.breadcrumb li + li::before { content: "/"; margin-inline-end: var(--s2); opacity: 0.6; }
.breadcrumb a { color: var(--ink-soft); }

.work__nav { padding-block: var(--s4) 0; }
.work__back { color: var(--ink-soft); text-decoration: none; font-size: var(--step--1); }
.work__back:hover { color: var(--accent); text-decoration: underline; }
.work__head { display: grid; grid-template-columns: minmax(140px, 210px) minmax(0, 1fr); gap: var(--s8); padding-block: var(--s5) var(--s10); align-items: start; }
.work__intro { min-width: 0; }
.work__title { font-size: clamp(1.9rem, 1.5rem + 1.8vw, 2.75rem); line-height: 1.12; max-width: 21ch; }
.work__byline { font-family: var(--font-display); font-size: var(--step-1); color: var(--ink-soft); margin-top: var(--s2); }
.work__byline a { color: inherit; display: inline-block; padding-block: 2px; }
.work__meta { margin-top: var(--s2); color: var(--ink-soft); font-size: var(--step--1); }
.work__synopsis-short { margin-top: var(--s5); max-width: 62ch; font-family: var(--font-display); font-size: var(--step-1); line-height: 1.5; }
.work__more { display: inline-block; margin-top: var(--s1); padding: var(--s2) 0; min-height: var(--tap);
  border: 0; background: none; font: inherit; font-size: var(--step--1); color: var(--accent); cursor: pointer;
  text-decoration: underline; text-underline-offset: 0.2em; }
.work__more[hidden] { display: none; }
.work__synopsis.is-clamped .work__synopsis-short { display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 6; line-clamp: 6; overflow: hidden; }
.work__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-top: var(--s5); }
.work__share.btn { min-height: var(--tap); padding-inline: var(--s2); border: 0;
  background: transparent; color: var(--ink-soft); box-shadow: none; font-size: var(--step--1); }
.work__share.btn:hover { color: var(--accent); text-decoration: underline; }
.work__cover { border-radius: var(--radius); overflow: hidden; box-shadow: var(--cover-shadow); background: var(--sunken); }
.work__cover img { display: block; width: 100%; }
.work__covernote { font-size: 0.6875rem; line-height: 1.4; color: var(--ink-soft); margin-top: var(--s2); opacity: .75; }
.work__nota { margin-top: var(--s5); }
.work__nota .nota-panel { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s3); padding: 0; border: 0; background: transparent; }
.work__nota .nota-panel__value { font-size: clamp(1.8rem, 1.4rem + 1.2vw, 2.25rem); font-weight: 700; }
.work__nota .nota-panel__scale { font-family: var(--font-ui); font-weight: 400; font-size: var(--step--1); }
.work__nota .nota-panel__name { margin: 0; font-size: var(--step--1); letter-spacing: 0; text-transform: none; color: var(--ink); }
.work__nota .talk { flex-basis: 100%; }
.work__nota .nota-panel--empty { font-size: var(--step--1); color: var(--ink-soft); }
.work__method { display: inline-block; margin-top: var(--s1); padding-block: var(--s2); font-size: var(--step--1); color: var(--accent); }
.work__method[hidden] { display: none; }

.work__body { display: grid; gap: var(--s12); padding-bottom: var(--s16); }
.work__body > section, .work__col > section { max-width: 840px; }
/* #271: the columns hold sections; one column on small screens, two from 1100px. */
.work__col { display: grid; gap: var(--s12); align-content: start; min-width: 0; }
@media (min-width: 1100px) {
  .work__body { grid-template-columns: minmax(0, 1fr) minmax(300px, 400px); column-gap: var(--s16); align-items: start; }
  .work__col > section { max-width: none; }
  .work__col--main { grid-column: 1; }
  .work__col--aside { grid-column: 2; }
  /* only one column present: keep the reading width instead of stretching */
  .work__body > .work__col:only-of-type { grid-column: 1; max-width: 840px; }
  .work__body > .work__related { grid-column: 1 / -1; max-width: none; }
  .work__col--aside .facet { grid-template-columns: 1fr; gap: var(--s2); }
  .work__col--aside .section-title { font-size: var(--step-2); }
}
.editions__find { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); margin: calc(-1 * var(--s2)) 0 var(--s3); font-size: var(--step--1); }
.edition__find { color: var(--ink-soft); }
.edition__find .dot { margin-inline: var(--s1); opacity: .5; }
.work__edition-empty { color: var(--ink-soft); }
.section-title__count { color: var(--ink-soft); font-family: var(--font-ui); font-size: var(--step-0); font-weight: 400; }

@media (max-width: 640px) {
  body:has(.work__head) .brand__slogan { display: none; }
  .work__head { grid-template-columns: 104px minmax(0, 1fr); gap: 0 var(--s4); padding-block: var(--s4) var(--s8); }
  .work__intro { display: contents; }
  .work__cover-wrap { grid-column: 1; grid-row: 1 / span 3; }
  .work__covernote { display: none; }
  .work__title, .work__byline, .work__meta { grid-column: 2; }
  .work__title { font-size: clamp(1.45rem, 6vw, 2rem); }
  .work__byline { font-size: var(--step-0); margin-top: var(--s1); }
  .work__meta { margin-top: var(--s1); }
  .work__synopsis, .work__nota, .work__method, .work__actions { grid-column: 1 / -1; }
  .work__synopsis.is-clamped .work__synopsis-short { -webkit-line-clamp: 8; line-clamp: 8; }
  .work__synopsis-short { margin-top: var(--s5); font-size: var(--step-0); }
  .work__nota { margin-top: var(--s4); }
  .work__actions { margin-top: var(--s4); }
  /* #271: --s9 does not exist, so this gap used to collapse to 0 and the
     section titles sat against the block above. */
  .work__body, .work__col { gap: var(--s10); }
}

.section-title { font-size: var(--step-3); margin-bottom: var(--s5); padding-bottom: var(--s3); border-bottom: 1px solid var(--line); }

.facets { display: grid; gap: var(--s5); }
.facet { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: var(--s4); align-items: baseline; }
.facet__label { font-size: var(--step--2); font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--ink-soft); }
.facet__values { display: flex; flex-wrap: wrap; gap: var(--s2); }
.facet a.chip { text-decoration: none; }
.facet a.chip:hover { border-color: var(--line-ui); background: var(--accent-quiet); color: var(--accent); }
/* The primary facet reads louder than the rest — genre is what a reader
   scans for first, and a flat wall of identical chips buries it. */
.facet--lead .chip { background: var(--brand); color: var(--on-brand); border-color: transparent; font-weight: 600; }
.facet--lead a.chip:hover { background: color-mix(in oklab, var(--brand) 85%, var(--ink)); color: var(--on-brand); }
@media (max-width: 640px) { .facet { grid-template-columns: 1fr; gap: var(--s2); } }

.awards { display: grid; gap: var(--s3); list-style: none; margin: 0; padding: 0; }
.award {
  display: grid; grid-template-columns: 5.5ch 1fr auto; gap: var(--s4); align-items: baseline;
  padding: var(--s3) var(--s4); border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
}
.award__year { font-family: var(--font-display); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--gold); }
.award__name { font-weight: 500; }
.award__result { font-size: var(--step--1); color: var(--ink-soft); }

.editions { display: grid; gap: 0; list-style: none; margin: 0; padding: 0; }
.edition {
  display: grid; grid-template-columns: 72px 6ch minmax(0, 1fr); gap: var(--s4);
  align-items: start; padding: var(--s3) var(--s2);
  border-bottom: 1px solid var(--line);
}
.edition__cover { width: 72px; height: auto; max-height: 108px; object-fit: contain; grid-row: 1; grid-column: 1; }
.edition__cover--none { height: 0; }
.edition__year { grid-column: 2; }
.edition__main { grid-column: 3; }
.edition:first-child { border-top: 1px solid var(--line); }
.edition__year { font-family: var(--font-display); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.edition__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.edition__publisher { font-weight: 600; font-size: var(--step-0); }
.edition__volume { font-family: var(--font-display); font-style: italic; color: var(--ink-soft); font-size: var(--step-0); }
.edition__facts { display: flex; flex-wrap: wrap; gap: var(--s2); font-size: var(--step--1); color: var(--ink-soft); }
.edition__isbn { font-variant-numeric: tabular-nums; }
.edition__links { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s2); font-size: var(--step--1); }
@media (max-width: 640px) {
  .edition { grid-template-columns: 56px minmax(0, 1fr); gap: var(--s2) var(--s3); }
  .edition__cover { width: 56px; grid-row: 1 / span 2; }
  .edition__year { grid-column: 2; }
  .edition__main { grid-column: 2; }
  .edition__year--none { display: none; }
}
/* #261: when no edition of the work shows its own cover, the reserved cover
   column is dropped for the whole list (both lists, so they stay aligned). */
section[aria-labelledby="edicions-h"]:not(:has(img.edition__cover)) .edition { grid-template-columns: 6ch minmax(0, 1fr); }
section[aria-labelledby="edicions-h"]:not(:has(img.edition__cover)) .edition__cover--none { display: none; }
section[aria-labelledby="edicions-h"]:not(:has(img.edition__cover)) .edition__year { grid-column: 1; }
section[aria-labelledby="edicions-h"]:not(:has(img.edition__cover)) .edition__main { grid-column: 2; }
@media (max-width: 640px) {
  section[aria-labelledby="edicions-h"]:not(:has(img.edition__cover)) .edition { grid-template-columns: 1fr; }
  section[aria-labelledby="edicions-h"]:not(:has(img.edition__cover)) .edition__main { grid-column: 1; }
}
.edition-more { margin-top: var(--s2); }
.edition-more summary {
  display: flex; align-items: center; gap: var(--s2); min-height: var(--tap);
  cursor: pointer; font-size: var(--step-0); font-weight: 600; color: var(--accent);
  list-style: none;
}
.edition-more summary::-webkit-details-marker { display: none; }
.edition-more summary::before { content: "+"; font-size: 1.1em; line-height: 1; }
.edition-more[open] summary::before { content: "\2212"; }
.edition-more summary .less, .edition-more[open] summary .more { display: none; }
.edition-more[open] summary .less { display: inline; }
.edition-more summary:hover { text-decoration: underline; text-underline-offset: 0.24em; }

.factlist { display: grid; gap: 0; margin: 0; }
.factlist > div { display: grid; grid-template-columns: 11ch 1fr; gap: var(--s4); padding-block: var(--s3); border-bottom: 1px solid var(--line); }
.factlist dt { font-size: var(--step--1); color: var(--ink-soft); }
.factlist dd { margin: 0; font-size: var(--step-0); }
.factlist dd a { display: inline-block; padding-block: 3px; }

.author-intro { display: flex; align-items: center; gap: var(--s6); }
.author-intro__text { min-width: 0; }
.author-intro h1 { margin-bottom: var(--s2); }
.author-intro .author-life { margin: 0; color: var(--ink-soft); font-family: var(--font-display); font-size: var(--step-1); font-style: italic; overflow-wrap: anywhere; }
/* Task 173 (#164): the portrait is the backdrop of the author's head — from
   the very top of the page (behind the translucent top bar) down to the end
   of the name block, never taller than the page would be without it. It
   covers the right 70% of the width and fades in gradually from 70% to 40%
   of the width (measured from the right), inked in the page's own tone. */
.author-hero { position: relative; isolation: isolate; }
.author-backdrop {
  position: absolute; z-index: -1; margin: 0; pointer-events: none;
  top: calc(-1 * var(--header-h) - 1px); right: 0; bottom: 0; width: 70%;
  overflow: hidden;
}
.author-backdrop img {
  display: block; width: 100%; height: 100%;
  /* Task 183 (#195): the head is a ~4:1 strip, so 45% showed mouths and chins
     on 13 of 18 reviewed pages; 25% shows the eyes on most. Per-author focus
     comes from catalit/author_photo_overrides.json as an inline style. */
  object-fit: cover; object-position: 50% 25%;
  filter: grayscale(1) sepia(.22) contrast(1.06);
  mix-blend-mode: multiply;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 42.857%),
    linear-gradient(to bottom, #000 78%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent 0, #000 42.857%),
    linear-gradient(to bottom, #000 78%, transparent 100%);
  mask-composite: intersect;
}
/* The CC credit is required, not decorative: present and legible on a
   close look, nearly invisible at a glance. One colour, no underlines. */
.author-backdrop figcaption {
  position: absolute; right: 6px; bottom: 3px; max-width: 60%;
  font-size: 9px; line-height: 1.2; letter-spacing: .01em; text-align: right;
  color: var(--ink-soft); opacity: .45; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  pointer-events: auto;
}
.author-backdrop figcaption a { color: inherit; text-decoration: none; }
.author-backdrop figcaption a:hover, .author-backdrop figcaption a:focus-visible { text-decoration: underline; }
.author-hero--photo .author-intro { padding-bottom: var(--s8); }
.author-hero--photo .author-intro__text { max-width: 40%; }
/* The bar stays readable but lets the portrait through at the top. */
body:has(.author-hero--photo) .header { background: color-mix(in srgb, var(--paper) 55%, transparent); }
:root[data-theme="dark"] .author-backdrop img { mix-blend-mode: normal; opacity: .7; filter: grayscale(1) sepia(.12) contrast(1.02) brightness(.9); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .author-backdrop img { mix-blend-mode: normal; opacity: .7; filter: grayscale(1) sepia(.12) contrast(1.02) brightness(.9); }
}
.author-biography { max-width: 68ch; line-height: 1.8; font-size: var(--step-1); }
.author-biography p:first-child { margin-top: 0; }
.author-biography p:first-child::first-letter { float: left; font-family: var(--font-display); font-size: 3.6em; line-height: .85; padding: .12em .12em 0 0; color: var(--accent); }
.author-shelf { margin-top: var(--s12); }
.author-shelf .section-title { margin-bottom: var(--s2); }
.author-shelf__intro { color: var(--ink-soft); margin: 0 0 var(--s6); }
@media (max-width: 480px) {
  .author-intro { align-items: flex-start; gap: var(--s4); }
  .author-intro h1 { font-size: var(--step-2); }
  .author-intro .author-life { font-size: var(--step-0); }
  .author-hero--photo .author-intro__text { max-width: 62%; }
  .author-backdrop img { opacity: .75; }
  .author-backdrop figcaption { font-size: 7px; max-width: 70%; right: 4px; bottom: 2px; }
  .author-biography { font-size: var(--step-0); }
}

.outlinks { display: grid; gap: var(--s2); list-style: none; margin: 0; padding: 0; }
.outlink {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  min-height: var(--tap); padding: var(--s2) var(--s3);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); text-decoration: none; color: var(--ink); font-size: var(--step-0);
}
.outlink:hover { border-color: var(--line-ui); background: var(--sunken); }
.outlink__kind { font-size: var(--step--2); color: var(--ink-soft); }
.outlink .icon { width: 15px; height: 15px; color: var(--ink-soft); flex: none; }

.aside-card { padding: var(--s5); border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface); }
.aside-card h2 { font-size: var(--step-2); margin-bottom: var(--s4); }
.aside-card p { font-size: var(--step-0); color: var(--ink-soft); }
.aside-card > * + * { margin-top: var(--s3); }

/* --------------------------------------------------------- 14. Author page */
.authorlist { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); list-style: none; margin: 0; padding: 0; }
.authorcard { position: relative; display: grid; align-content: start;
  gap: var(--s2); padding: var(--s4) var(--s5); border: 1px solid var(--line);
  border-radius: var(--radius-lg); background: var(--surface); }
.authorcard:hover { border-color: var(--line-ui); box-shadow: var(--shadow-sm); }
.authorcard h3 { font-size: var(--step-1); }
.authorcard h3 a { color: var(--ink); text-decoration: none; }
.authorcard h3 a::after { content: ""; position: absolute; inset: 0; }
.authorcard__meta { font-size: var(--step--1); color: var(--ink-soft); }

/* The author page's header: their books standing in a row. */
.shelf {
  display: flex; gap: var(--s2); align-items: flex-end; overflow-x: auto;
  padding-bottom: var(--s3); margin-bottom: var(--s6); scrollbar-width: thin;
}
.shelf img {
  width: 74px; height: 111px; object-fit: cover; flex: none;
  border-radius: 3px; box-shadow: var(--shadow-sm);
  border: 1px solid rgb(0 0 0 / .10);
}
.alpha-nav { display: flex; flex-wrap: wrap; gap: var(--s1); margin-bottom: var(--s6); }
.alpha-nav a { display: grid; place-items: center; min-width: 36px; min-height: 36px; padding-inline: var(--s2); border: 1px solid var(--line); border-radius: var(--radius-sm); text-decoration: none; color: var(--ink); font-size: var(--step--1); }
.alpha-nav a:hover { background: var(--sunken); border-color: var(--line-ui); }
/* #203: the list is drawn from a JSON index in pages; each letter heading
   keeps its own spacing now that sections are no longer inline-styled. */
.authorindex section + section { margin-top: var(--s10); }
.authorindex .section-title { margin-bottom: var(--s4); }
.authorindex__more { margin-top: var(--s8); text-align: center; }
/* Without JavaScript only the prerendered first rows exist, so a letter
   link or "more" button would lead nowhere; every author still has a
   page, linked from the sitemap and from each work. */
html:not(.js) .alpha-nav, html:not(.js) [data-author-more] { display: none !important; }

/* --------------------------------------------------------- 15. Prose pages */
.page-head { padding-block: var(--s12) var(--s8); border-bottom: 1px solid var(--line); }
.page-head h1 { font-size: var(--step-5); max-width: 18ch; }
.page-head p { font-family: var(--font-display); font-size: var(--step-2); color: var(--ink-soft); max-width: 52ch; margin-top: var(--s4); }

.prose { font-family: var(--font-display); font-size: 1.1875rem; line-height: 1.65; max-width: var(--prose); }
.prose > * + * { margin-top: var(--s5); }
.prose h2 { font-size: var(--step-3); margin-top: var(--s12); }
.prose h3 { font-size: var(--step-2); margin-top: var(--s8); }
.prose ul, .prose ol { padding-inline-start: 1.3em; display: grid; gap: var(--s3); }
.prose strong { font-weight: 700; }
.prose a { font-weight: 500; }
.prose code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em; background: var(--sunken); padding: 1px 5px; border-radius: var(--radius-sm); }

/* The reading column, plus the section index in the space that was empty
   at any width over about 1100px. Below that the aside is hidden (section
   18) rather than reflowed into the column, where a second table of
   contents would just sit between the reader and the first paragraph. */
.prose-layout { display: grid; gap: var(--s8); align-items: start; }
/* A grid item's default `min-width` is `auto`, which means "never narrower
   than my content". The figures inside `.prose` set `min-width: 420px` on
   their SVG so the axis labels stay legible, and that floor propagated out
   through the grid item and made the whole page 470px wide at a 320px
   viewport — `scripts/site_check.py` caught it, the Python suite could
   not. `min-width: 0` lets the item shrink so each figure's own
   `overflow-x: auto` does the scrolling, which is what was intended. */
.prose-layout > * { min-width: 0; }
@media (min-width: 1100px) {
  .prose-layout { grid-template-columns: minmax(0, var(--prose)) 1fr; gap: var(--s12); }
}
.prose-index { position: sticky; top: calc(var(--header-h, 64px) + var(--s6)); }
.prose-index ol {
  list-style: none; margin: var(--s3) 0 0; padding: 0; display: grid; gap: var(--s2);
  border-left: 1px solid var(--line); padding-left: var(--s4);
}
.prose-index a {
  color: var(--ink-soft); text-decoration: none; font-size: var(--step--1);
}
.prose-index a:hover { color: var(--ink); text-decoration: underline; }

/* Figures. `currentColor` throughout the plot chrome, so the axes, ticks
   and labels follow the page's ink in both colour schemes instead of
   carrying two hard-coded greys that only work in one. */
.plot {
  margin: var(--s8) 0; padding: var(--s5); border: 1px solid var(--line);
  border-radius: var(--radius-lg); background: var(--surface); color: var(--ink);
}
.plot svg { display: block; width: 100%; height: auto; overflow: visible; }
.plot figcaption {
  margin-top: var(--s4); font-family: var(--font-ui); font-size: var(--step--1);
  line-height: 1.5; color: var(--ink-soft);
}
.plot figcaption code { font-size: 0.95em; }
.corpus-exclusion {
  margin-block: var(--s5);
  padding: var(--s5);
  border-inline-start: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--sunken);
  color: var(--ink);
  font-family: var(--font-ui);
}
.corpus-exclusion p { margin-block: var(--s2) 0; }
.prose .callout {
  font-family: var(--font-ui); font-size: var(--step-0);
  padding: var(--s5); border-inline-start: 3px solid var(--accent);
  background: var(--sunken); border-radius: 0 var(--radius) var(--radius) 0;
}
.prose figure { margin: 0; }
.prose blockquote {
  margin: 0; padding-inline-start: var(--s6); border-inline-start: 2px solid var(--line-ui);
  font-style: italic; color: var(--ink-soft);
}

.band { background: var(--sunken); border-block: 1px solid var(--line); }
.band__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s10); align-items: center; padding-block: var(--s12); }
.band__claim { font-family: var(--font-display); font-size: var(--step-4); line-height: 1.15; }
.band__claim em { font-style: italic; color: var(--accent); }
@media (max-width: 800px) { .band__grid { grid-template-columns: 1fr; gap: var(--s5); padding-block: var(--s10); } }

.cardlinks { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: var(--s4); }
.cardlink { position: relative; display: grid; gap: var(--s2); padding: var(--s6); border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface); }
.cardlink:hover { border-color: var(--line-ui); box-shadow: var(--shadow-sm); }
.cardlink h3 { font-size: var(--step-2); }
.cardlink h3 a { color: var(--ink); text-decoration: none; }
.cardlink h3 a::after { content: ""; position: absolute; inset: 0; }
.cardlink p { font-size: var(--step-0); color: var(--ink-soft); }

/* ------------------------------------------------------------- 16. Footer */
.footer { background: #16241d; color: #e8e4d8; margin-top: var(--s16); padding-bottom: env(safe-area-inset-bottom, 0px); }
:root[data-theme="dark"] .footer, :root:not([data-theme="light"]) .footer { background: #0d120e; }
.footer a { color: #e8e4d8; }
.footer__grid { display: grid; grid-template-columns: minmax(0, 1.2fr) repeat(2, minmax(0, 1fr)) minmax(0, 1.3fr); gap: var(--s8); padding-block: var(--s10) var(--s8); }
.footer__grid > * { min-width: 0; }
/* Older published pages receive spacing too, until their next regeneration. */
.footer > .footer__brand { padding-block: var(--s8) var(--s10); }
.footer > .footer__brand > p { margin-top: var(--s3); }
.footer > .footer__brand > p a { display: inline-flex; align-items: center; min-height: 44px; }
.footer__brand .brand__name { display: inline-flex; align-items: center; min-height: 44px; color: #f3efe4; font-size: 1.5rem; text-decoration: none; }
.footer__tagline { font-family: var(--font-display); font-style: italic; color: #bcc7ba; margin-top: var(--s3); }
.footer__description, .footer__support p { font-size: var(--step--1); color: #bcc7ba; }
.footer__description { margin-top: var(--s3); max-width: 28ch; }
.footer__coffee { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: var(--s2) var(--s4); margin-block: var(--s4) var(--s2); border: 1px solid #bcc7ba; border-radius: var(--radius); font-size: var(--step--1); font-weight: 600; text-decoration: none; }
.footer__coffee:hover { background: rgb(255 255 255 / 10%); }
.footer__support .footer__support-note { font-size: var(--step--2); }
.footer h2 { font-family: var(--font-ui); font-size: var(--step--2); font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; color: #a9b6a7; margin-bottom: var(--s4); }
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s1); }
.footer ul a { display: inline-flex; align-items: center; min-height: 44px; font-size: var(--step--1); text-decoration: none; }
.footer ul a:hover { text-decoration: underline; text-underline-offset: 0.25em; }
.footer__legal { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s6); justify-content: space-between; padding-block: var(--s5) var(--s8); border-top: 1px solid rgb(255 255 255 / 12%); font-size: var(--step--1); color: #a9b6a7; }
@media (max-width: 800px) { .footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s8) var(--s5); padding-block: var(--s10) var(--s6); } }
@media (max-width: 460px) {
  .footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s6) var(--s4); }
  .footer__brand, .footer__support { grid-column: 1 / -1; }
  .footer__brand .brand__name { font-size: 1.4rem; }
  .footer__tagline { margin-top: var(--s1); }
  .footer__description { max-width: 36ch; }
  .footer__legal { padding-bottom: var(--s10); gap: var(--s2); }
}

/* ------------------------------------------- 17. Dialog, states, utilities */
dialog.support-dialog {
  width: min(560px, calc(100vw - 24px)); max-width: none;
  max-height: calc(100dvh - 24px); margin: auto; padding: 0;
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--paper); color: var(--ink); box-shadow: var(--shadow-md);
  overflow-y: auto; overscroll-behavior: contain;
}
.support-dialog::backdrop { background: rgb(12 18 14 / 60%); }
html:has(.support-dialog[open]) { overflow: hidden; }
.support-dialog__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  position: sticky; top: 0; z-index: 1; padding: var(--s5);
  background: var(--paper); border-bottom: 1px solid var(--line);
}
.support-dialog__head h2 { font-size: var(--step-2); line-height: 1.2; }
.support-dialog__head .icon-btn { flex-shrink: 0; }
.support-dialog__eyebrow { margin: 0 0 var(--s2); color: var(--muted); font-size: var(--step--1); }
#support-description { padding: var(--s4) var(--s5); margin: 0; font-size: var(--step--1); }
.support-dialog__panel { position: relative; background: #fff; }
.support-dialog__loading { position: absolute; inset: var(--s4) var(--s5) auto; color: #424242; font-size: var(--step--1); }
.support-dialog__frame { display: block; width: 100%; height: 760px; border: 0; color-scheme: light; }
.support-dialog__alternative { margin: 0; padding: var(--s4) var(--s5); border-top: 1px solid var(--line); font-size: var(--step--1); color: var(--muted); }
.support-dialog__alternative a { display: inline-block; padding-block: var(--s2); }
@media (max-width: 620px) {
  .support-dialog__head { padding: var(--s4); }
  #support-description, .support-dialog__alternative { padding: var(--s3) var(--s4); }
  .support-dialog__head h2 { font-size: var(--step-1); }
}

dialog.sheet {
  width: min(560px, 100vw); max-width: 100vw; max-height: 100dvh; height: auto;
  margin: 0 0 0 auto; padding: 0; border: 0;
  background: var(--paper); color: var(--ink);
  box-shadow: var(--shadow-md);
}
dialog.sheet::backdrop { background: rgb(12 18 14 / 55%); }
.sheet__inner { display: flex; flex-direction: column; max-height: 100dvh; }
.sheet__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); padding: var(--s5) var(--s5) var(--s4); border-bottom: 1px solid var(--line); }
.sheet__head h2 { font-size: var(--step-2); }
.sheet__body { flex: 1; overflow-y: auto; padding: var(--s5); }
.sheet__foot { display: flex; gap: var(--s3); padding: var(--s4) var(--s5); border-top: 1px solid var(--line); background: var(--surface); }
.sheet__foot .btn { flex: 1; }
@media (max-width: 620px) { dialog.sheet { width: 100vw; height: 100dvh; } }

.empty {
  display: grid; gap: var(--s4); justify-items: start;
  padding: var(--s12) var(--s6); border: 1px dashed var(--line-ui); border-radius: var(--radius-lg);
  background: var(--surface);
}
.empty h2 { font-size: var(--step-2); }
.empty p { color: var(--ink-soft); max-width: 52ch; font-size: var(--step-0); }

.pending { font-size: var(--step--1); color: var(--ink-soft); font-style: italic; }

.toast {
  position: fixed; inset-block-end: var(--s6); inset-inline: var(--s4) auto; z-index: 60;
  max-width: min(420px, calc(100vw - var(--s8)));
  padding: var(--s3) var(--s5); border-radius: var(--radius);
  background: var(--brand); color: var(--on-brand); box-shadow: var(--shadow-md);
  font-size: var(--step-0);
}

.noscript-note { padding: var(--s3) 0; font-size: var(--step--1); color: var(--ink-soft); }

/* ------------------------------------------- 18. Small-screen overrides */
/* Last on purpose: these override components defined throughout the
   file, and a media query carries no extra specificity — placed any
   earlier, the base rules below it quietly win. */
@media (max-width: 700px) {
  /* The search button wrapped onto its own full-width row (136px for one
     field), and the catalogue heading repeated what the origin switch
     directly above it already says. Both go, and the toolbar collapses to
     a single row: about 170px reclaimed before the first book. */
  .searchbar__form { flex-wrap: nowrap; gap: var(--s2); }
  .searchbar__field { flex: 1 1 auto; min-width: 0; }
  .searchbar .btn { padding-inline: var(--s5); }
  /* «Explora el catàleg» took half a 390px row and cut the placeholder to
     «Cerca per t» (#201). The verb alone says it. */
  .searchbar__btn-long { display: none; }
  .catalog-head { flex-wrap: nowrap; align-items: center; }
  .catalog-head__title h2 { position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap; }
  .toolbar { flex: 1; justify-content: flex-end; gap: var(--s2); }
  .toolbar .select label { display: none; }

  /* The count gets its own short line; the controls share a row when they
     fit. Enlarged text must be able to move controls onto another row. */
  .catalog-head { flex-direction: column; align-items: stretch; gap: var(--s2); }
  .catalog-head__title { gap: var(--s2); }
  .toolbar { justify-content: space-between; flex-wrap: wrap; width: 100%; }
  .filter-btn__long { display: none; }
  .filter-btn__short { display: inline; }
  .toolbar .select select { padding-inline: var(--s3) var(--s6); }

  /* Task 022's additions. The section index goes: below 1100px it has no
     column of its own, and reflowed into the reading column it is a
     second table of contents in front of the first paragraph. The anchors
     it linked to are still there for anyone with the URL. */
  .prose-index { display: none; }

  /* A figure keeps its own horizontal scroll rather than making the page
     scroll sideways — the axis labels stop being legible much below
     440px, and squeezing them is worse than letting the plot pan. */
  .plot { padding: var(--s4); overflow-x: auto; }
  .plot svg { min-width: 420px; }

  /* Bands get shorter, and the author page's shelf of covers becomes a
     single scrolling row instead of a wall. */
  .band { height: clamp(96px, 30vw, 150px); }
  .band--inline { height: clamp(64px, 20vw, 96px); }
  .shelf img { width: 58px; height: 87px; }

  /* The hero's scrim runs left-to-right, which works when there is room
     beside the artwork. On a phone there is none: the text would sit over
     the middle of the picture. So the gradient turns vertical and the
     artwork keeps the top, which is the same trick a book jacket uses. */
  .hero-band { min-height: clamp(250px, 66vw, 330px); }
  .hero-band__art { object-position: center top; }
  .hero-band__scrim {
    background: linear-gradient(
      to top,
      color-mix(in srgb, var(--paper) 96%, transparent) 0%,
      color-mix(in srgb, var(--paper) 92%, transparent) 48%,
      color-mix(in srgb, var(--paper) 40%, transparent) 72%,
      color-mix(in srgb, var(--paper) 0%, transparent) 100%);
  }
  .hero-band__inner { padding-block: var(--s12) var(--s6); }
  .hero-band__text { max-width: none; }
}

@media (max-width: 560px) {
  /* The KPI row is context, not a task. On a phone it cost a third of the
     first screen before a single book appeared, so it moves out of the way
     and the numbers live on /projecte instead. */
  .hero__stats { display: none; }
  .hero__lede { font-size: var(--step-0); margin-top: var(--s2); }
  .hero .eyebrow { font-size: 0.6875rem; }
  .searchbar { padding-bottom: var(--s4); }
  .section--tight { padding-block: var(--s4) var(--s2); }
  .section--catalog { padding-block: var(--s5) var(--s12); }
  .catalog-head { margin-bottom: var(--s3); }
  .origin-switch { margin-bottom: var(--s4); }
}

@media print {
  .header, .footer, .toolbar, .pagination, .hero__art, .skip-link, dialog { display: none !important; }
  body { background: #fff; color: #000; }
  a::after { content: " (" attr(href) ")"; font-size: 0.8em; }
}

/* Task 165 (#147): «Joanot en xifres» on the project page. */
.stat-tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: var(--s3); margin: var(--s6) 0 var(--s6); font-family: var(--font-ui);
}
/* The total heads the row; the four parts of it sit underneath. */
.stat-tile:first-child { grid-column: 1 / -1; }
.stat-tile {
  padding: var(--s4); border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); display: flex; flex-direction: column; gap: var(--s2);
}
/* The number reads first, at the top of every tile, whatever the label's length. */
.stat-tile dt { order: 2; font-size: var(--step--1); line-height: 1.35; color: var(--ink-soft); }
.stat-tile dd { order: 1; margin: 0; }
.stat-tile__n {
  font-family: var(--font-display); font-size: var(--step-3); line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.stat-tile__pct { font-size: var(--step--1); color: var(--ink-soft); }
.stat-details { margin-top: var(--s4); font-family: var(--font-ui); font-size: var(--step--1); }
.stat-details summary { cursor: pointer; color: var(--ink-soft); }
.stat-ranked { margin-top: var(--s8); }
.stat-table {
  width: 100%; border-collapse: collapse; font-family: var(--font-ui);
  font-size: var(--step--1); font-variant-numeric: tabular-nums;
}
.stat-table caption {
  text-align: start; font-family: var(--font-display); font-size: var(--step-1);
  font-weight: 600; padding-bottom: var(--s3); color: var(--ink);
}
.stat-table th, .stat-table td {
  padding: var(--s2); border-bottom: 1px solid var(--line); text-align: end;
}
.stat-table th:first-child { text-align: start; font-weight: 500; overflow-wrap: anywhere; }
.stat-table thead th { color: var(--ink-soft); font-weight: 500; }
.stat-ranked tbody th { width: 75%; }
.stat-bar { display: block; height: 6px; margin-top: 5px; border-radius: 3px; background: var(--sunken); }
.stat-bar > span { display: block; height: 100%; border-radius: 3px; background: var(--brand); opacity: .82; }
.stat-note {
  margin-top: var(--s3); font-family: var(--font-ui); font-size: var(--step--1);
  line-height: 1.5; color: var(--ink-soft);
}

/* #79 «Quanta gent en parla?»: one to four rising bars, a level name and a
 * tooltip on hover/focus. The question label is dropped on narrow screens;
 * the visually hidden sentence keeps it for assistive technology. */
.talk { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s3); font-size: var(--step--1); }
.talk__q { font-weight: 600; color: var(--ink-soft); }
.talk__level { position: relative; display: inline-flex; align-items: center; gap: var(--s2); font-weight: 600; cursor: help; border-radius: 4px; }
.talk__level:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.talk__bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 1em; }
.talk__bar { width: 4px; border-radius: 1px; background: var(--line); }
.talk__bar:nth-child(1) { height: 25%; }
.talk__bar:nth-child(2) { height: 50%; }
.talk__bar:nth-child(3) { height: 75%; }
.talk__bar:nth-child(4) { height: 100%; }
.talk__bar.is-on { background: var(--brand); }
.talk__level::after {
  content: attr(data-help); position: absolute; inset-inline-start: 0; top: calc(100% + 6px); z-index: 5;
  width: max-content; max-width: min(18rem, 80vw); padding: var(--s2) var(--s3); border-radius: 6px;
  background: var(--ink); color: var(--paper, #fff); font-weight: 400; line-height: 1.35;
  opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .12s;
}
.talk__level:hover::after, .talk__level:focus-visible::after { opacity: 1; visibility: visible; }
@media (max-width: 640px) { .talk__q { display: none; } }
.talk-levels { display: grid; gap: var(--s3); margin: var(--s4) 0; }
.talk-levels__item dt { display: flex; align-items: center; gap: var(--s2); font-weight: 700; }
.talk-levels__item dd { margin: 0; color: var(--ink-soft); }
.talk-levels .talk__bars { font-size: var(--step-1); }

/* #231 / task 206: the special «Sense autor conegut» author page. No portrait,
   no dates: a quiet emblem, the explanation as copy, then the works. */
.unknown-head { align-items: center; gap: var(--s8); }
.unknown-emblem { flex: none; width: 112px; height: 112px; color: var(--accent); }
.unknown-head .eyebrow { margin-bottom: var(--s2); }
.unknown-head h1 { max-width: none; }
.page-head.unknown-head p.unknown-lead { font-family: var(--font-display); font-style: italic; font-size: var(--step-2); color: var(--ink-soft); margin: var(--s3) 0 0; max-width: 46ch; }
.unknown-copy { max-width: 68ch; font-size: var(--step-1); line-height: 1.7; }
.unknown-copy > p { margin: 0 0 var(--s4); }
.unknown-intro { font-family: var(--font-display); font-size: var(--step-2); line-height: 1.55; }
.unknown-cases { list-style: none; margin: var(--s6) 0 var(--s10); padding: 0; display: grid; gap: var(--s4); grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 100%; }
.unknown-case { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--s4) var(--s5); }
.unknown-case h3 { font-size: var(--step-1); margin: 0 0 var(--s2); }
.unknown-case p { margin: 0; font-size: var(--step-0); line-height: 1.55; color: var(--ink-soft); }
.unknown-h { font-size: var(--step-3); margin: var(--s10) 0 var(--s3); }
.unknown-help { margin-top: var(--s10); padding: var(--s6); background: var(--accent-quiet); border-radius: var(--radius); }
.unknown-help .unknown-h { margin-top: 0; }
.unknown-help p:last-child { margin-bottom: 0; }
.work__credited { margin: var(--s1) 0 0; font-size: var(--step--1); color: var(--ink-soft); font-style: italic; }
.page-head p.authors-unknown-note { font-family: var(--font-ui); font-size: var(--step-0); margin-top: var(--s3); }
@media (max-width: 640px) {
  .unknown-head { flex-direction: column; align-items: flex-start; gap: var(--s4); }
  .unknown-emblem { width: 72px; height: 72px; }
  .unknown-cases { grid-template-columns: 1fr; }
  .unknown-copy { font-size: var(--step-0); }
  .unknown-intro { font-size: var(--step-1); }
  .unknown-help { padding: var(--s4); }
}
