/* ============================================================================
   archive.css — layout for the directory listing, the state pages and the
   three taxonomy archives. Depends on tokens.css + base.css, and is the
   sibling of profile.css: one responsive sheet, desktop-first, same
   breakpoints (1180 gutters/rail, 980 rail drops, 640 mobile artboard).

   NO NEW COLOURS. Every value below is a token already declared in
   tokens.css — this sheet only arranges the profile page's existing
   vocabulary (surface + line + radius-xl cards, sage primary, sand wells,
   Newsreader headings, Geist Mono eyebrows) into a results grid.

   The one interactive element is <details>, used for the facet accordions
   exactly as profile.css uses it for the bio: no JavaScript is shipped.
   ========================================================================== */

.page { max-width: var(--page-max); margin: 0 auto; }

/* -- Page head ------------------------------------------------------------- */
.archive { padding: 0 var(--pad); }

.archive-head {
  margin: 28px 0 26px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--line);
}
.archive-head .eyebrow { margin-bottom: 10px; }
.archive-head h1 {
  font-size: 44px;
  line-height: 1.06;
  letter-spacing: -0.022em;
  max-width: 20ch;
}
.archive-intro {
  margin-top: 14px;
  max-width: var(--measure);
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-3);
  text-wrap: pretty;
}

/* Active filters — each one a link that removes itself. */
.activebar {
  margin-top: 20px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 12px;
}
.activebar-t { flex-shrink: 0; }
.activebar-l { gap: 8px; }
.opt-x { gap: 6px; }
.opt-x:hover { background: var(--sage-tint-2); color: var(--sage-deep); text-decoration: none; }
/* The facet name in front of the value. With thirteen filters "Anxiety" alone
   no longer says which of them it came off, and two chips reading "Female" and
   "Spanish" beside each other are a guessing game. Mono + muted so the value
   still reads as the thing. */
.opt-x-k {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.65;
}
.opt-x-g { font-size: 15px; line-height: 1; opacity: 0.7; }
.activebar-clear { font-size: 13px; color: var(--ink-3); }
.activebar-clear:hover { color: var(--sage-deep); }

/* -- Two-column shell ------------------------------------------------------ */
/* Filters lead in the DOM as well as visually: they are the page's controls,
   and a keyboard user should reach them before 24 result cards. On mobile the
   accordions collapse to ~6 rows, so leading with them costs one swipe. */
.archive-layout {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  gap: var(--gap);
  align-items: start;
}
.archive-layout.no-rail { grid-template-columns: minmax(0, 1fr); }

/* -- Filter rail -----------------------------------------------------------
   Ten <details> sections, thirteen filters, ~350 options, and still no
   JavaScript. Three structural jobs this sheet does that the markup cannot:

     · the rail is its own scroller (sticky, capped to the viewport) so a long
       page of filters never runs past the results beside it;
     · .fsel is sticky INSIDE that scroller, so the applied filters stay on
       screen while the other 340 options scroll past;
     · at <=980px the sections become a two-column grid of summary rows, and an
       [open] one spans both columns — an attribute selector, so the responsive
       behaviour needs no script even though <details open> is server-decided.
   -------------------------------------------------------------------------- */
.filters {
  position: sticky;
  top: 24px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  padding: 8px 16px 12px;
}
/* The mobile-only control that collapses everything below it. Above 980px the
   rail is a column of the page layout and there is nothing to collapse, so the
   button is not in the flow at all — see filterPanel() for why the disclosure
   holds the button and not the rail. */
.ftoggle { display: none; }

.filters-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 4px 6px;
}
.filters-title { padding: 0; }
/* An in-rail skip link. The page-level one is the first thing in the document
   and is long gone by the time somebody has scrolled ten filter groups. */
.filters-skip { font-size: 12px; color: var(--ink-3); }
.filters-skip::after { content: " ↓"; }
.filters-skip:hover { color: var(--sage-deep); }

/* Applied filters, pinned to the top of the rail's own scroller. */
.fsel {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 -4px 4px;
  padding: 8px 4px 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.fsel-h {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
.fsel-t { margin: 0; }
.fsel-clear { font-size: 12px; color: var(--ink-3); }
.fsel-clear:hover { color: var(--sage-deep); }
.fsel-l { gap: 6px; }
.fsel-l .chip { font-size: 11.5px; padding: 4px 8px; }

.fgroup { border-top: 1px solid var(--line-soft); }
.fgroups > .fgroup:first-child { border-top: none; }

.fgroup-sum {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 4px;
  cursor: pointer;
  list-style: none;
}
.fgroup-sum::-webkit-details-marker { display: none; }
.fgroup-sum::marker { content: ""; }
.fgroup-sum:hover .fgroup-title { color: var(--sage-deep); }

.fgroup-title {
  font-family: var(--font-sans);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.3;
  color: var(--ink);
  margin-right: auto;
}
/* Count of applied selections in a collapsed group. */
.fgroup-on {
  display: grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--sage);
  color: var(--on-sage);
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1;
}
.fgroup-chev { display: inline-flex; color: var(--ink-3); font-size: 15px; }
.fgroup[open] > .fgroup-sum .fgroup-chev { transform: rotate(180deg); }

.fopts { display: flex; flex-direction: column; gap: 1px; padding-bottom: 4px; }
.fgroup > .fopts:last-child { padding-bottom: 10px; }

/* The folded tail of a long facet. The head above it holds the busiest ten and
   the selection; this holds everything else, so opening it really does show
   all N — it is a fold, not a truncation, and every option is in the DOM
   either way. */
.fmore { padding-bottom: 8px; }
.fmore-sum {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  margin-top: 2px;
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;
  color: var(--sage-deep);
  font-size: 12.5px;
}
.fmore-sum::-webkit-details-marker { display: none; }
.fmore-sum::marker { content: ""; }
.fmore-sum:hover { background: var(--bg-2); }
.fmore-l { margin-right: auto; }
.fmore[open] > .fmore-sum .fgroup-chev { transform: rotate(180deg); }
/* Only the tail scrolls, and only when it is open — the head, the selection
   and the "show all" control stay where they were put. */
.fopts--tail {
  max-height: 300px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 2px 4px 2px 0;
  border-left: 2px solid var(--line-soft);
  margin-left: 6px;
  padding-left: 6px;
}
/* The selected option, hoisted out of the fold into the head. */
.fopt-pin {
  padding-bottom: 5px;
  margin-bottom: 5px;
  border-bottom: 1px solid var(--line-soft);
}

.opt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
  border-radius: var(--radius);
  font-size: 13.5px;
  line-height: 1.35;
  color: var(--ink-2);
}
.opt:hover { background: var(--bg-2); color: var(--ink); text-decoration: none; }
.opt.is-on { background: var(--sage-tint); color: var(--sage-deep); font-weight: 500; }
.opt.is-on:hover { background: var(--sage-tint-2); }
/* An option with nobody behind it. Not a link — see optionRow in listing.js.
   ⚠ `--ink-3`, NOT `--ink-4`, AND THE DIFFERENCE IS 2.26:1 vs 5.37:1. This row
   used to be painted in the placeholder token, which measured 2.26:1 on the
   surface — under half the AA floor, on text that is not decoration: "Added in
   the last 30 days · First appeared in our roster · 0" is how you learn that a
   filter has nothing behind it, and it was effectively invisible.

   WCAG 1.4.3 does exempt an INACTIVE user-interface component, and that
   exemption is why this passed review for as long as it did. It does not apply
   here: `.opt.is-off` is a <span>, not a disabled control — nothing in the
   markup or the accessibility tree says "disabled", so a reader gets no
   compensating signal, only faint grey.

   "Unavailable" is still carried, and by things that do not depend on reading
   the text at all: the checkbox drops to half opacity, the count reads 0, and
   it is not a link. `--ink-3` is visibly lighter than the `--ink` of a live
   option while staying legible. See tokens.css. */
.opt.is-off { color: var(--ink-3); cursor: default; }
.opt.is-off .opt-box { opacity: 0.5; }
.opt.is-off .opt-note,
.opt.is-off .opt-n { color: var(--ink-3); }

/* Square = a toggle you can combine, round = one-of-a-set. Decorative: the
   link's own text plus aria-current carries the state. */
.opt-box {
  width: 14px; height: 14px;
  flex-shrink: 0;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--surface);
}
.opt-box--one { border-radius: 50%; }
.opt.is-on .opt-box { background: var(--sage); border-color: var(--sage); box-shadow: inset 0 0 0 2px var(--surface-2); }

.opt-l { min-width: 0; }
/* The one filter whose name does not explain itself. */
.opt-note {
  display: block;
  font-size: 11px;
  line-height: 1.3;
  color: var(--ink-3);
}
.opt.is-on .opt-note { color: var(--sage-deep); opacity: 0.8; }
.opt-n {
  margin-left: auto;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
/* A selected row shows × where its number would be: its link CLEARS the
   filter, and no facet count describes the wider set that opens. */
.opt.is-on .opt-x-g { margin-left: auto; }

/* -- Results --------------------------------------------------------------- */
.results { min-width: 0; }
.results-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}
.results-count { font-size: 21px; letter-spacing: -0.014em; }
.results-page { font-size: 13px; color: var(--ink-3); font-family: var(--font-mono); }

.rgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr));
  gap: 16px;
}

/* -- Therapist card -------------------------------------------------------- */
.tcard {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
}
.tcard:hover { box-shadow: var(--shadow-md); }

.tcard-head { display: flex; align-items: flex-start; gap: 13px; min-width: 0; }

/* The tinted monogram tile. It was the stand-in for a photo we did not have;
   it is now the layer UNDER the photo we do, so it is still what the card shows
   for a row with no portrait and what an image failure uncovers. */
.tavatar {
  position: relative;
  width: 46px; height: 46px;
  flex-shrink: 0;
  border-radius: 13px;
  background: var(--sage-tint);
  color: var(--sage-deep);
  display: grid;
  place-items: center;
}
.tavatar .ini {
  font-family: var(--font-serif);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
}
/* `border-radius: inherit` rather than `overflow: hidden` on the tile: the live
   dot deliberately overhangs the corner by 2px, and clipping the tile would
   clip the dot. The tile's fixed 46px box means the image can never move
   anything on the card, whatever the source's intrinsic size is. */
.tavatar .photo {
  position: absolute;
  inset: 0;
  /* Explicit, not inherited from DOM order — see the note on `.avatar .photo`
     in profile.css. Without it a stale stylesheet paints the initials over the
     face on every card in the grid. */
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
  background: transparent;
  border: 0;
}
.tavatar .live {
  position: absolute;
  z-index: 2;
  right: -2px; bottom: -2px;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--success);
  box-shadow: 0 0 0 2px var(--surface);
}

.tcard-id { min-width: 0; }
.tname { font-size: 20px; line-height: 1.2; letter-spacing: -0.014em; }
.tname a { color: var(--ink); }
.tname a:hover { color: var(--sage-deep); text-decoration: underline; }
.tname .cred { color: var(--sage-deep); font-weight: 500; }

.tsub {
  margin-top: 6px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink-2);
}
.tsub li { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.tsub li + li::before {
  content: "";
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--ink-4);
  flex-shrink: 0;
}
.tsub .lic { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-3); }

.tcard-chips { gap: 6px; }

/* Mini availability strip — the compact form of the profile's 30-day strip.
   Cells are aria-hidden; the visually-hidden sentence above them is the
   accessible version, generated from the same array. */
.mini-strip {
  --mcell-h: 13px;
  --mcell-w: 13px;              /* cap, not width — see .mcell and base.css .cell */
  display: flex;
  gap: 2px;
  align-items: stretch;
  justify-content: flex-start;
}
.mcell {
  flex: 1 1 0;
  min-width: 0;
  /* Fallback for the same reason as `.cell` in base.css: an undefined custom
     property does not fall back to "no cap", it invalidates the declaration. */
  max-width: var(--mcell-w, var(--mcell-h, 13px));
  height: var(--mcell-h);
  border-radius: 3px;
  border: 1px solid var(--line);
  background: transparent;          /* no data / waitlist / full */
}
.mcell.is-on { background: var(--sage); border-color: var(--sage); }
/* Not observed. Same distinction the profile strip draws: a day with no check
   is not a day with no availability. */
.mcell.is-none { border-style: dashed; border-color: var(--line-soft); opacity: 0.7; }
.mcell.is-last, .mcell.is-today { outline: 1.5px solid var(--ink); outline-offset: 1.5px; }
.mini-cap {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.01em;
  color: var(--ink-3);
}
/* An honest blank, not a row of empty cells: an empty cell means "waitlist or
   full", so drawing 30 of them would assert 30 closed days we never observed. */
.mini-none {
  padding: 10px 12px;
  font-size: 12.5px;
  font-style: italic;
  color: var(--ink-3);
  background: var(--bg-2);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

.tcard-tags { gap: 6px; }
.tmore {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  color: var(--ink-3);
  padding: 5px 2px;
}

.tmeta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 12px;
  font-size: 12.5px;
  color: var(--ink-3);
}
.tmeta li { display: inline-flex; align-items: center; gap: 12px; }
.tmeta li + li::before {
  content: "";
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--ink-4);
  flex-shrink: 0;
}

.tcard-foot {
  margin-top: auto;            /* CTAs line up across a row of uneven cards */
  padding-top: 4px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.tcard-foot .btn { flex: 1 1 auto; }
.tcard-more { font-size: 13px; font-weight: 500; color: var(--sage-deep); white-space: nowrap; }

/* -- Empty results --------------------------------------------------------- */
.noresults {
  padding: 44px 28px;
  text-align: center;
  background: var(--bg-2);
  border: 1px dashed var(--line);
  border-radius: var(--radius-xl);
}
.noresults-t { font-family: var(--font-serif); font-size: 22px; letter-spacing: -0.012em; color: var(--ink); }
.noresults-s {
  margin: 10px auto 0;
  max-width: 42ch;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-3);
  text-wrap: pretty;
}
.noresults-a { margin-top: 20px; }

/* -- Pagination ------------------------------------------------------------ */
.pager {
  margin-top: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
}
.pager-step {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 11px;
}
.pager-step:hover { background: var(--surface-2); color: var(--sage-deep); text-decoration: none; }
/* Same correction as `.opt.is-off`. The dashed border already says
   "unavailable" without asking anybody to read faint text. */
.pager-step.is-off { color: var(--ink-3); background: transparent; border-style: dashed; }
.pager-back { display: inline-flex; transform: rotate(180deg); }

.pager-n { display: flex; align-items: center; gap: 4px; }
.pager-p {
  display: grid;
  place-items: center;
  min-width: 36px;
  height: 36px;
  padding: 0 8px;
  border-radius: 10px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.pager-p:hover { background: var(--surface-2); color: var(--sage-deep); text-decoration: none; }
.pager-p.is-on { background: var(--sage); color: var(--on-sage); }
.pager-gap { color: var(--ink-4); padding: 0 2px; }
.pager-plain { font-family: var(--font-mono); font-size: 13px; color: var(--ink-3); }

/* The disclosure block lives in base.css — see the note there. */

/* ==========================================================================
   1180 → gutters + rail step down (tokens.css), headline follows
   ========================================================================== */
@media (max-width: 1180px) {
  .archive-head h1 { font-size: 38px; }
}

/* ==========================================================================
   980 → the filter rail becomes a normal block above the results
   ==========================================================================
   THE RESULTS MUST NOT END UP BELOW A WALL OF FILTERS. Ten stacked <details>
   summaries is ~520px of nothing before the first therapist; laid out two per
   row it is ~260px, and the whole rail plus the applied-filter chips fits above
   the fold on a 390×844 screen.

   An open group needs the full width, and `[open]` is a plain attribute
   selector — so the grid reflows on the server's decision (a group holding a
   selection arrives open) AND on the visitor's, with no script either time.
   ========================================================================== */
@media (max-width: 980px) {
  .archive-layout { grid-template-columns: minmax(0, 1fr); gap: 20px; }

  /* THE RESULTS COME FIRST NOW, and the way they do it is that the rail stops
     being 380px of collapsed accordions and becomes one 52px row. `.filters`
     gives up its card chrome to `.filters-body`, which is the thing that opens;
     closed, the whole rail is the button. DOM order is unchanged — filters
     still precede the results for a keyboard user, and now cost one tab stop
     to skip rather than fifty. */
  .filters { position: static; max-height: none; overflow: visible; padding: 0; background: transparent; border: none; border-radius: 0; }
  .ftoggle { display: block; }
  .ftoggle-b {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 52px;
    padding: 0 16px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 14px;
    font-size: 15px;
    font-weight: 500;
    color: var(--ink);
    cursor: pointer;
    list-style: none;
  }
  .ftoggle-b::-webkit-details-marker { display: none; }
  .ftoggle-b::marker { content: ""; }
  .ftoggle[open] > .ftoggle-b { border-color: var(--ink-4); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
  .ftoggle-l { margin-right: auto; }
  .ftoggle-n { font-family: var(--font-mono); font-size: 12px; font-weight: 400; color: var(--ink-3); }
  .ftoggle[open] > .ftoggle-b .fgroup-chev { transform: rotate(180deg); }

  .filters-body { display: none; }
  .ftoggle[open] ~ .filters-body {
    display: block;
    padding: 4px 14px 10px;
    background: var(--surface);
    border: 1px solid var(--ink-4);
    border-top: none;
    border-radius: 0 0 14px 14px;
  }
  /* "Refine" and its skip link are the rail's own head; inside an open panel
     that is opened by a button reading "Filters" they are one line of nothing. */
  .filters-head { display: none; }
  /* The rail's own copy of the applied filters exists to survive scrolling
     past them, and here it cannot: `.activebar` is rendered from the same
     `activeFilters` call ~40px above this. Two identical chip rows stacked on a
     390px screen is 150px of the same sentence twice. */
  .fsel { display: none; }

  .fgroups {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 12px;
    /* THE WALL HAS A CEILING, even once somebody has opened it. Five applied
       filters open five groups, and five open groups is a ~950px panel; bounded
       here it costs at most this much whatever the query does, and closing the
       control above it puts the results back at the top in one tap. */
    max-height: 60vh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .fgroup { border-top: 1px solid var(--line-soft); }
  /* The first row of the grid, whichever two cells that is. */
  .fgroups > .fgroup:nth-child(-n + 2) { border-top: none; }
  .fgroup[open] { grid-column: 1 / -1; border-top: 1px solid var(--line-soft); }
  /* `.fgroups` above is the scroller; nesting another inside it makes a swipe
     ambiguous, so nothing within it scrolls on its own. */
  .fopts--tail { max-height: none; overflow: visible; }
}

/* ==========================================================================
   640 → the 390px artboard
   ========================================================================== */
@media (max-width: 640px) {
  /* The header, the breadcrumb and the footer are in base.css — see the note
     there. This sheet owns the archive and nothing else. */

  .archive-head { margin: 20px 0 20px; padding-bottom: 18px; }
  .archive-head h1 { font-size: 26px; line-height: 1.12; letter-spacing: -0.018em; max-width: none; }
  /* 13.5px. This is the paragraph that explains what the page IS, and it was
     the smallest type on a phone screen that had room for it. */
  .archive-intro { margin-top: 10px; font-size: 16px; }

  /* The applied filters, directly above the results. Each chip is a LINK that
     removes a filter, so it is a control and gets a control's target: 44px,
     which on a pill means padding rather than type size. They still wrap two or
     three to a row at 390. */
  .activebar { margin-top: 14px; gap: 8px; }
  .activebar .chip { min-height: 44px; padding: 6px 12px; font-size: 12.5px; }
  .activebar-clear { display: inline-flex; align-items: center; min-height: 44px; font-size: 14px; }
  .opt-x-k { font-size: 10px; }

  .filters { padding: 0; }
  /* Every row inside the panel is a filter you tap. 32px was a desktop rail row
     rendered on a phone. */
  .fgroup-sum { min-height: 48px; padding: 6px 2px; gap: 8px; }
  .fgroup-title { font-size: 13.5px; }
  .fgroups { column-gap: 10px; }
  .fmore-sum { min-height: 44px; font-size: 13px; }
  .opt { min-height: 44px; padding: 6px 8px; font-size: 14px; }
  .opt-note { font-size: 12px; }
  .opt-n { font-size: 12px; }

  .results-head { margin-bottom: 12px; }
  .results-count { font-size: 19px; }
  .results-page { font-size: 12px; }

  .rgrid { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .tcard { padding: 16px; gap: 12px; border-radius: 16px; }
  .tavatar { width: 42px; height: 42px; border-radius: 12px; }
  .tavatar .ini { font-size: 15px; }
  .tname { font-size: 18px; }
  /* The card's name is a link and on a phone it is the biggest thing anyone
     aims at. Padding grows the hit box to 44px and an equal negative margin
     hands the space back to the layout, so the target grows without the card
     doing so. The 11px it borrows below is `.tsub`, which is text, not a
     control — overlapping it costs nothing. */
  .tname a { display: inline-block; padding: 12px 0; margin: -12px 0; }
  .tsub { font-size: 13px; position: relative; }
  .tcard .chip { padding: 6px 10px; font-size: 12.5px; }
  .mini-strip { --mcell-h: 12px; gap: 2px; }
  .mini-cap { margin-top: 6px; font-size: 11.5px; }
  /* The card's two actions sit on one row; the secondary one is a link and
     needs the same target as the button beside it. */
  .tcard-foot { gap: 8px 12px; }
  .tcard-more { display: inline-flex; align-items: center; min-height: 44px; padding: 0 4px; font-size: 14px; }
  .tmore { min-height: 44px; padding: 5px 4px; font-size: 13px; }

  .noresults { padding: 32px 20px; border-radius: 16px; }
  .noresults-t { font-size: 19px; }
  .noresults-s { font-size: 15px; }

  /* `.facts` is a label/value list on the profile rail, where nothing in it is
     a link — and the licence-lookup page, where the label IS one. The row is
     already 46px; this makes the anchor the row rather than the 15px of text
     inside it. */
  .facts dt a { display: flex; align-items: center; min-height: 44px; margin: -11px 0; }
  .facts dt { font-size: 14px; }
  .facts dd { font-size: 13.5px; }

  /* Two rows instead of three: the numbers take a line of their own, then
     Previous and Next sit side by side beneath. This does put Previous after
     the numbers visually while it stays before them in the DOM — accepted
     because the whole cluster is one control group inside ~80px, every link
     keeps its label, and the alternative (dropping the numbers on mobile)
     would make deep pages unreachable without a desktop. */
  .pager { margin-top: 22px; gap: 10px 8px; }
  .pager-n { order: 1; width: 100%; justify-content: center; flex-wrap: wrap; gap: 6px; }
  .pager-step { order: 2; min-height: 44px; padding: 8px 14px; font-size: 13.5px; }
  .pager-p { min-width: 44px; height: 44px; font-size: 13.5px; }

}

/* Print: the filter rail is dead weight on paper. */
@media print {
  .filters, .pager, .skip-link, .site-nav, .navt { display: none; }
  .archive-layout { grid-template-columns: minmax(0, 1fr); }
  .tcard { break-inside: avoid; box-shadow: none; }
}
