/* ─────────────────────────────
   PROJECT STYLES
   ─────────────────────────────

   Project-specific styles. Prefer the design system;
   add here only what it can't express.

   Two parts, and the boundary matters:

   §1  DOCS-SITE SHELL — copied from the docs kit. Not ours. Do not redesign
       these rules; they exist to keep the glossary looking like the rest of
       the studio. See the provenance note at the top of §1.
   §2  GLOSSARY — genuinely this project's own.
   ───────────────────────────── */


/* ═══════════════════════════════════════════════════════════════════════
   1. DOCS-SITE SHELL
   ═══════════════════════════════════════════════════════════════════════

   Copied from `docs/site/assets/docs-kit/docs.css`, the stylesheet the docs
   generator writes. Only the rules the glossary actually uses are here —
   the kit is ~2,500 lines and most of it is sidebar, auth, sticky bars,
   asset cards and demo chrome that this site has no use for.

   WHY COPIED RATHER THAN LINKED. These classes live only in the docs kit,
   which `CLAUDE.md` §17 calls layer discipline. The only built copy lives
   under `docs/site/`, a generated artefact of `npm run docs:build`. Pointing
   a product page at it would make the glossary depend on the docs build
   having run, and would pull in the whole kit. So the needed rules are
   lifted here instead.

   WHAT v2.2.1 CHANGED, and what is left. The book family (`.book-shelf`,
   `.book-cover`, `.book-contents`) plus `.page-nav` and `.breadcrumb` were
   promoted out of the docs-site layer into the framework, and the kit no
   longer defines them at all. Those arrive through `design-system.css` now
   and are used directly — see §2b. Everything still in this section was
   checked selector by selector against `design-system.css` v2.2.1 on
   2026-08-04 and resolves nowhere in it, so it remains genuinely kit-only:
   `.docs-layout`, `.docs-main-area`, `.docs-main`, `.docs-content-grid`,
   `.docs-toc`, `.toc-*`, `.docs-hero*`, `.docs-section*`, `.top-nav*` and
   the `no-sidebar` state. The layout variables in 1a are kit-only too.

   (One upstream doc is stale on this and should not be trusted over the CSS:
   `llms-full.txt` still lists `book-cover` under the non-shipping docs-site
   layer. The stylesheet is the authority — it ships the component.)

   THE COST, STATED PLAINLY: what is left is still duplicated code and it
   will still drift. Treat any change to the docs kit as a prompt to
   re-check this section — the v2.2.1 re-check found real drift and it is
   folded in below, marked SYNCED.

   Class names are the kit's, unchanged, on purpose: renaming now would make
   that upstream move a rewrite instead of a copy. They will be renamed for
   context once they land in their permanent home.

   Divergences from the kit are marked DIVERGENCE and each says why. There
   are four. Nothing else has been altered. */


/* ------ 1a. Layout variables ------ */
/* docs.css §1 */

:root {
  --top-nav-height: 80px;

  /* DIVERGENCE — the kit ships 70px for its sticky sub-header bar (breadcrumb,
     filename, page actions). The glossary has no such bar, and the value feeds
     two calc()s below. Zero here keeps those calcs correct and makes adding the
     bar later a one-line change rather than a hunt. */
  --sticky-bar-height: 0px;

  --sidebar-width: 280px;
  --toc-width: 220px;
  --toc-top-offset: calc(var(--top-nav-height) + var(--sticky-bar-height) + var(--space-xl));
  --toc-border: color-mix(in srgb, var(--text-primary) 10%, transparent);
  --toc-border-active: color-mix(in srgb, var(--text-primary) 30%, transparent);
  --sidebar-transition: 0.3s ease;
}

/* Smooth scroll for same-page anchor links — the A–Z rail and the table of
   contents are both anchor jumps, so this is doing real work here. */
html {
  scroll-behavior: smooth;
}

/* DIVERGENCE — not in the kit. Smooth scrolling is motion, and a jump of
   several thousand pixels down an A–Z list is a lot of it. Honour the setting. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}


/* ------ 1b. Layout grid ------ */
/* docs.css §1. Sidebar, top nav and main content are siblings in one grid.
   The glossary runs with `body.no-sidebar`, which is a state the kit ships
   rather than something bolted on — so a sidebar can be added later by
   dropping an <aside class="site-sidebar"> in and removing the body class,
   with no structural change. The sidebar's own rules come across at that
   point; there is no reason to carry dead CSS until then. */

/* SYNCED 2026-08-04 — the kit moved the flexible tracks in this section from
   `1fr` to `minmax(0, 1fr)` between v2.1.1 and v2.2.1, here and in the three
   rules below. `1fr` means `minmax(auto, 1fr)`, so the track floors at the
   min-content width of whatever is in it and a wide child widens the page.
   This is the track-level half of the same bug the .docs-main-area DIVERGENCE
   fixes at the item level; both are needed, and neither replaces the other. */
.docs-layout {
  display: grid;
  grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
  grid-template-rows: var(--top-nav-height) 1fr;
  grid-template-areas:
    "sidebar topbar"
    "sidebar main";
  min-height: 100vh;
  background: var(--background-primary);
  transition: grid-template-columns var(--sidebar-transition);
}

body.no-sidebar .docs-layout {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "topbar"
    "main";
}

.docs-main-area {
  grid-area: main;
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - var(--top-nav-height));
  position: relative;
  background-color: var(--background-primary);

  /* DIVERGENCE — not in the kit, and it pairs with the one on .docs-main below.
     This is a grid item, so its min-width resolves to `auto`, meaning it refuses
     to shrink below the min-content width of everything inside it. One image
     with a width attribute is enough to break that open: during intrinsic
     sizing a percentage max-width has no definite container to resolve against,
     so `max-width: 100%` is ignored and an <img width="1200"> contributes all
     1200px. The track then stretches past the viewport and the whole page —
     top nav included — scrolls sideways on a phone.

     The kit never meets this because `overflow: hidden` on .docs-main clips it;
     removing that (see below) is what exposed it. Zeroing the floor here gives
     the column a definite width again, at which point max-width: 100% resolves
     normally and the image caps itself.

     Found with a stubbed entry image, since no real upload exists yet — it
     would otherwise have shipped and surfaced on the first entry with a picture.

     STILL NEEDED at v2.2.1, re-checked 2026-08-04: the kit's .docs-main-area
     carries no min-width. Upstream did adopt `minmax(0, 1fr)` on the track
     (see SYNCED above), which stops the *track* growing — this stops the
     *item* refusing to shrink. Worth raising in the Design System repo. */
  min-width: 0;
}

.docs-content-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2xl);
  max-width: 1080px;
  margin: 0 auto;
  width: 100%;
  padding: var(--space-xl) var(--space-xl) 0;
  flex: 1;
}

/* The presence of a table of contents is what makes the layout two-column.
   :has() matches on the element being in the DOM, not on it being visible, so
   the rail's column is reserved from first paint and the content does not
   reflow when the entry arrives. */
.docs-content-grid:has(.docs-toc) {
  grid-template-columns: minmax(0, 1fr) var(--toc-width);
}


/* ------ 1c. Top nav ------ */
/* docs.css §1B */

.top-nav {
  grid-area: topbar;
  position: sticky;
  top: 0;
  height: var(--top-nav-height);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-l);
  background: var(--background-top-nav);
  color: var(--text-top-nav);
  border-bottom: var(--border-s) solid var(--border-faded);
}

.top-nav-left {
  display: flex;
  align-items: stretch;
  height: 100%;
  gap: var(--space-m);
  min-width: 0;
}

.top-nav-right {
  display: flex;
  align-items: stretch;
  height: 100%;
}

.top-nav-logo-link {
  display: flex;
  align-items: center;
  text-decoration: none;
}

/* DIVERGENCE — not in the kit, which uses an SVG logo (.svg-logo.nav-logo) and
   so never needed a text treatment. The glossary's logo is still the template
   placeholder, and a wordmark is more honest than a borrowed one. Delete this
   and use the kit's logo classes once there is a real mark. */
.top-nav-logo-text {
  font-family: var(--font-quaternary);
  font-size: var(--font-s);
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-plain);
}

/* Standalone interactive items in top-nav-right — the theme toggle here. */
.top-nav-link {
  display: flex;
  align-items: center;
  align-self: stretch;
  gap: var(--space-s);
  padding: 0 var(--space-m);
  background: none;
  border: none;
  color: var(--text-primary);
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-primary);
  font-size: var(--font-s);
  line-height: 1;
  text-decoration: none;
  transition: color 0.2s, background 0.2s;
}

.top-nav-link:hover {
  color: var(--text-plain);
  background: var(--background-faded);
}

.top-nav-link .svg-icn {
  width: 1.25rem;
  height: 1.25rem;
}


/* ------ 1d. Main content column ------ */
/* docs.css §3 */

.docs-main {
  width: 100%;
  padding: var(--space-m) 0 var(--space-5xl) 0;

  /* DIVERGENCE — the kit sets `overflow: hidden` here to stop wide content
     widening the page. That also makes this element a scroll container, which
     silently breaks `position: sticky` on anything inside it — and the A–Z rail
     lives in here. `min-width: 0` stops the grid item being sized by its
     content's min-content width, which is the actual containment wanted, with
     no scroll container created. Wide children handle themselves: <pre>
     scrolls, tables get .table-scroll, prose sets overflow-wrap.

     STILL NEEDED at v2.2.1, re-checked 2026-08-04: the kit's .docs-main is
     unchanged and still sets `overflow: hidden`. Do not put it back. */
  min-width: 0;
}

/* Anchor targets clear the sticky nav. */
h1[id],
h2[id],
h3[id],
section[id] {
  scroll-margin-top: calc(var(--top-nav-height) + var(--sticky-bar-height) + var(--space-xl));
}


/* ------ 1e. Table of contents ------ */
/* docs.css §4 */

.docs-toc {
  position: sticky;
  top: var(--toc-top-offset);
  height: fit-content;
  max-height: calc(100vh - var(--toc-top-offset));
  overflow-y: auto;
  padding: var(--space-l);
}

.toc-header {
  display: block;
  font-size: var(--font-xs);
  font-weight: var(--font-weight-semi-bold);
  color: var(--text-faded);
  margin-bottom: var(--space-m);
}

.toc-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.toc-item {
  margin: 0;
}

.toc-link {
  color: var(--text-plain);
  text-decoration: none;
  font-size: var(--font-s);
  font-weight: var(--font-weight-medium);
  display: block;
  padding: var(--space-s) 0 var(--space-s) var(--space-l);
  border-left: var(--border-m) solid var(--toc-border);
  transition: color 0.2s, border-color 0.2s;
}

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

.toc-link-active {
  color: var(--text-link);
  font-weight: var(--font-weight-semi-bold);
  border-left-color: var(--toc-border-active);
}

.toc-empty {
  color: var(--text-faded);
  font-size: var(--font-s);
}


/* ------ 1f. Hero and sections ------ */
/* docs.css §7 */

.docs-hero {
  text-align: center;
  margin-bottom: var(--space-6xl);
}

.docs-hero-title {
  font-size: var(--font-8xl);
  margin-bottom: var(--space-l);
}

.docs-hero-description {
  font-size: var(--font-xl);
  color: var(--text-secondary);
}

.docs-section {
  margin-bottom: var(--space-3xl);
}

.docs-section-title {
  font-size: var(--font-4xl);
  margin-bottom: var(--space-xl);
  color: var(--text-plain);
}


/* ------ 1g. Book family — NOT COPIED, and no longer copyable ------ */
/* The book family is framework-owned as of v2.2.0 and this section is
   deliberately empty.

   The earlier note here said to re-copy `.book-cover` from
   `docs/site/assets/docs-kit/docs.css` if a full-size cover was ever wanted
   again. That instruction is now wrong twice over: the kit no longer defines
   `.book-cover` at all, and `design-system.css` does — so copying is both
   impossible and unnecessary. Use the component directly and re-point its
   `--book-cover-*` tokens, which is how the system says to re-skin it.

   Reduced motion needs no local handling either. The framework re-points
   `--book-cover-lift` to `0` under `prefers-reduced-motion: reduce`, so the
   hover lift stops while the colour and shadow feedback stay. The DIVERGENCE
   that used to be needed here is upstream behaviour now. */


/* ------ 1h. Footer ------ */
/* docs.css §6, copied verbatim. Kit-only: the framework ships no `.docs-footer`
   (checked, 0 hits in design-system.css v2.2.1).

   FULL WIDTH BY POSITION, not by CSS. Both this and `.page-nav` sit as
   siblings of `.docs-content-grid` inside `.docs-main-area`, which is the
   docs site's own arrangement — the grid caps its contents at 1080px, so
   anything that must span the viewport has to sit outside it. The inset then
   comes from `padding-global` on the inner wrapper. Nothing here forces a
   width; moving either element back inside the grid is all it would take to
   confine it again.

   `margin-top: auto` is what pins the footer to the bottom of a short page.
   It works because `.docs-main-area` is a flex column and `.docs-content-grid`
   carries `flex: 1` — both already in 1b.

   Note the inner is capped at 1400px rather than the grid's 1080px. That is
   the kit's number and is left alone; the footer is chrome spanning the page,
   not part of the reading column. */

.docs-footer {
  background: var(--background-primary);
  border-top: var(--border-s) solid var(--border-faded);
  padding: var(--space-2xl) 0;
  margin-top: auto;
  width: 100%;
}

.docs-footer-inner {
  max-width: 1400px;
  margin: 0 auto;
  text-align: center;
}

.docs-footer-text {
  color: var(--text-secondary);
  font-size: var(--font-s);
  margin: 0;
}


/* ------ 1i. Back to top ------ */
/* docs.css §6, copied verbatim. Kit-only: `back-to-top` resolves nowhere in
   design-system.css v2.2.1. It sits on 117 pages of the design system's own
   site, which is what makes it site furniture rather than a flourish.

   It earns its place here more than it does there. A docs page is a few
   screens; this index is 22 on a desktop and 31 on a phone, and the A–Z rail
   only helps once you know which letter you want. Behaviour is in
   assets/js/back-to-top.js.

   The button is the design system's own `.button` with `data-icon-only`; only
   the two colour tokens are re-pointed, which is the sanctioned way to reskin
   it. Nothing here overrides a button rule. */

.back-to-top-wrapper {
  position: fixed;
  right: var(--space-m);
  bottom: var(--space-m);
  z-index: 100;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s, visibility 0.3s;
}

.back-to-top-wrapper.is-visible {
  opacity: 1;
  visibility: visible;
}

.back-to-top-btn {
  --button-primary: var(--background-secondary);
  --button-text: var(--text-primary);
}


/* ------ 1j. Responsive ------ */
/* docs.css §8. The design system already collapses .grid to one column at
   768px, so the kit's duplicate of that rule is not copied. */

/* Tablet: the table of contents drops below the content it indexes. */
@media (max-width: 1024px) {
  /* SYNCED 2026-08-04 — `minmax(0, 1fr)`, as above. */
  .docs-content-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .docs-toc {
    position: static;
    max-height: none;
  }
}

@media (max-width: 768px) {
  .docs-content-grid {
    display: block;
    padding-left: var(--space-l);
    padding-right: var(--space-l);
    padding-top: 0;
  }

  /* DIVERGENCE — the kit zeroes .top-nav padding on mobile because its
     hamburger button supplies the inset. There is no sidebar here and so no
     hamburger, and flush-to-the-edge is just wrong. Keep the inset. */
}


/* ═══════════════════════════════════════════════════════════════════════
   2. GLOSSARY
   ═══════════════════════════════════════════════════════════════════════ */


/* ------ 2a. A–Z INDEX ------ */
/* The one component neither the design system nor the docs kit provides.
   Behaviour lives in assets/js/az-index.js; only presentation is here.

   Built on a flex-wrap row rather than a new layout primitive, so the rail
   inherits the system's wrapping and gap behaviour. */

:root {
  /* Starting value only. az-index.js measures the rail and overwrites this on
     :root as it rewraps — one row of letters at desktop width, three on a
     phone, which is 48px against 120px. Nothing should rely on the number
     here; it exists so the offset below is sane for the moment between first
     paint and the rail being built. */
  --az-index-height: 3rem;
}

.az-index {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: center;

  /* Sticks under the top nav — not the viewport top, which is where the nav
     is — so the rail stays reachable while scrolling a long list. That is the
     whole point of a phone book index, and at 500+ entries it is the primary
     way anyone gets anywhere. */
  position: sticky;
  top: var(--top-nav-height);
  z-index: 1;

  padding-block: var(--space-s);
  background-color: var(--background-primary);
}

.az-index-letter {
  display: inline-flex;
  align-items: center;
  justify-content: center;

  /* Fixed square so the rail does not reflow between narrow letters (I) and
     wide ones (W) as the active state changes. */
  min-width: 2rem;
  min-height: 2rem;

  font-size: var(--font-s);
  font-family: var(--font-primary);
  text-transform: uppercase;
  text-decoration: none;

  color: var(--text-secondary);
  background-color: var(--bg-faded-5);
  border-radius: 3px;

  transition: background-color 0.2s, color 0.2s;
}

.az-index-letter:hover {
  color: var(--text-primary);
  background-color: var(--background-faded);
}

.az-index-letter:focus-visible {
  outline: 1px solid var(--input-focus);
  outline-offset: 2px;
}

.az-index-letter.is-active {
  color: var(--background-primary);
  background-color: var(--text-primary);
}

/* Letters with no entries stay in place rather than being removed, so the
   alphabet does not shift under the cursor as filters change. */
.az-index-letter.is-disabled {
  color: var(--text-faded);
  background-color: transparent;
  pointer-events: none;
}


/* ------ 2b. GLOSSARY LIST ------ */

/* Offsets both sticky layers, so clicking a letter does not park its heading
   underneath the nav and the rail. There are two of them here — the docs kit
   only ever has the one — which is why this cannot use the kit's offset.

   The attribute selector is load-bearing, not decoration. §1 copies the kit's
   `h1[id], h2[id], h3[id], section[id]` rule, and a letter group is a <section>
   with an id, so that rule matches too. At (0,1,1) it outweighs a bare
   `.az-group` at (0,1,0) and silently wins, leaving the rail out of the sum
   entirely — the heading then lands 24px under the rail on a desktop and 96px
   under it on a phone. Adding [id] takes this to (0,2,0), which is enough.
   Do not simplify it back. */
.az-group[id] {
  scroll-margin-top: calc(var(--top-nav-height) + var(--az-index-height) + var(--space-m));
}

/* -- Search --
   The design system already styles input[type="search"] — font, colour,
   border, 44px min-height, focus outline. Only the things specific to this
   field are here.

   No icon inside the field. The sprite is 105KB and this page otherwise
   requests none of it, so one decorative magnifier would cost more than the
   markup it decorates; inlining a second SVG would mean a second exception to
   "icons come from the sprite" for something the placeholder already says. */
.glossary-search {
  /* The field is a lookup box, not a form control in a column of form
     controls, so it reads better a step up from body size. */
  font-size: var(--font-l);
}


/* -- Entries --
   The shelf and its covers are the framework's components as of v2.2.1. Their
   grid, height, type and hover all come from `design-system.css`; nothing here
   restates any of it. What is here is the density tuning for 221 covers, done
   the way the system says to do it — by re-pointing `--book-cover-*` and
   `--book-shelf-*` tokens, never by overriding a component rule.

   SCOPED TO THIS SHELF ON PURPOSE. `.glossary-shelf` is a project class added
   beside `.book-shelf` in glossary-list.js so the tuning cannot leak. A tag
   landing view is still on ROADMAP.md and would want full-size covers at their
   shipped proportions; if these tokens were re-pointed globally it would
   silently inherit an index's density.

   WHY THE TOKENS ARE SET ON `.book-cover` AND NOT ON THE SHELF. The component
   declares every `--book-cover-*` token inside its own `.book-cover` rule and
   re-points them again on `.book-cover[data-size="small"]`. A value set on an
   ancestor is inherited and then immediately overridden by the element's own
   declaration, so it does nothing at all — this was measured, and a first pass
   setting them on `.book-shelf` moved the page height by zero pixels. They
   have to land on the element that declares them.

   THE MEASUREMENT, and it corrects the estimate this work was planned against.
   The plan assumed small covers would sit at their 170px floor, giving roughly
   74 rows at ~195px. They do not: content pushes the median to 221px and the
   grid stretches each row to its tallest of three, so untouched small covers
   three-up measured 22,259px and 29 screens at 1440x900 — worse than the dense
   rows they replace. Tuned as below it is 16,922px and 22 screens, against
   16,973px and 22 screens for the rows. Full figures in PROJECT_PROGRESS.md.

   Worth reporting upstream: `data-size="small"` is sized for a docs index of a
   dozen sections, and a 221-item shelf has to go under its floor to hold
   density. That is a gap in the size scale, not a fault in this page. */
.glossary-shelf {
  /* 24px between covers rather than the shipped 24px at every breakpoint is
     the cheapest 1,700px on the page: 73 gaps down a 74-row shelf. */
  --book-shelf-gap: var(--space-l);
}

.glossary-shelf .book-cover[data-size="small"] {
  /* Below the component's own 170px floor. Justified by the measurement above:
     at 170px this page is 18,746px and 25 screens, a regression against the
     rows. */
  --book-cover-min-height: 140px;

  /* 28px is display type for a shelf of ten. At 221 covers a long term wraps
     to three lines and drags its whole row with it. */
  --book-cover-title-size: var(--font-l);

  --book-cover-body-padding: var(--space-m);
}

/* Two lines maximum on the one-liner. Not a re-skin and not an override — the
   component sets colour, size and margin on this element and says nothing
   about line count, so this adds a constraint rather than replacing a rule.
   It is the same clamp the dense rows carried, for the same reason: the
   descriptions run to 143 characters, and one long one does not just make its
   own cover tall, it stretches the two beside it. Unclamped costs 2,485px more
   page. The full text is on the entry page.

   Stated as a difference rather than a pair of absolutes on purpose: the page
   total moves whenever chrome is added — the footer put 90px on every variant —
   and a comparison written as two totals goes stale the first time it does. */
.glossary-shelf .book-cover-description {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* -- Entries, narrow --
   The component collapses every shelf to one column at 768px, which is right
   for a shelf of ten and wrong for one of 221. Measured at a true 375px phone:
   one column is 53 screens, against about 35 screens for the dense rows this
   replaces — the only width at which covers were genuinely worse. Two columns
   takes it to 23,540px and 31 screens, so the rebuild is ahead of the rows at
   every width rather than trading desktop against phone.

   Both selectors are listed for the same reason the component lists both: a
   media query adds no specificity, so `[data-cols="3"]` at (0,3,0) would
   otherwise keep winning inside it.

   All of this is verified in a 375px iframe, never a 375px window — Chrome
   headless clamps to a 500px minimum and a narrower --window-size crops the
   screenshot rather than rendering narrow, which looks exactly like an
   overflow bug. No cover overflows its box at 375px; that was checked per
   element rather than by trusting document scrollWidth, which a clipping
   ancestor can hide. */
@media (max-width: 768px) {
  .book-shelf.glossary-shelf,
  .book-shelf.glossary-shelf[data-cols="3"] {
    --book-shelf-columns: 2;
  }
}

/* One step down again on a phone, where two columns leave about 163px each and
   the longest terms are 30 characters. 18px keeps the title a rung above the
   16px description; 16px was measured and is denser still, at 29 screens, but
   a title the same size as the body text stops reading as a title.

   Note this replaces the component's own title step rather than adding to it.
   Its 959px rule is (0,2,0) and everything here is (0,3,0), so the tuned sizes
   above already win at every width — the step is re-implemented on this page's
   own scale, not defeated by accident. */
@media (max-width: 479px) {
  .glossary-shelf .book-cover[data-size="small"] {
    --book-cover-title-size: var(--font-m);
  }
}

/* -- Tag filters --
   The disclosure itself takes only alignment. The design system indents both
   summary and .disclosure-content so a disclosure reads as a nested block; here
   it is a sibling of the search field in the same column, so the indent would
   put the chips and the summary on two different left edges. */
.glossary-filters > summary,
.glossary-filters > .disclosure-content {
  padding-inline: 0;
}

.glossary-filters > .disclosure-content {
  /* The system's line-height for disclosure prose is generous; the content
     here is a wrapped row of chips, which brings its own spacing. */
  line-height: var(--line-height-s);
}

/* .tag ships without a selected state, so the active treatment is added here.
   Filters are <button> elements for keyboard and screen reader semantics; these
   rules undo the button reset that would otherwise fight .tag. */
.glossary-filter {
  cursor: pointer;
  border: none;
  font-family: inherit;
  transition: background-color 0.2s, color 0.2s;
}

.glossary-filter:hover {
  color: var(--text-primary);
  background-color: var(--background-faded);
}

.glossary-filter:focus-visible {
  outline: 1px solid var(--input-focus);
  outline-offset: 2px;
}

.glossary-filter.is-active {
  color: var(--background-primary);
  background-color: var(--text-primary);
}


/* -- Loading state --
   Skeletons stand in for covers, so the page never reads as finished-but-empty
   and does not lurch when the real ones land. The wrapper in index.html is a
   real `.book-shelf` with the same `data-cols`, so the grid, the gap and the
   breakpoint all come from the component and cannot drift from it.

   Only the cover's own box is restated: the same minimum height and border as
   `data-size="small"`, centred content. This is a placeholder shaped like the
   component, not a second copy of it — a skeleton must not be a `.book-cover`,
   because that class carries hover, focus and cursor treatment for something
   there is nothing to click. */
.glossary-skeleton-cover {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-s);

  /* Matches the tuned --book-cover-min-height above, not the component's
     shipped 170px — a skeleton that stands 30px taller than the thing it
     stands in for reintroduces the lurch it exists to prevent. */
  min-height: 140px;
  padding: var(--space-m);
  border: var(--border-s) solid var(--border-faded);
}

/* .skeleton-line ships as a shape with no height — the design system sizes
   only its named variants (.skeleton-label, .skeleton-input) and leaves the
   plain line to the caller. Unsized it collapses to 0px and the skeleton shows
   nothing but its label, which is exactly the finished-but-empty look the
   loading state exists to avoid. */
.glossary-skeleton-cover .skeleton-line,
.entry-skeleton .skeleton-line:not(.skeleton-label) {
  height: 0.875rem;
}

/* Proportioned like the title and one-liner they stand in for: a taller bar
   for the name, a longer thinner one for the description.

   Both selectors carry .skeleton-line deliberately. Without it these are
   (0,1,0) against the (0,2,0) rule above and the heights silently lose — the
   same specificity trap .az-group[id] documents further up. */
.skeleton-line.glossary-skeleton-name {
  height: 1.5rem;
  width: 60%;
}

.skeleton-line.glossary-skeleton-description {
  width: 85%;
}


/* ------ 2c. ENTRY DETAIL ------ */
/* entry.html — one entry, reached from a card on the index. */

/* The one-liner, promoted to a standfirst under the name. Left-aligned rather
   than reusing .docs-hero, because this sits at the top of a reading column
   and the kit's hero is centred display type for an index page. */
.entry-standfirst {
  font-size: var(--font-l);
  line-height: var(--line-height-l);
  color: var(--text-secondary);
}

/* `sharp` does not run on Workers, so uploads have no resized variants and
   arrive at whatever size they were uploaded. Capping the width is the whole
   of the responsive behaviour; height: auto keeps the ratio once it caps. */
.entry-image {
  display: block;
  max-width: 100%;
  height: auto;
}

/* -- Prose from markdown --
   Deliberately not a .block. Blocks zero margin-block on their direct
   children, which is right for composed layout and wrong for prose: rendered
   markdown is a stream of h2/p/ul/pre that needs the design system's own
   element rhythm. A plain div preserves it. */
.entry-prose {
  /* An unbroken token — usually a bare URL in scraped source — is the common
     cause of a page that scrolls sideways on a phone. `anywhere` rather than
     `break-word` because only `anywhere` shrinks the min-content width, which
     is what a grid child is sized against. */
  overflow-wrap: anywhere;
}

.entry-prose > *:first-child {
  margin-top: 0;
}

.entry-prose > *:last-child {
  margin-bottom: 0;
}

/* code ships with white-space: nowrap, which suits a UI label and not a
   sentence — a long token inside a paragraph would push the page sideways.
   Code blocks are excluded: pre scrolls on its own and should not reflow. */
.entry-prose :not(pre) > code {
  white-space: normal;
}

/* -- The right-hand rail --
   Table of contents on top, related entries beneath it, in one sticky column.
   Both are lists of links to somewhere else, so they share .toc-link rather
   than introducing a second treatment two inches apart. */
.docs-toc .toc-list + .toc-header,
.docs-toc .toc-empty + .toc-header {
  margin-top: var(--space-xl);
}

/* Sub-headings step in, so the rail shows the shape of the entry rather than a
   flat list. The kit's .toc-link has one fixed indent because docs pages index
   h2 only; entry prose can legitimately go a level deeper. */
.toc-item[data-level="3"] .toc-link {
  padding-left: var(--space-2xl);
}

/* -- Entry loading state --
   Shaped like the entry it stands in for: a tag row, the name, then body
   lines, so the layout does not lurch when the real content lands. */
.entry-skeleton-title {
  height: 2.5rem;
  width: 60%;
}

.entry-skeleton-short {
  width: 45%;
}


/* ------ 2d. DARK MODE TOGGLE (icon swap) ------ */
/* Icons injected by assets/js/theme-toggle.js; the framework no
   longer ships these rules, so they live here alongside the script. */
.dark-mode-toggle .dark-mode-icon-dark {
  display: none;
}

[data-theme="dark"] .dark-mode-toggle .dark-mode-icon-light {
  display: none;
}

[data-theme="dark"] .dark-mode-toggle .dark-mode-icon-dark {
  display: flex;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .dark-mode-toggle .dark-mode-icon-light {
    display: none;
  }

  :root:not([data-theme]) .dark-mode-toggle .dark-mode-icon-dark {
    display: flex;
  }
}
