/* ── responsive ─────────── */
/* ── RESPONSIVE LAYER ─────────────────────────────────────────────────
   Shared by all 13 render shells. The source is this file; the copy in
   each shell is written by prototypes/_partials/inject-responsive.mjs and
   sits AFTER the shell's own <style>, so at equal specificity these rules
   win by source order -- no !important, no --spk-* definitions.

   Why one file: the chrome and pane CSS is byte-identical across the 13
   shells (md5 x13, docs/briefs/mobile/REPORT-frontend.md 1.6). Pasting a
   phone rule into 13 files by hand is how they would drift apart.

   The viewport decides the SHELL (.app, .bar, .sports, .panes, .pane).
   The pane decides its BLOCKS: a pane is ~600px at 1440, 432 at 768, 374
   stacked on a phone, so blocks query the pane, not the viewport.

   Contract and numbers: docs/briefs/MOBILE-ADAPTATION-PLAN.md.
   Measured with: node scripts/audit-page.mjs <url> --viewport 390x844 --assert-mobile
   ────────────────────────────────────────────────────────────────────── */

/* Every pane is a container so block-level rules can query the width the
   block actually has -- in the rail, the centre, or the phone column. */
.pane{container-type:inline-size;container-name:pane}

/* Tablet, 768-1023. The shell already folds the rail here; the list pane
   kept 320 of 768px. 300 leaves the centre 468px, enough for the 5-tab
   strip (347-403px measured) and a 3-up odds row without scrolling. */
@media (max-width:1023px){
  .panes{grid-template-columns:minmax(272px,300px) minmax(0,1fr)}
}

/* Phone, <=767. One column; the document scrolls; the chrome is sticky.
   767 not 900 (the benchmark's old number): at 768 the two-pane layout is
   measured healthy and iPad portrait keeps the application model. */
@media (max-width:767px){
  :root{
    --h-bar:48px;
    /* Sum of the sticky chrome. Every sticky `top` inside the document
       reads this, so the number lives in one place. 48 while the sports
       rail lists one sport; 88 once a second sport brings it back. */
    --chrome-h:48px;
    --h-tap:44px;
    --h-star:44px;
    --h-grp:44px;
    --h-row-tbl:44px;
    --h-chip:40px;
    --h-seg:32px;
    --fs-micro:11px;
    --fs-sm:13px;
    --fs-det:20px;
    --cr-hero:56px;
    --sp-page:0px;
  }
  html,body{height:auto}
  body{overflow:auto}
  html{scroll-padding-top:calc(var(--chrome-h) + var(--h-tab))}
  .app{height:auto;min-height:100dvh;grid-template-rows:auto auto auto}

  /* ── Chrome. The bar's min-content was 603px (a 280px search box, a
     24px logo margin, two padded nav links) and sized the whole document
     on every phone: the page rendered at 0.65 scale. Now ~270px. */
  .bar{position:sticky;top:0;height:var(--h-bar);gap:var(--sp-sm);padding:0 var(--sp-sm)}
  .logo{margin-right:var(--sp-xs);font-size:20px}
  .nav a{height:var(--h-tap);padding:0 var(--sp-sm)}
  /* The search box becomes a 44px icon button. The label stays in the DOM
     for the accessible name; the glyph is drawn, not a font, so it needs
     no asset. Real SVG markup replaces this in the Phase 2 shell edit. */
  .srch{width:var(--h-tap);flex:0 0 var(--h-tap);height:var(--h-tap);padding:0;
    justify-content:center;overflow:hidden;color:transparent;position:relative}
  .logo{display:inline-flex;align-items:center;height:var(--h-tap)}
  .srch::before{content:"";position:absolute;width:13px;height:13px;left:12px;top:11px;
    border:2px solid rgba(255,255,255,.85);border-radius:50%}
  .srch::after{content:"";position:absolute;width:2px;height:8px;left:27px;top:25px;
    background:rgba(255,255,255,.85);transform:rotate(-45deg)}
  .srch kbd{display:none}
  /* The language switcher keeps its place on a phone: the search box has
     already collapsed to an icon, so two 28px codes fit beside it, and a
     reader who lands on the wrong language must be able to leave it without
     scrolling to the foot of a pane that scrolls on its own. */
  /* `.bar` centres its children, so the nav shrinks to its content and its
     links never reach the 44px tap target however tall they ask to be. The
     container takes the height; the links fill it. */
  .lang{margin-left:var(--sp-xs);flex:none;align-self:stretch;align-items:stretch}
  .lang a{min-width:30px;height:var(--h-tap);padding:0 2px;font-size:11px}
  /* Two 30px codes plus the icon button leave the bar at its 320px budget on
     the narrowest phone. They are under the 44px tap width, which the audit
     counts in its share rather than its core set: a language switch is a
     rare, deliberate tap, not a control a reader hits while scrolling. */
  /* 390px is the narrowest phone we target and the bar was 37px over it once
     the switcher joined. The second nav word is the cheapest thing to drop:
     the transfers hub is one tap away in the footer and in the left rail,
     while a reader stranded in the wrong language has no other way out. */
  .nav a + a{display:none}
  .sports{position:sticky;top:var(--h-bar);z-index:var(--z-hd)}
  .sports a{height:40px}
  /* One sport listed = a 53px band that says "Football" under a bar that
     already says it. It returns, at 40px, when there is a second entry. */
  .sports:has(a:only-child){display:none}

  /* ── Panes. Three become one column. DOM order is unchanged (the golden
     extract and the h1 position in source hold still); grid areas set the
     visual order. Subject first, then the rail blocks -- the entity's own
     links, and the market tick's target -- then the match list, as the
     last section under its sticky league header. No height cap: a capped
     inner scroller inside a scrolling body is the scroll trap phones handle
     worst. Home and transfers invert: there the list IS the page. */
  .panes{display:flex;flex-direction:column;overflow:visible;
    container-type:inline-size;container-name:pane}
  .pane{overflow:visible;min-height:auto;scrollbar-gutter:auto;overscroll-behavior:auto}
  /* The centre pane dissolves into the column so its footer -- its last
     child, on every shell -- can sort after the rail and the list instead
     of splitting the page in the middle. Its blocks then query .panes. */
  .pane.c{display:contents}
  /* A flex item's minimum is its min-content: a standings table or the
     bracket's stage rail would size the column to itself and push the page
     past the viewport. Zero it; scrollers then scroll inside their block. */
  .pane.c > *,.pane.r,.pane.l{min-width:0;max-width:100%}
  .pane.r{order:1;display:block;border-left:0;padding:var(--sp-page);background:var(--s0)}
  .pane.l{order:2;border-right:0;border-top:1px solid var(--ink4)}
  .panes .ft{order:3;margin-left:0;margin-right:0}
  .app.is-list .pane.l{order:-1;border-top:0}

  /* ── Sticky inside the document. Everything pins under the bar. */
  .filters{top:var(--chrome-h)}
  .pane.l{--rail-chrome:0px}
  .pane.l:has(.filters){--rail-chrome:56px}
  .lg{top:calc(var(--chrome-h) + var(--rail-chrome));height:var(--h-grp)}
  .tabs{top:var(--chrome-h)}
  .tabs button{padding:0 var(--sp-md)}
  /* The tab strip sits inside a .blk that clips overflow, which turns the
     block into the sticky's scroll root and pins nothing. */
  .blk:has(> .tabs){overflow:visible}
  /* Table heads stuck to a wrapper that no longer scrolls vertically. */
  .stbl th{position:static}

  /* ── Blocks, edge to edge. */
  .blk{border-radius:0;box-shadow:none;margin-bottom:var(--sp-sm)}
  .pgh1{font-size:18px;line-height:1.25;display:-webkit-box;-webkit-line-clamp:2;
    -webkit-box-orient:vertical;overflow:hidden}
  .det-nm{white-space:normal;font-size:var(--fs-det)}
  .cap-tall{max-height:none;overflow:visible}
  .mv-plot,.mv-plot svg{max-width:100%}

  /* ── Tap targets: 44px is the contract for controls. */
  .m{grid-template-columns:var(--w-rail) minmax(0,1fr) auto var(--h-star)}
  .m .pr b{min-width:34px;padding:4px}
  .star{width:var(--h-star);height:var(--h-star)}
  .odd{min-height:var(--h-tap)}
  .chip{height:var(--h-chip)}
  .dstep button{width:var(--h-tap);height:var(--h-tap)}
  .seg button{height:var(--h-seg)}
  .leg{min-height:36px;min-width:var(--h-tap)}
  .stbl td{height:var(--h-row-tbl)}
  .rgn .rw{min-height:var(--h-tap)}
  /* A link inside a row or a cell takes the row's height as its hit box:
     a 20px team name in a 44px standings row was the largest family of
     sub-44px targets on every entity page (24-36 per page). */
  td a,.rw a,.tmc a,.tie a,.nm a,.yr a{display:inline-flex;align-items:center;min-height:var(--h-tap)}
  .md-row,.fx-row{min-height:var(--h-tap)}
  /* Form pills are links to the matches. The pill stays a 32px square; a
     transparent border pads the hit box to 44 without a wrapper. */
  .fpills{gap:0}
  .fpills>*{width:var(--h-tap);height:var(--h-tap);border:6px solid transparent;background-clip:padding-box}
  a.more{min-height:var(--h-tap);display:flex;align-items:center}
  .leg{height:auto}
  /* Link lists: the crumb and the footer columns are the bulk of the
     sub-44px targets on every page (27 footer links + 5 crumb links). */
  .crumb a{display:inline-block;padding:12px 0;line-height:20px}
  .panes .ft-col li{margin:0}
  .panes .ft-col a,.panes .ft-links a,.panes .ft-legal a{display:inline-block;padding:12px 0;line-height:20px}
  .panes .ft-col a{display:block}
  .panes .ft-brand{display:inline-flex;align-items:center;min-height:var(--h-tap)}

  /* ── Type floor: nothing a reader is asked to read sits under 11px.
     These are the only sub-11px declarations left in the shells' own CSS
     (crest initials, pitch ratings, two tags). */
  i.cr,i.cr.xs,i.cr.inline,.pitch .rt,.pitch .gl-dot,.tn-tag,.stgwrap .stg-h .tag{font-size:11px}
  /* Form pips: 16px boxes with 10px letters read as 6.5px at the old zoom. */
  .frm.w,.frm.l,.frm.d,.frm i,.rgn .frm i{width:18px;height:18px;font-size:11px}

  /* ── The match hero: a 72px crest per side was the h2h leak. */
  .mh .side .cr{width:var(--cr-hero);height:var(--cr-hero)}
  .mh .tt{gap:var(--sp-sm)}
  .mh .side{min-width:0}
  .mh .side b{font-size:var(--fs-md)}
  /* The served matchup (.blk.rgn > .side / .mid). At 283px each side had
     10px and the crest overflowed by 65px; at 374px the grid fits once the
     gutter and the score come down and the name may wrap. */
  .blk.rgn:has(> .side){gap:var(--sp-sm);padding:var(--sp-md) var(--sp-sm)}
  .blk.rgn > .side .cr{width:44px;height:44px}
  .blk.rgn > .side .nm{font-size:var(--fs-md);overflow-wrap:anywhere}
  .blk.rgn > .mid .k{font-size:32px}
}

/* ── Blocks query the pane, not the viewport (see the header comment).
   These replace the four viewport rules the shells carried at 640/560/900,
   which never fired on desktop where the pane was already that narrow. */
@container pane (max-width:480px){
  .tn{grid-template-columns:1fr}
  .sqd .sq{grid-template-columns:1fr}
  .lead3{grid-template-columns:1fr}
  .tiles{grid-template-columns:1fr 1fr}
  .stgwrap .ties{grid-template-columns:1fr 1fr}
  .recstrip{grid-template-columns:repeat(2,1fr)}
}
@container pane (max-width:400px){
  .stgwrap .ties{grid-template-columns:1fr}
  .facts,.fcts{grid-template-columns:1fr}
}
/* Standings in a narrow pane keep # Team P W D L GD Pts; the renderer marks
   GF, GA and the form cells hide-m. */
@container pane (max-width:420px){
  .hide-m{display:none}
}

/* ── Every viewport ────────────────────────────────────────────────────
   The light theme's secondary ink was rgba(34,34,38,.45) on white: 2.76:1,
   under the 4.5:1 floor on row times, crumbs, table heads and unselected
   tabs at every width. The dark value (5.18:1) is untouched: the guard
   keeps this from outranking the shell's [data-theme=dark] block, which
   sits earlier in the cascade at the same specificity. */
:root:not([data-theme=dark]){--ink3:rgba(34,34,38,.62)}

/* @@SHELLS-SECTION@@ -- rules added with the Phase 2 shell markup changes */

/* Search. The magnifier is SVG in the 13 shells now; the label is wrapped
   in .srch-t so a phone can hide it visually and keep the accessible name. */
.srch-i{flex:none;opacity:.85}
.srch-t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Matches jump. A link at the end of each tab strip to the list pane
   (#matches), which the phone stacks last; on wider viewports the list is
   beside the panel and the link has nothing to jump to. */
.tab-jump{display:none}

@media (max-width:767px){
  .srch{color:inherit}
  /* Phase 1 drew the glyph with pseudo-elements; the SVG replaces it. */
  .srch::before,.srch::after{content:none}
  .srch-t{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
  .tab-jump{display:inline-flex;align-items:center;height:var(--h-tab);padding:0 var(--sp-md);
    margin-left:auto;font-weight:700;font-size:var(--fs-md);color:var(--ink2);white-space:nowrap}
  /* Right-edge fade as the scroll cue. The script sets is-over while the
     strip overflows and is not scrolled to its end. */
  .tabs.is-over{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);
    mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent)}

  /* Stat bars. The desktop row is 190px of label + a flexible track with a
     120px floor + 44px of value = wider than a 390px phone, and the track
     pushed 64px past the viewport. The floor is what has to go: on a phone
     the label wraps and the track takes whatever is left. This only began to
     show when the panels holding these rows stopped being hidden -- the rule
     was always wrong, it was just never on screen. */
  .bar-row{grid-template-columns:minmax(0,1fr) minmax(0,1fr) 40px}
  .bar-row .track{min-width:0}

  /* Pitch badges -- the shirt-number bubble and the captain's armband disc --
     were set at 9px, under the phone's 11px floor. Their discs are 15px and
     14px, so the glyph grows with them. Same reason as the bar row above:
     these only reached a phone screen once the panels stopped being hidden,
     and the rule was always below the floor, just never on show. */
  .pitch .mk:has(.face) .sh,.pitch .cap{font-size:11px;width:17px;height:17px}

  /* Pitch markers are links, and at 34px they sat under the 44px tap floor --
     eleven of them, which took the page's small-target share from 6% to 17%
     the moment the Squad panel stopped being hidden. The disc grows to the
     floor; the pitch is a grid of percentages, so nothing else moves. The
     name beneath it is part of the same link and gets the same floor. */
  .pitch .mk{width:44px;height:44px}
}

/* @@RENDER-SECTION@@ -- rules added with the Phase 2 renderer changes */

/* ── The match row and its star (match-list.render.ts). The star was a
   <button> inside <a class="m"> -- interactive inside interactive, which
   HTML forbids -- and is now the anchor's sibling in .mrow, drawn over the
   anchor's empty fourth grid column (28px on desktop, --h-star on a phone,
   both set on .m itself). The shell script keeps filtering on `.m[data-st]`
   and toggling `hidden` on the anchor; the wrapper follows, so a filtered
   row cannot leave its star behind. */
.mrow{position:relative}
.mrow>.star{position:absolute;right:var(--sp-md);top:50%;transform:translateY(-50%)}
.mrow:has(>.m[hidden]){display:none}

/* ── Match Details by state. The renderer stamps data-state="pre|live|ft"
   on the Details panel and data-blk on the timeline, periods and statistics
   regions. In play or finished, what happened outranks what was priced:
   those three sort up under the matchup, ahead of the odds and the swing
   chart. Visual order only -- the DOM, and with it the h2 sequence the
   golden extract watches, keeps the shell's order. */
@media (max-width:767px){
  [data-panelbody][data-state]{display:flex;flex-direction:column}
  [data-panelbody][data-state=live]>.blk:has(>.side),
  [data-panelbody][data-state=ft]>.blk:has(>.side){order:-4}
  [data-panelbody][data-state=live]>.blk:has(>.rgn>[data-blk=timeline]),
  [data-panelbody][data-state=ft]>.blk:has(>.rgn>[data-blk=timeline]){order:-3}
  [data-panelbody][data-state=live]>.blk:has(>.rgn>[data-blk=periods]),
  [data-panelbody][data-state=ft]>.blk:has(>.rgn>[data-blk=periods]){order:-2}
  [data-panelbody][data-state=live]>.blk:has(>.rgn>[data-blk=stats]),
  [data-panelbody][data-state=ft]>.blk:has(>.rgn>[data-blk=stats]){order:-1}
}

/* A tab that is a page link (team: Fixtures) dresses like the buttons beside
   it; the switcher script ignores anchors, so the click navigates. */
.tabs .tab-link{height:var(--h-tab);padding:0 var(--sp-lg);display:inline-flex;align-items:center;
  color:var(--ink3);font-weight:700;font-size:var(--fs-md);line-height:1;letter-spacing:-.028em;
  border-bottom:2px solid transparent;white-space:nowrap}
.tabs .tab-link:hover{color:var(--ink1)}
@media (max-width:767px){
  .tabs .tab-link{padding:0 var(--sp-md)}
}

@media (max-width:767px){
  /* Ten 44px pills overran a 374px strip by 62px: they wrap, seven to a
     row; the pill inside stays a 32px square. */
  .fpills{flex-wrap:wrap}
  .fpills>*{width:var(--h-tap)}
  /* The hub's fixture rows: "Sat 13 Sep" at 13px no longer fits 76px. */
  .md-row{grid-template-columns:84px minmax(0,1fr) 52px minmax(0,1fr)}
  /* Links in stat rows and block headings take the row's height. */
  .t a,.blk-h a{display:inline-flex;align-items:center;min-height:var(--h-tap)}
}

/* ── Phase 3 ──────────────────────────────────────────────────────────── */

/* Archivo, self-hosted: one variable file per subset, served immutable from
   /assets/fonts by TokensAssetController. The fallback is Arial scaled to
   Archivo's advance widths so the swap moves nothing. */
@font-face{font-family:"Archivo";font-style:normal;font-weight:400 700;font-display:swap;
  src:url(/assets/fonts/archivo-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Archivo";font-style:normal;font-weight:400 700;font-display:swap;
  src:url(/assets/fonts/archivo-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Archivo Fallback";src:local("Arial"),local("Helvetica Neue"),local("Liberation Sans");
  size-adjust:103%;ascent-override:88%;descent-override:22%;line-gap-override:0%}
body{font-family:Archivo,"Archivo Fallback",-apple-system,system-ui,sans-serif}

@media (max-width:767px){
  /* The panel header and the tab strip share a card. A sticky child pins
     only within its parent, and that card is 120px tall, so the strip never
     pinned on a phone (measured: at scrollY 1500 only the bar was pinned).
     The card dissolves; header and strip become items of the column. */
  .blk:has(> .tabs){display:contents}
  .blk:has(> .tabs) > *{min-width:0;max-width:100%;background:var(--s1)}
  .det-hd{padding:var(--sp-md) var(--sp-md) var(--sp-sm)}

  /* Compact header (responsive.js sets the classes). Past the header, the
     bar slides away and the entity strip pins at 44px with the tabs under
     it: 96px of chrome, under the 100px budget, and the score stays in
     view on every tab of a live match. */
  .bar{transition:transform .2s ease-out}
  .app.no-motion .bar{transition:none}
  .app.bar-hidden .bar{transform:translateY(-100%)}
  .app.is-compact{--strip-top:var(--h-bar);--chrome-h:calc(var(--h-bar) + 44px)}
  .app.is-compact.bar-hidden{--strip-top:0px;--chrome-h:44px}
  .app.is-compact .det-hd{position:sticky;top:var(--strip-top);z-index:var(--z-s1);
    min-height:44px;padding:0 var(--sp-md);display:flex;align-items:center;
    border-bottom:1px solid var(--ink4)}
  .app.is-compact .det-hd .det-sub,.app.is-compact .det-hd .cr.big,.app.is-compact .det-hd .av-photo{display:none}
  .app.is-compact .det-hd .det-nm{font-size:15px;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .app.is-compact .det-hd .det-tt{gap:var(--sp-sm);flex:1;min-width:0}
  .det-score{font-weight:700;font-variant-numeric:tabular-nums;margin-left:auto;white-space:nowrap}
  .det-score .min{color:var(--live);font-size:var(--fs-sm);margin-left:6px}

  /* League groups fold on a tap of the header. */
  .lgrp .lg[role=button]{cursor:pointer}
  .lgrp .lg[role=button]::after{content:"";width:8px;height:8px;margin-left:var(--sp-sm);
    border-right:2px solid var(--ink3);border-bottom:2px solid var(--ink3);transform:rotate(45deg);
    transition:transform .15s ease-out}
  .lgrp.is-collapsed .lg[role=button]::after{transform:rotate(-45deg)}
  .lgrp.is-collapsed .mrow,.lgrp.is-collapsed > .m{display:none}

  /* Bottom bar on the list pages (home, transfers): the places a reader
     goes between, in the thumb's reach. Links, so it works without script. */
  .bnav{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:var(--z-bottom,106);
    height:calc(56px + env(safe-area-inset-bottom));padding-bottom:env(safe-area-inset-bottom);
    background:var(--s1);border-top:1px solid var(--ink4)}
  .bnav a{flex:1;display:inline-flex;align-items:center;justify-content:center;
    font-weight:700;font-size:var(--fs-sm);color:var(--ink3);min-height:var(--h-tap)}
  .bnav a.on{color:var(--primary)}
  .app.is-list{padding-bottom:calc(56px + env(safe-area-inset-bottom))}

  /* One stage per swipe on the bracket. */
  .bracket{scroll-snap-type:x mandatory}
  .stg{scroll-snap-align:start}
}
.bnav{display:none}
@media (max-width:767px){.bnav{display:flex}}

/* ── search ─────────── */
/* ═══════════════════════════════════════════════════════════════════════
   SEARCH — the command palette, and the row it shares with /search.

   The header has carried a magnifier, a "Search teams, players, leagues"
   label and a Cmd-K hint on all thirteen shells since the redesign, with
   nothing behind it. This layer is the surface that button now opens.

   The results page and the palette draw the SAME row (.sr-row), because
   they answer the same question, and someone who learns the palette's row
   should not have to re-learn it on the page.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── The result row, shared by both surfaces ────────────────────────── */
.sr-row{display:flex;align-items:center;gap:10px;padding:8px 10px;
  border-radius:var(--r-md,8px);color:inherit;text-decoration:none;
  min-height:44px;box-sizing:border-box}
.sr-row:hover{background:var(--s2)}
/* Keyboard selection is a STATE, not a hover: the palette moves it with the
   arrow keys and the pointer must not fight it. One treatment for both so
   the two can never disagree about which row Enter will open. */
.sr-row[aria-selected="true"]{background:var(--s3,var(--s2));
  outline:2px solid var(--acc);outline-offset:-2px}
.sr-row img,.sr-row i.cr,.sr-row span.cr{flex:none;width:24px;height:24px;
  border-radius:6px;object-fit:contain}
.sr-txt{min-width:0;flex:1;display:flex;flex-direction:column;gap:1px}
.sr-nm{font-size:13px;font-weight:600;line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sr-ctx{font-size:11px;color:var(--fg-dim);line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The matched span of the name. Weight and accent rather than a highlight
   block: a filled background on every other character turns a list of
   answers into a list of stripes. */
.sr-row mark{background:none;color:var(--acc);font-weight:800}

/* ── The results page (/search) ─────────────────────────────────────── */
/* The form is the no-JS path and the way a shared ?q= URL re-runs, so it is
   a real control, not a decoration behind the palette. */
.sr-form{display:flex;gap:8px;margin:0 0 14px}
.sr-form input{flex:1;min-width:0;height:var(--h-tap,44px);padding:0 14px;
  border-radius:var(--r-pill,999px);border:1px solid var(--s3,var(--s2));
  background:var(--s2);color:var(--fg);font:inherit;font-size:14px}
.sr-form input:focus{border-color:var(--acc);outline:none}
/* Height matches the input and clears the 44px tap floor. The audit measures
   this one: a 21px button passed every visual review and failed the phone
   contract, which is the whole reason that check exists. */
.sr-form button{flex:none;height:var(--h-tap,44px);padding:0 18px;border:0;
  border-radius:var(--r-pill,999px);background:var(--acc);color:#0b0b0f;
  font:inherit;font-size:14px;font-weight:700;cursor:pointer}
.sr-grp{margin:0 0 18px}
/* The count sits with the heading rather than under it: the group label alone
   does not say whether there are two answers here or twenty. */
.sr-count{font-size:11px;color:var(--fg-dim);font-weight:600;margin-left:6px}
/* One frame for both the invitation and the nothing-found line, so the page
   does not jump between the two states. */
.sr-note{padding:28px 16px;text-align:center;color:var(--fg-dim)}
.sr-note b{display:block;color:var(--fg);font-size:14px;margin:0 0 4px}
.sr-note p{margin:0;font-size:12px;line-height:1.5;max-width:44ch;
  margin-inline:auto}
/* The h1 the shell exposes for the SEO surface; the panel header already
   says the same words at full size. */
.sr-h1{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap}
/* A label kept for screen readers only -- the placeholder is not a label. */
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap}

/* ── The command palette ────────────────────────────────────────────── */
/* Hidden until opened, and hidden via [hidden] so a page whose JS never
   runs cannot paint an overlay it has no way to close. */
.pal[hidden]{display:none}
.pal{position:fixed;inset:0;z-index:80;display:grid;
  grid-template-rows:auto 1fr;justify-items:center;
  padding:10vh 16px 16px;background:rgba(4,4,8,.62);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
.pal-box{width:min(620px,100%);max-height:min(560px,78vh);
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--s1);border:1px solid var(--s3,var(--s2));
  border-radius:var(--r-lg,12px);box-shadow:0 24px 64px rgba(0,0,0,.5)}
.pal-hd{display:flex;align-items:center;gap:8px;padding:10px 12px;
  border-bottom:1px solid var(--s2)}
.pal-hd svg{flex:none;opacity:.7}
.pal-hd input{flex:1;min-width:0;background:none;border:0;outline:none;
  color:var(--fg);font:inherit;font-size:15px;padding:4px 0}
.pal-hd input::placeholder{color:var(--fg-dim)}
.pal-hd kbd{flex:none;font:600 10px/1 var(--mono,ui-monospace),monospace;
  color:var(--fg-dim);border:1px solid var(--s3,var(--s2));
  border-radius:4px;padding:4px 6px}
.pal-bd{overflow-y:auto;overscroll-behavior:contain;padding:6px;flex:1}
.pal-grp{margin:0 0 4px}
.pal-grp>h3{margin:0;padding:6px 10px 4px;font-size:10px;font-weight:800;
  letter-spacing:.08em;text-transform:uppercase;color:var(--fg-dim)}
.pal-note{padding:26px 16px;text-align:center;color:var(--fg-dim)}
.pal-note b{display:block;color:var(--fg);font-size:14px;margin:0 0 4px}
.pal-note p{margin:0;font-size:12px;line-height:1.5}
.pal-ft{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:8px 12px;border-top:1px solid var(--s2);
  font-size:11px;color:var(--fg-dim)}
.pal-ft kbd{font:600 10px/1 var(--mono,ui-monospace),monospace;
  border:1px solid var(--s3,var(--s2));border-radius:4px;padding:3px 5px;
  margin-right:4px}
.pal-ft .all{margin-left:auto;color:var(--acc);font-weight:700;
  text-decoration:none}
/* The palette owns the scroll while it is open, or the page behind it
   scrolls under the overlay on a trackpad. */
body.pal-open{overflow:hidden}

@media (max-width:767px){
  /* A phone gets the whole screen. 10vh of backdrop above a 78vh box is a
     box that cannot show three rows once the keyboard is up -- and the
     keyboard is always up here, because the field takes focus on open. */
  .pal{padding:0}
  .pal-box{width:100%;max-height:100%;height:100%;border-radius:0;border:0}
  .pal-ft .all{margin-left:0;width:100%;padding-top:4px}
  /* The hint row is the first thing the keyboard covers and the least
     useful thing on a touch screen, where there are no arrow keys. */
  .pal-ft .keys{display:none}
}

/* ── footer ─────────── */
.ft{container-type:inline-size;margin:var(--sp-lg) calc(-1 * var(--sp-sm)) calc(-1 * var(--sp-sm));color:var(--ink2);font-size:var(--fs-md);line-height:1.45}
.ft-top{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:var(--sp-lg) 40px;padding:var(--sp-lg) var(--sp-lg) 28px}
.ft-h{font-size:var(--fs-lg);font-weight:700;color:var(--ink1);margin:0 0 var(--sp-md);line-height:1.2}
.ft-about p{margin:0;font-size:var(--fs-md);line-height:1.55;color:var(--ink2);max-width:62ch}
.ft-about p+p{margin-top:var(--sp-sm)}
.ft-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:28px var(--sp-lg)}
.ft-col ul{list-style:none;margin:0;padding:0}
.ft-col li{margin:0 0 7px}
.ft-col a{color:var(--ink2);text-decoration:none;font-size:var(--fs-md);line-height:1.35}
.ft-col a:hover,.ft-col a:focus-visible{color:var(--ink1);text-decoration:underline;outline:none}
.ft-n{color:var(--ink3);font-variant-numeric:tabular-nums;margin-left:var(--sp-xs)}
.ft-band{background:var(--s1);padding:var(--sp-lg);display:grid;grid-template-columns:auto 1fr;gap:var(--sp-md) var(--sp-lg);align-items:center}
.ft-brand{font-weight:800;font-size:22px;letter-spacing:-.02em;color:var(--ink1);line-height:1;text-decoration:none}
.ft-links{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--sp-sm) 28px}
.ft-links a{color:var(--ink1);text-decoration:none;font-size:var(--fs-sm);font-weight:700;letter-spacing:.08em;text-transform:uppercase}
.ft-links a:hover{text-decoration:underline}
.ft-lang{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--sp-sm) 20px;margin-top:var(--sp-sm)}
.ft-lang a{color:var(--ink3);text-decoration:none;font-size:var(--fs-sm)}
.ft-lang a[aria-current]{color:var(--ink1);font-weight:700}
.ft-lang a:hover{text-decoration:underline}
.ft-fine{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:var(--sp-xs) var(--sp-lg);justify-content:space-between;padding-top:var(--sp-md);border-top:1px solid var(--ink5);font-size:var(--fs-sm);color:var(--ink3)}
.ft-fine p{margin:0;max-width:72ch}
.ft-legal a,.ft-legal span{color:var(--ink3);text-decoration:none}
.ft-legal a:hover{color:var(--ink1);text-decoration:underline}
.ft-legal a+a::before,.ft-legal a+span::before{content:"·";margin:0 var(--sp-sm);color:var(--ink4)}
@container (max-width:720px){.ft-top{grid-template-columns:1fr;gap:28px}}
@container (max-width:440px){.ft-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.ft-band{grid-template-columns:1fr}.ft-links{justify-content:flex-start}}
@container (max-width:300px){.ft-grid{grid-template-columns:1fr}}
