/* ==========================================================================
   HOUSE STYLE — site.css
   The LAN site's furniture: top bar, sidebar, search, landing page.

   ⚠ THIS FILE IS NEVER LOADED BY THE PDF, AND THAT IS THE POINT.

   `_pdf()` in build.py inlines exactly base.css + arabic.css + print.css, in
   that order. Everything in here — the whole navigation shell — is therefore
   structurally incapable of reaching the printed manual. Chrome CSS put into
   base.css would be parsed by WeasyPrint on every build and would sit one
   `body` class away from changing a page that is already correct.

   The same rule as base.css applies throughout: logical properties for
   anything that mirrors (margin, padding, border, inset, text-align),
   physical only for size. The sidebar lands on the right in Arabic because
   the grid follows `dir`, not because anything here says "right".
   ========================================================================== */

:root {
  --topbar-h:     3.5rem;
  --sidebar-w:    18rem;

  /* ⚠ THE CONTENT COLUMN IS A FIXED WIDTH, DELIBERATELY, AND IT IS LOAD-BEARING.

     A screenshot's legibility is arithmetic: the image column comes out of
     this number, and `docs shots` uses it to decide which shots are readable
     inline and which have to be re-cropped. A content column that grows with
     the window would make that a range instead of a number, and "this shot is
     unreadable" would depend on the reader's monitor.

     CHANGING THIS INVALIDATES THE SHOT AUDIT. `INLINE_COLUMN_CSS_PX` in
     factory/legibility.py is derived from it and carries the same warning —
     change one, recompute the other, and re-run `docs shots`. */
  --content-max:  74rem;
  --shell-inner:  calc(var(--sidebar-w) + var(--content-max));
}

/* --------------------------------------------------------------------------
   THE SHELL

   `body` itself is the grid — there is no wrapper element. That is not
   golfing: the PDF builder slices the unit template on the literal string
   `<main class="page">` and reassembles the pieces, so every wrapper opened
   before `<main>` and closed after it would have to survive that slice. With
   no wrapper there is nothing to survive.
   -------------------------------------------------------------------------- */

/* FOUR COLUMNS, AND THE OUTER TWO HOLD NOTHING.

   The reading area is a fixed width and has to sit in the middle of the
   window. Capping the whole grid instead — `max-width` on the body — centres
   the reading area but takes the top bar with it, and a bar that stops short
   of both edges makes the page read as a box floating in an empty room. That
   is the "unfinished" the right-hand void was.

   So the rails are grid columns that absorb whatever the window has spare.
   The bar spans all four and reaches both edges; the sidebar and the content
   sit in the middle two and are centred by construction. No wrapper element,
   which matters because anything opened before `<main class="page">` and
   closed after it has to survive the PDF builder's slice. */
body.has-chrome {
  display: grid;
  grid-template-columns:
    minmax(0, 1fr)                        /* rail */
    var(--sidebar-w)
    minmax(0, var(--content-max))
    minmax(0, 1fr);                       /* rail */
  /* Three rows: bar, content, footer. The content row takes `1fr`, so on a
     page with little on it the footer is pushed to the bottom of the window
     instead of stopping halfway down with nothing under it. */
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

/* A page with no sidebar must not reserve the sidebar's column — the notice
   the Arabic edition serves was sitting 18rem right of centre because the
   track was still there, empty. Both variables, because `--shell-inner` is
   resolved where it is declared and does not see an override made here. */
body.is-bare { --sidebar-w: 0rem; --shell-inner: var(--content-max); }

body.has-chrome > .topbar  { grid-column: 1 / -1; }
body.has-chrome > .sidebar { grid-column: 2; }

/* The content fills its track exactly, so there is no leftover slack between
   the sidebar and the text — the gap the reader sees is this padding and
   nothing else. base.css's 62rem cap is for the PDF and is overridden here.  */
body.has-chrome > .page {
  grid-column: 3;
  max-width: none;
  margin-inline: 0;
  padding-inline: 3rem;
}

/* The page's bottom edge. Carries the one thing a reader might want that the
   site cannot give them — the printable copy — so it earns its space rather
   than merely filling it. */
body.has-chrome > .pagefoot {
  grid-column: 3;
  grid-row: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
  margin-inline: 3rem;
  padding-block: 1.25rem 2.5rem;
  border-block-start: 1px solid var(--rule);
  color: var(--ink-faint);
  font-size: 0.85rem;
}
.pagefoot a { color: var(--ink-soft); text-decoration: none; }
.pagefoot a:hover { color: var(--accent); text-decoration: underline; }
.pagefoot__top::before { content: "↑ "; }

/* Absolutely positioned, so neither becomes a grid item and neither opens a
   row of its own. */
.skip, .skip-target { position: absolute; }

.skip {
  inset-inline-start: 0.5rem;
  inset-block-start: -3rem;
  z-index: 60;
  background: var(--accent);
  color: #fff;
  padding: 0.6rem 1rem;
  border-radius: var(--radius);
  text-decoration: none;
  transition: inset-block-start 0.12s;
}
.skip:focus { inset-block-start: 0.5rem; }

/* --------------------------------------------------------------------------
   TOP BAR
   -------------------------------------------------------------------------- */

.topbar {
  position: sticky;
  inset-block-start: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 1rem;
  height: var(--topbar-h);
  /* Full-bleed background and border, but the CONTENTS line up with the
     sidebar and the text below. The padding is the rail width, recomputed
     from the same two variables the grid uses, so the bar cannot drift out of
     alignment with the columns it sits above. */
  padding-inline: max(1.25rem, calc((100% - var(--shell-inner)) / 2));
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  backdrop-filter: blur(10px);
  border-block-end: 1px solid var(--rule);
}

.topbar__home {
  font-weight: 650;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* The sidebar disclosure button. Hidden on desktop, where the sidebar is
   simply present. */
.topbar__toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--ground);
  color: var(--ink);
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   SEARCH
   -------------------------------------------------------------------------- */

/* The bar is now title | search | language | theme. `margin-inline-start:
   auto` on the search pushes everything after it to the far end, which is
   what `.topbar__views` used to do before it moved to the sidebar. */
.search { position: relative; flex: 0 1 22rem; margin-inline-start: auto; }

.search__field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--ground-sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding-inline: 0.6rem;
}
.search__field:focus-within { border-color: var(--accent); background: var(--ground); }

.search__icon { flex: none; color: var(--ink-faint); display: flex; }

.search input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 0.92rem;
  padding-block: 0.5rem;
  outline: none;
}
/* Safari draws its own clear button on type=search and it fights the layout. */
.search input::-webkit-search-decoration,
.search input::-webkit-search-cancel-button { -webkit-appearance: none; }

.search__key {
  flex: none;
  font-size: 0.7rem;
  color: var(--ink-faint);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.05em 0.35em;
  background: var(--ground);
}
.search__field:focus-within .search__key { display: none; }

.search__results {
  position: absolute;
  inset-inline: 0;
  inset-block-start: calc(100% + 0.4rem);
  max-height: min(28rem, 70vh);
  overflow-y: auto;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgb(0 0 0 / 0.16);
  z-index: 50;
  padding: 0.3rem;
}
.search__results[hidden] { display: none; }

.search__hit {
  display: block;
  padding: 0.55rem 0.7rem;
  border-radius: 4px;
  text-decoration: none;
  color: inherit;
}
.search__hit:hover,
.search__hit.is-active { background: var(--accent-soft); }

.search__unit { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-faint); }
.search__head { font-weight: 650; font-size: 0.95rem; margin-block: 0.1rem; }
.search__snip { font-size: 0.85rem; color: var(--ink-soft); }
.search__snip mark, .search__head mark {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  color: inherit;
  border-radius: 2px;
  padding: 0 0.1em;
}

.search__empty { padding: 1rem 0.7rem; color: var(--ink-faint); font-size: 0.9rem; }
.search__count {
  padding: 0.35rem 0.7rem 0.5rem;
  font-size: 0.75rem;
  color: var(--ink-faint);
  border-block-end: 1px solid var(--rule);
  margin-block-end: 0.3rem;
}

/* A match the reader was sent to, marked in the page itself. */
mark.hit {
  background: color-mix(in srgb, var(--accent) 25%, transparent);
  color: inherit;
  border-radius: 2px;
}

@keyframes flash {
  from { background: var(--accent-soft); }
  to   { background: transparent; }
}
.step.is-target, .step__prose.is-target { animation: flash 1.8s ease-out; border-radius: var(--radius); }

/* --------------------------------------------------------------------------
   LANGUAGE SWITCHER AND THEME BUTTON
   -------------------------------------------------------------------------- */

.langs { display: flex; align-items: center; gap: 0.15rem; flex: none; }

.langs a {
  padding: 0.3rem 0.55rem;
  border-radius: 4px;
  font-size: 0.85rem;
  color: var(--ink-faint);
  text-decoration: none;
  white-space: nowrap;
}
.langs a:hover { background: var(--ground-sunk); color: var(--ink); }
.langs a[aria-current] { color: var(--ink); font-weight: 650; }

/* An edition that does not exist yet is still listed, and still reachable —
   it leads to a page that says so. Dimming it sets the expectation before the
   click rather than after it. */
.langs__unwritten { opacity: 0.55; text-decoration: underline dotted; text-underline-offset: 3px; }

.theme {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--ground);
  color: var(--ink-soft);
  cursor: pointer;
}
.theme:hover { color: var(--ink); border-color: var(--accent); }

/* One icon at a time. Unstamped — before viewer.js has run, or with no
   JavaScript at all — the page IS following the system, so `auto` is what an
   unstamped button should show. */
.theme svg { display: none; }
.theme .theme__auto,
.theme[data-state="auto"] .theme__auto { display: block; }
.theme[data-state="auto"] .theme__light,
.theme[data-state="auto"] .theme__dark { display: none; }
.theme[data-state="light"] .theme__auto { display: none; }
.theme[data-state="light"] .theme__light { display: block; }
.theme[data-state="dark"] .theme__auto { display: none; }
.theme[data-state="dark"] .theme__dark { display: block; }

/* Nothing to cycle with scripting off. */
.no-js .theme { display: none; }

/* --------------------------------------------------------------------------
   OPENING A SCREENSHOT FULL SIZE

   The inline picture is about a third of the size the application was
   photographed at. This is where the reader gets the rest.
   -------------------------------------------------------------------------- */

.shot--zoomable { cursor: zoom-in; }
.shot--zoomable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A quiet affordance. The picture is the subject; this only has to be enough
   that a reader knows there is more of it. */
.shot--zoomable::after {
  content: "";
  position: absolute;
  inset-block-start: 0.5rem;
  inset-inline-end: 0.5rem;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 4px;
  background: rgb(0 0 0 / 0.55) no-repeat center/0.9rem 0.9rem
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='7' cy='7' r='4.6'/%3E%3Cpath d='M10.4 10.4L14 14M7 5v4M5 7h4'/%3E%3C/svg%3E");
  opacity: 0;
  transition: opacity 0.12s;
  pointer-events: none;
}
.shot--zoomable:hover::after,
.shot--zoomable:focus-visible::after { opacity: 1; }

.lb-open { overflow: hidden; }        /* the page behind must not scroll too */

.lb {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  background: rgb(9 11 14 / 0.94);
  color: #f2f4f7;
}

.lb__bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.6rem 0.9rem;
  border-block-end: 1px solid rgb(255 255 255 / 0.14);
  font-size: 0.9rem;
}
.lb__title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.lb__close {
  flex: none;
  width: 2.1rem;
  height: 2.1rem;
  border: 1px solid rgb(255 255 255 / 0.24);
  border-radius: var(--radius);
  background: none;
  color: inherit;
  font-size: 0.95rem;
  cursor: pointer;
}
.lb__close:hover { background: rgb(255 255 255 / 0.12); }

.lb__scroll {
  flex: 1;
  overflow: auto;
  padding: 1rem;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  cursor: zoom-out;
}

/* ⚠ THE STAGE IS THE IMAGE'S BOX AND NOTHING ELSE.

   Pins and rings inside it are positioned as percentages of this element, so
   any difference between its box and the picture's box is a proportional
   error in every marker. Only the image is in the flow here — the caption and
   the close button are outside — and `display: block` on the image removes
   the inline baseline gap that would otherwise add a few pixels of height
   below it and push every pin up.

   Same rule, and the same reason, as the note in print.css. */
.lb__stage {
  position: relative;
  display: block;
  flex: none;
  line-height: 0;
}
.lb__stage img { display: block; width: 100%; height: auto; }

/* The markers keep their measured percentages. They are drawn slightly larger
   here only because the picture is: a 1.55rem pin over a 486px image and the
   same pin over a 1440px one are not the same weight on the page. */
.lb__stage .shot__pin { width: 1.75rem; height: 1.75rem; font-size: 0.9rem; line-height: 1.75rem; }
.lb__stage .shot__ring { border-width: 3px; }

@media (max-width: 900px) {
  .langs a { padding: 0.3rem 0.4rem; font-size: 0.8rem; }
  .lb__scroll { padding: 0.4rem; }
}

/* --------------------------------------------------------------------------
   SIDEBAR
   -------------------------------------------------------------------------- */

/* THE RULE RUNS THE FULL HEIGHT OF THE PAGE; THE NAV INSIDE IT STICKS.

   These are two different jobs and they were being asked of one element. A
   sticky box has to be `align-self: start` — it needs somewhere to travel
   within its grid area — but that also shrinks it to its own content, so the
   border stopped wherever the list of chapters happened to end, usually a
   third of the way down. It read as a divider that had given up.

   So the OUTER element stretches to the grid row and carries the rule, and an
   INNER element does the sticking. Nothing else changes. */
.sidebar {
  grid-row: 2 / -1;   /* the rule runs past the footer, to the very bottom */
  border-inline-end: 1px solid var(--rule);
  font-size: 0.9rem;
  min-width: 0;
}

.sidebar__inner {
  position: sticky;
  inset-block-start: var(--topbar-h);
  max-height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  overscroll-behavior: contain;   /* scrolling the nav must not scroll the page */
  padding-block: 1.75rem 3rem;
  padding-inline: 0 1rem;
}

.sidebar__label {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-faint);
  margin-block: 0 0.5rem;
}

.sidebar__group { margin-block-end: 1.6rem; }

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

.sidebar__list li {
  margin: 0;
  /* The spine the current-page marker sits on. */
  border-inline-start: 2px solid var(--rule);
}

.sidebar__link {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.32rem 0.65rem;
  color: var(--ink-soft);
  text-decoration: none;
  border-radius: 0 4px 4px 0;
  line-height: 1.35;
  /* A label that wraps turns a list of six into a wall. The short forms come
     from `nav:` in each unit's frontmatter; this is the belt to that braces —
     if a project ships a long one it is clipped rather than allowed to
     reflow the whole column. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar__link:hover { background: var(--ground-sunk); color: var(--ink); }

.sidebar__list li:has(> [aria-current]) { border-inline-start-color: var(--accent); }
.sidebar__link[aria-current] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 650;
}

.sidebar__n {
  flex: none;
  font-size: 0.75rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  min-width: 1.1em;
}
.sidebar__link[aria-current] .sidebar__n { color: inherit; }

/* THE TWO WAYS IN — the first thing in the sidebar, set apart from the
   chapters by a rule, because they are a different kind of thing: a way of
   ordering the manual rather than a place in it. */
.views {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin-block-end: 1.5rem;
  padding-block-end: 1.25rem;
  border-block-end: 1px solid var(--rule);
}

.views__item {
  display: block;
  padding: 0.45rem 0.7rem;
  border-radius: var(--radius);
  color: var(--ink-soft);
  text-decoration: none;
  font-weight: 600;
}
.views__item:hover { background: var(--ground-sunk); color: var(--ink); }
.views__item[aria-current] { background: var(--accent-soft); color: var(--accent); }

/* --------------------------------------------------------------------------
   BREADCRUMB — real links now, so it needs to look like them
   -------------------------------------------------------------------------- */

.crumb a { color: var(--ink-faint); text-decoration: none; }
.crumb a:hover { color: var(--accent); text-decoration: underline; }
.crumb__sep { color: var(--ink-faint); opacity: 0.6; margin-inline: 0.3em; }
/* The separator is a glyph that points along the reading direction, so it has
   to be mirrored by hand — it is content, not layout. */
[dir="rtl"] .crumb__sep { transform: scaleX(-1); display: inline-block; }

/* --------------------------------------------------------------------------
   PREVIOUS / NEXT — the pathnav block that unit.html.j2 already had
   -------------------------------------------------------------------------- */

.pathnav { gap: 0.75rem; }
.pathnav .card { flex: 1 1 0; min-width: min(100%, 14rem); }
.pathnav__next { text-align: end; }
.pathnav .card__sub { display: flex; align-items: center; gap: 0.3rem; }
.pathnav__next .card__sub { justify-content: flex-end; }
.pathnav__prev .card__sub::before { content: "←"; }
.pathnav__next .card__sub::after  { content: "→"; }
[dir="rtl"] .pathnav__prev .card__sub::before { content: "→"; }
[dir="rtl"] .pathnav__next .card__sub::after  { content: "←"; }

/* --------------------------------------------------------------------------
   LANDING PAGE
   -------------------------------------------------------------------------- */

.hero { margin-block-end: 3rem; }
.hero__kicker {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  font-weight: 700;
  margin-block-end: 0.7rem;
}
.hero__title { font-size: clamp(2.1rem, 5vw, 3rem); margin-block: 0 0.8rem; letter-spacing: -0.015em; }
.hero__lede { font-size: 1.15rem; color: var(--ink-soft); max-width: 40rem; margin-block-end: 2rem; }

.facts { display: flex; flex-wrap: wrap; gap: 2.5rem; padding-block: 1.25rem; border-block: 1px solid var(--rule); }
.fact__n { display: block; font-size: 1.9rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.fact__k { font-size: 0.82rem; color: var(--ink-faint); }

.ways { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 1.1rem; margin-block: 2.5rem; }

.way {
  display: flex;
  flex-direction: column;
  padding: 1.5rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--ground);
  text-decoration: none;
  color: inherit;
}
.way:hover { border-color: var(--accent); background: var(--accent-soft); }
.way__title { font-size: 1.25rem; font-weight: 650; margin-block-end: 0.4rem; }
.way__blurb { color: var(--ink-soft); font-size: 0.95rem; margin-block-end: 1rem; }

/* What is actually inside this way in — the path names, or three real
   questions. The landing page stopped listing all six chapters, so this is
   what tells a reader which of the two doors is theirs. */
.way__list {
  list-style: none;
  margin: 0 0 1.2rem;
  padding: 0;
  flex: 1;
  font-size: 0.9rem;
  color: var(--ink-soft);
}
.way__list li {
  padding-block: 0.25rem;
  padding-inline-start: 0.9rem;
  position: relative;
  margin: 0;
}
.way__list li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.85em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--ink-faint);
}

.way__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  padding-block-start: 0.9rem;
  border-block-start: 1px solid var(--rule);
}
.way__go { color: var(--accent); font-weight: 650; font-size: 0.9rem; }
.way__go::after { content: " →"; }
[dir="rtl"] .way__go::after { content: " ←"; }
.way__meta { font-size: 0.8rem; color: var(--ink-faint); }

/* --------------------------------------------------------------------------
   THE TWO VIEWS

   One template renders both, and these are the two presentations it switches
   between. A training path is an ORDER — it is numbered and read downwards.
   The reference index is a LOOKUP — it is a list of questions, scanned. They
   are the same units either way, which is the whole point of the two files.
   -------------------------------------------------------------------------- */

/* Training: an ordered walk. */
.walk { list-style: none; margin: 1.5rem 0 0; padding: 0; counter-reset: walk; }

.walk__item { position: relative; counter-increment: walk; }

.walk__link {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr) auto;
  gap: 0.2rem 1rem;
  align-items: baseline;
  padding: 1rem 0.8rem;
  border-block-end: 1px solid var(--rule);
  text-decoration: none;
  color: inherit;
}
.walk__item:first-child .walk__link { border-block-start: 1px solid var(--rule); }
.walk__link:hover { background: var(--ground-sunk); }

.walk__n {
  grid-row: span 2;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: var(--ground-sunk);
  color: var(--ink-soft);
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1.85rem;
  text-align: center;
}
.walk__link:hover .walk__n { border-color: var(--accent); color: var(--accent); }
.walk__title { font-weight: 650; font-size: 1.05rem; }
.walk__note { grid-column: 2; color: var(--ink-soft); font-size: 0.9rem; }
.walk__meta { grid-row: span 2; color: var(--ink-faint); font-size: 0.8rem; white-space: nowrap; }

/* Reference: a scannable list of questions.

   ⚠ COLUMNS, NOT GRID, AND THAT IS THE POINT.

   A grid with `auto-fill` lays its items out ACROSS and then down, so
   fourteen questions in three tracks read 1-2-3 on the first line and 4-5-6
   on the second. The eye wants to go down a list of questions, and it was
   being sent sideways.

   Multi-column flows top to bottom and then to the next column, which is the
   order the reader is already using. `break-inside: avoid` is what stops a
   card being split across the column boundary, and the vertical rhythm comes
   from each card's own margin rather than a grid `gap` — a gap in one axis
   and a margin in the other is where the uneven spacing came from. */
.asks {
  columns: 21rem;
  column-gap: 1rem;
  margin-block: 1.25rem 2.5rem;
}

.ask {
  display: block;
  break-inside: avoid;
  margin-block-end: 0.7rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--ground);
  text-decoration: none;
  color: inherit;
}
.ask:hover { border-color: var(--accent); background: var(--accent-soft); }
.ask:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ask__q { font-weight: 600; }
.ask__a { font-size: 0.82rem; color: var(--ink-faint); margin-block-start: 0.15rem; }

.group-head { margin-block: 2.5rem 0.4rem; padding-block-end: 0.5rem; border-block-end: 1px solid var(--rule); }
.group-head h2 { margin: 0; font-size: 1.2rem; }
.group-head p { margin: 0.4rem 0 0; color: var(--ink-soft); font-size: 0.95rem; max-width: var(--measure); }

/* --------------------------------------------------------------------------
   COLLAPSIBLE GROUPS — the lookup view only

   The reference page is scanned, not read. Closed, its four or five headings
   fit on one screen; open, sixteen questions push the last heading out of
   sight and the reader is reading a wall again.

   ⚠ IT HAS TO LOOK PRESSED-ABLE. The first version of this styled almost
   nothing and left the browser's own disclosure triangle showing above the
   heading with the count on a line of its own. It worked and it read as
   debris — a reader does not click something that looks like it fell out of
   the page. So each group is now a bordered row with its own hover, the
   count sits quietly on the row, and the chevron is at the end where a
   reader looks for one.

   ⚠ EVERY COLOUR IS A TOKEN, so both themes come out right from one set of
   rules. There is no dark-mode block below and there should never need to be
   — base.css redefines the tokens and this inherits the answer.
   -------------------------------------------------------------------------- */

/* The row itself. `overflow: hidden` keeps the summary's hover background
   inside the rounded corners; without it the tint bleeds past the radius at
   the top two corners and the row looks chipped. */
.group {
  margin-block: 0 0.5rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--ground);
  overflow: hidden;
}

/* ⚠ THE MARKER IS REMOVED THREE WAYS, AND ALL THREE ARE NEEDED.
   `list-style` is what Firefox honours, `::-webkit-details-marker` is
   Safari's, and Chrome drops it once `display` stops being `list-item`.
   Any one alone leaves a stray triangle in one of the three. */
.group-head--toggle {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0;
  padding: 0.85rem 1.05rem;
  border: 0;
  cursor: pointer;
  list-style: none;
  user-select: none;
  transition: background 0.1s ease;
}
.group-head--toggle::-webkit-details-marker { display: none; }
.group-head--toggle::marker { content: ""; }

.group-head--toggle h2 {
  flex: 1 1 auto;
  margin: 0;
  font-size: 1.05rem;
}

/* Quiet, and on the row rather than under it. It is there to tell a reader
   whether opening the group is worth the click, which is a job it can only do
   from beside the heading. */
.group-head__n {
  flex: none;
  font-size: 0.8rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* The chevron, drawn from two borders so it inherits the text colour and
   needs no asset. Down when closed, up when open — a vertical pair rather
   than the right/down pair, because vertical needs no mirroring in Arabic
   and one less RTL rule is one less thing to get wrong. */
.group-head--toggle::after {
  content: "";
  flex: none;
  width: 0.42rem;
  height: 0.42rem;
  border-inline-end: 2px solid currentColor;
  border-block-end: 2px solid currentColor;
  transform: rotate(45deg);
  translate: 0 -0.12rem;
  opacity: 0.5;
  transition: transform 0.12s ease, opacity 0.1s ease;
}
.group[open] > .group-head--toggle::after {
  transform: rotate(225deg);
  translate: 0 0.12rem;
}

/* Hover on the ROW, not on the text. A colour change on a heading is easy to
   miss and says "link"; a tinted row says "press me". */
.group-head--toggle:hover { background: var(--ground-sunk); }
.group-head--toggle:hover h2 { color: var(--accent); }
.group-head--toggle:hover::after { opacity: 1; }

.group-head--toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* The rule under the heading appears only when the group is open, so a closed
   list reads as separate rows rather than one ruled block. */
.group[open] > .group-head--toggle { border-block-end: 1px solid var(--rule); }

.group-head__blurb {
  margin: 0;
  padding: 0.8rem 1.05rem 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
  max-width: var(--measure);
}

/* `.asks` carries its own margin for the uncollapsed page; inside a group the
   row's padding does that job instead. */
.group > .asks {
  margin: 0;
  padding: 0.9rem 1.05rem 0.35rem;
}

/* ⚠ PRINT. The site's PDF is built from unit pages only, so a closed group
   cannot hide anything from it — but a reader may still hit ctrl-P here, and
   four headings is not a page worth printing.

   The mechanism that actually opens them is viewer.js's `beforeprint`
   handler, because setting `open` is the only way that works in every engine.
   The rule below is the second line of defence, for scripting turned off: it
   reveals the content in engines that hide it with `display`, and quietly
   does nothing in those that do not. Deliberately not the only defence. */
@media print {
  .group { border-color: var(--rule); break-inside: avoid; }
  .group-head--toggle { cursor: auto; }
  .group-head--toggle::after { display: none; }
  details.group:not([open]) > *:not(summary) { display: block; }
}

/* --------------------------------------------------------------------------
   NOTICE — a language that has no content yet says so
   -------------------------------------------------------------------------- */

.notice {
  max-width: 38rem;
  margin-block: 3rem;
  padding: 1.75rem;
  border: 1px solid var(--rule);
  border-inline-start: 4px solid var(--warn);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--warn-soft);
}
.notice h1 { font-size: 1.5rem; margin-block: 0 0.6rem; }
.notice p { color: var(--ink-soft); }
.notice p:last-child { margin-block-end: 0; }
.notice__actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-block-start: 1.5rem; }
.notice__actions a {
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--ground);
  text-decoration: none;
  font-size: 0.92rem;
}

.site-note {
  margin-block-start: 3rem;
  padding-block-start: 1.5rem;
  border-block-start: 1px solid var(--rule);
  color: var(--ink-faint);
  font-size: 0.85rem;
}

/* --------------------------------------------------------------------------
   NARROW SCREENS

   The sidebar becomes a disclosure. Below 900px base.css has already stacked
   each step's prose above its screenshot, so the reading order is unchanged —
   only the navigation moves.
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {
  body.has-chrome { display: block; }

  .topbar { padding-inline: 0.9rem; gap: 0.6rem; }
  .topbar__toggle { display: flex; }
  .search { flex: 1 1 auto; }
  .search__key { display: none; }

  .sidebar {
    display: none;
    border-inline-end: 0;
    border-block-end: 1px solid var(--rule);
  }
  .sidebar.is-open { display: block; }
  .sidebar__inner {
    position: static;
    max-height: none;
    overflow: visible;
    padding: 1.25rem 0.9rem;
  }

  body.has-chrome > .page { padding-inline: 1.25rem; }
  body.has-chrome > .pagefoot { margin-inline: 1.25rem; }
}

/* With no JavaScript there is nothing to open the disclosure with, so the
   sidebar stays expanded and the button that would do nothing is hidden. */
.no-js .sidebar { display: block; }
.no-js .topbar__toggle { display: none; }
.no-js .search { display: none; }

@media print {
  .topbar, .sidebar, .skip, .search, .pathnav, .pagefoot { display: none !important; }
  body.has-chrome > .page { padding-inline: 0; }
  body.has-chrome { display: block; }
}
