/* shell.css - the persistent header + footer search bar (generator 0.10.0,
   P2.1 "shell" ship) plus the result-ROW vocabulary shell-search.js renders
   into. Split out of site.css the same way pages.css/pkg.css/leaf.css were
   (site.css was 27,408 B LF-normalised against a 27,648 B cap - 240 B of
   headroom, nowhere near this sheet's ~7-8 KB) - registered/hashed/emitted
   the same way pages_css_path is (generate/site_builder.py), linked
   unconditionally next to site.css's own <link> in base.html.j2 (the header/
   footer render on every page, so this cannot be a per-template opt-in the
   way pages_css_href is).

   Source of truth: seo/design/portal/header-search/build.py's SB_CSS (the
   Yahoo-pattern header bar, Mike-approved 2026-09-02) and rows_build.py's
   PAGE_CSS (the result-row vocabulary D4/P2.3 will reuse verbatim in
   _row.html.j2). This file is NOT a byte-identical copy of either - see the
   two deviations noted inline below.

   Cascade law: media tiers last, ordered by DESCENDING max-width (1139 ->
   860 -> 560 -> 420) so a later, NARROWER override always wins over an
   earlier, broader one when both match the same viewport - never the
   reverse (this project has been bitten by that before). */

/* ===================================================================
   1. HEADER + FOOTER SEARCH BAR (build.py SB_CSS, verbatim except for the
   two `.sb-page`-scoped rail-offset rules below, which are unscoped here -
   `.sb-page` was a design-canvas wrapper class that has no counterpart in
   the real site; base.html.j2 never stamps a `.sb-page` body class, so the
   real rail offset must apply globally instead).
   =================================================================== */
.topbar.sb{display:flex;padding:0;background:color-mix(in srgb,var(--paper) 90%,transparent)}
.sb-inner{display:grid;grid-template-columns:216px minmax(0,1fr) 292px;gap:36px;align-items:center;max-width:1240px;width:100%;margin:0 auto;padding:10px 28px}
.sb-row1{display:flex;align-items:center;gap:10px;min-width:0}
.sb .brand{font-size:21px}
.sb .burger{display:none}
.sb-spacer{flex:1}
.sb-search{position:relative;display:flex;align-items:center;gap:8px;height:46px;border:2px solid var(--violet);border-radius:var(--r-pill);background:var(--violet-t);padding:0 5px 0 18px;box-shadow:0 6px 18px oklch(.56 .18 292 / .18);transition:border-color .15s,box-shadow .15s,background .15s}
.sb-search:focus-within,.sb-search.focus{background:var(--card);border-color:var(--violet-d);box-shadow:0 0 0 4px var(--violet-t),0 6px 18px oklch(.56 .18 292 / .18)}
.sb-search input::placeholder{color:var(--ink-soft)}
.sb-go{position:relative;display:inline-flex;align-items:center;gap:7px;flex:none;height:36px;padding:0 16px 0 13px;border-radius:var(--r-pill);background:var(--violet);color:#fff;font-size:13.5px;font-weight:700;letter-spacing:.01em;transition:background .15s}
.sb-go:hover,.sb-go:focus-visible{background:var(--violet-d)}
.sb-go .ico{width:16px;height:16px;color:#fff;flex:none}
/* The tooltip is anchored to the button's RIGHT edge, not centred on it, and
   this is load-bearing rather than taste. The artboard centres it with
   `left:50%;transform:translateX(-50%)`, but a transform does not reduce
   SCROLLABLE OVERFLOW - the browser still counts the untransformed box, which
   for a `white-space:nowrap` tooltip reads ~54px wider than the button. That
   propagated up through .sb-search and .sb-inner to the document and produced
   a real horizontal scrollbar on every viewport at or below ~860px: measured
   at 561px, documentElement.scrollWidth was 592 against a clientWidth of 561,
   with the header AND footer buttons each contributing. Anchoring right means
   the box overflows LEFTWARD, and in LTR a leftward overflow never increases
   scrollWidth - so the tooltip costs nothing and no ancestor needs an
   `overflow:clip` that could cut it off. Verified after: scrollWidth == 561. */
.sb-go::after{content:attr(data-tip);position:absolute;right:0;top:calc(100% + 9px);transform:translateY(-4px);background:var(--ink);color:var(--paper);font-size:11.5px;font-weight:600;padding:5px 10px;border-radius:7px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s,transform .12s;z-index:60}
.sb-go:hover::after,.sb-go:focus-visible::after{opacity:1;transform:translateY(0)}
.sb-search>.ico{width:18px;height:18px;color:var(--muted);flex:none}
.sb-search.focus>.ico{color:var(--violet-d)}
.sb-search input{flex:1;min-width:0;border:0;background:none;outline:none;font:400 15px/1.4 var(--sans);color:var(--ink);padding:0}
.sb-search input::placeholder{color:var(--muted)}
.sb-search input::-webkit-search-cancel-button{display:none}
.sb-scope{display:inline-flex;align-items:center;gap:5px;flex:none;font-size:12.5px;font-weight:600;color:var(--ink-soft);padding:6px 9px 6px 11px;border-radius:var(--r-pill);border:1px solid var(--line-soft);background:var(--paper);transition:border-color .15s}
.sb-scope:hover{border-color:var(--ink-soft)}
.sb-agent{display:inline-flex;align-items:center;gap:8px;height:40px;background:var(--term-bg);color:var(--term-ink);font-family:var(--mono);font-size:12.5px;border-radius:var(--r-sm);padding:0 13px;border:1px solid transparent;transition:border-color .15s;white-space:nowrap}
.sb-agent:hover{border-color:var(--violet)}
.sb-agent .dollar{color:var(--violet);font-weight:700}
.sb-agent .what{font-size:9.5px;color:var(--term-dim);letter-spacing:.05em;text-transform:uppercase;margin-left:6px}
.sb-agent-d{justify-self:end}
.sb-agent-m{display:none;width:38px;height:38px;padding:0;justify-content:center;border-radius:10px}
.sb-agent-m .ico{width:18px;height:18px}
/* DEVIATION 1: unscoped from build.py's `.sb-page .rail-left,.sb-page
   .rail-right` - the real header is sticky at every width (D3), so the
   left/right rails always need the same 65px offset, not only inside a
   design-canvas wrapper that doesn't exist here. */
.rail-left,.rail-right{top:65px;height:calc(100vh - 65px);padding-top:18px}
/* the focus-empty drop panel ONLY (the three pinned wishes) - G-3 killed the
   typed/results dropdown this file used to also describe here; a typed
   query now always renders in place (shell-search.js's renderInPlace),
   never in a positioned panel. */
.sb-panel{position:absolute;left:0;right:0;top:calc(100% + 8px);background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-pop);padding:8px 8px 0;z-index:60;text-align:left;font-weight:400;cursor:default}
.sb-sec{padding:6px 8px 4px}
.sb-panel .kicker{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;color:var(--muted);margin:2px 6px 6px}
.sb-panel .kicker::before{content:"\2014 ";color:var(--violet)}
/* `.badge.pkg/.skill/.res` are still used by buildFocusPanel's TRY_WISHES
   badges (shell-search.js) - grep-verified before this ship (G-3): the only
   OTHER caller, the now-deleted groupHeader(), is gone, but these three are
   not dead. */
.sb-panel .badge{font-size:9.5px;font-weight:800;letter-spacing:.12em;padding:3px 8px;border-radius:5px;flex:none}
.sb-panel .badge.skill{background:var(--violet-t);color:var(--violet-d)}
.sb-panel .badge.pkg{background:var(--green-t);color:var(--green-ink)}
.sb-panel .badge.res{background:var(--amber-t);color:var(--amber-ink)}
.sb-try{display:flex;align-items:center;gap:12px;padding:9px 10px;border-radius:var(--r-sm);font-size:14px;color:var(--ink);transition:background .15s}
.sb-try:hover{background:var(--violet-t)}
.sb-try .q{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sb-try .hint{flex:none;font-family:var(--mono);font-size:11px;color:var(--muted)}
.sb-foot{display:flex;align-items:center;gap:12px;flex-wrap:wrap;border-top:1px solid var(--line-soft);margin-top:6px;padding:11px 16px 12px;font-size:12.5px;color:var(--muted)}
.sb-foot code{font-family:var(--mono);font-size:11.5px;color:var(--violet-d)}
.sb-foot a{margin-left:auto;color:var(--violet-d);font-weight:600}
.sb-reshead{margin-top:6px}
.sb-reshead .stamp a{color:var(--violet-d);font-weight:600}
/* footer variant */
.sbf-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr) auto;gap:24px;align-items:center;width:100%;padding:0 0 14px;margin-bottom:6px;border-bottom:1px solid var(--line-soft)}
.sbf-lead{font-size:13.5px;color:var(--ink-soft)}
.sbf-lead b{font-family:var(--serif);font-size:17px;color:var(--ink);display:block;margin-bottom:2px}

/* ===================================================================
   2. RESULT ROWS (rows_build.py's PAGE_CSS, filtered - D4: same markup/
   classes P2.3's _row.html.j2 will reuse verbatim, so shell-search.js's
   client-built rows and the server-rendered ones can never disagree.

   DEVIATION 2: PAGE_CSS is `.page{}`/`.rules{}`/`.qhead{}`/`.spec{}` (design-
   canvas page chrome) PLUS the `.list`/`.row`/`.verdict`/`.kind`/`.num`
   result-row vocabulary. Only the latter ships here - `.fact`/`.fact.good`/
   `.fact.warn` are dropped entirely because they already exist in site.css
   (verified: generate/assets/site.css:103-105) and would just be a
   duplicate/dead re-declaration. The `.phone{}`/`.phone .row{}` rules are
   also dropped (a design-canvas iframe-scaling wrapper, no runtime
   counterpart) - the REAL narrow-viewport row reflow is the actual
   `@media (max-width:560px)` block in build.py's ROW_CSS, which this file
   keeps, in the media-tiers-last section below. */
.list{display:flex;flex-direction:column;background:var(--card);border:1px solid var(--line-soft);border-radius:var(--r-lg);box-shadow:var(--shadow-card);overflow:hidden}
.row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 18px;padding:15px 20px 13px;border-top:1px solid var(--line-soft);transition:background .15s;color:inherit}
.row:first-child{border-top:none}
.row:hover{background:var(--paper)}
.row:hover .name{color:var(--violet-d)}
.row .body{min-width:0}
.row .t{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.row .name{font-family:var(--serif);font-size:17px;font-weight:700;line-height:1.3;transition:color .15s}
.row .name.claim{font-size:16.5px}
.row .verdict{font-family:var(--mono);font-size:10.5px;font-weight:700;letter-spacing:.03em;padding:2.5px 8px;border-radius:var(--r-pill)}
.row .verdict.yes{background:var(--green-t);color:var(--green-ink)}
.row .verdict.cond{background:var(--amber-t);color:var(--amber-ink)}
.row .verdict.no{background:var(--coral-t);color:var(--coral-ink)}
.row .verdict.unr{background:var(--paper);color:var(--muted);border:1px solid var(--line-soft)}
.row .kind{font-size:9px;font-weight:800;letter-spacing:.12em;padding:2.5px 7px;border-radius:4px}
.row .kind.pkg{background:var(--green-t);color:var(--green-ink)} .row .kind.skill{background:var(--violet-t);color:var(--violet-d)} .row .kind.res{background:var(--amber-t);color:var(--amber-ink)}
.row .thin{font-size:11px;color:var(--muted);font-family:var(--mono)}
.row .meta{font-size:12px;color:var(--muted);margin-top:1px}
.row .meta b{color:var(--ink-soft);font-weight:600}
.row .dek{font-size:13.5px;color:var(--ink-soft);margin:5px 0 0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.row .note{font-size:12.5px;color:var(--coral-ink);margin:5px 0 0;padding-left:10px;border-left:2px solid var(--coral-t)}
.row .facts{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
.row .num{grid-column:2;grid-row:1;align-self:start;text-align:right;min-width:88px;padding-top:2px}
.row .num b{display:block;font-family:var(--mono);font-size:15px;font-weight:700;letter-spacing:-.3px;line-height:1.2}
.row .num b.kn{font-size:13px;color:var(--coral-ink)}
.row .num small{display:block;font-size:9px;color:var(--muted);letter-spacing:.06em;text-transform:uppercase;margin-top:2px;line-height:1.3}
/* G: "Show more" (re-requests the same in-place query at the 25-row ceiling)
   and the footer line that replaces it once a list is AT that ceiling - new
   vocabulary, checked against pages.css's `.pill`/news.css's `.nw-pager`
   first (neither fits: pages.css never loads on the index/hub pages this
   renders into, and `.nw-pager` is an offset page-number list, not a single
   re-fetch button) - shell.css is the sanctioned drop point per this file's
   own header comment. */
.sb-more{display:inline-flex;align-items:center;margin-top:14px;padding:9px 18px;border:1px solid var(--line);border-radius:var(--r-pill);background:var(--card);color:var(--ink-soft);font:600 13px/1 var(--sans);cursor:pointer;transition:border-color .15s,color .15s}
.sb-more:hover,.sb-more:focus-visible{border-color:var(--violet);color:var(--violet-d)}
.sb-more:disabled{opacity:.6;cursor:default}
.sb-cap{margin:14px 0 0;font-size:12.5px;color:var(--muted)}
/* rows inside the app shell (build.py's ROW_CSS, real-class-scoped rules
   only - `.feedcol`/`.wrap`/`body.compact-nav` already exist in site.css). */
.feedcol .list{margin-top:4px}
.feedcol .row{padding:15px 20px 13px}
body.compact-nav .list,.wrap .list{max-width:56rem}

/* The sticky header is 65px tall, so an in-page #anchor would otherwise land
   UNDER it - the reader clicks a jump link and the heading they asked for is
   the one thing hidden. Measured before this rule: scroll-margin-top computed
   to 0 on every h2[id] on every page. 76px = the header plus a little air. */
h2[id],.listhead h2{scroll-margin-top:76px}

/* The package leaf's head row describes the page you are already on, so its
   dek is the subject of the page rather than one entry in a list - it gets the
   full capability sentence, not the 2-line clamp every other row keeps. Scoped
   by position, not by a new modifier class: the head row is the only .row that
   is a direct child of <article> (every other row on that page sits inside a
   .list), so this needs no extra contract field on the shared row dict. */
article > .row .dek{display:block;-webkit-line-clamp:none;overflow:visible}

/* Touch targets. The approved artboard sizes these for a mouse - the Search
   button is 36px tall, the scope pill 33px, the burger and mobile agent chip
   38px - all under the 44px a finger needs. Rather than change sizes Mike
   approved, each control gets an INVISIBLE 44px-tall hit area on touch
   pointers only: the painted control is unchanged at every width, and nothing
   moves. `left:0;right:0` keeps the overlay inside the control's own box, so
   it cannot reintroduce the horizontal overflow the tooltip once caused. */
@media (pointer:coarse){
  .sb-go,.sb-scope,.sb .burger,.sb-agent-m{position:relative}
  .sb-go::before,.sb-scope::before,.sb .burger::before,.sb-agent-m::before{
    content:"";position:absolute;left:0;right:0;top:50%;height:44px;transform:translateY(-50%)}
}

/* ===================================================================
   3. MEDIA TIERS - LAST, ordered by DESCENDING max-width (cascade law).
   =================================================================== */
@media (max-width:1139px){.sb-inner{grid-template-columns:auto minmax(0,1fr) auto;gap:22px}}
@media (max-width:860px){.sb-inner{grid-template-columns:minmax(0,1fr);gap:8px;padding:8px 16px 10px}.sb .burger{display:inline-flex}.sb .brand{font-size:18px}.sb-agent-d{display:none}.sb-agent-m{display:inline-flex}.sb-search{height:46px}.rail-left{top:0;height:auto;padding:20px 16px}.sb-try .hint{display:none}.sbf-row{grid-template-columns:minmax(0,1fr)}/* minmax(0,...) not 1fr: a grid item defaults to min-width:auto and refuses to shrink below its min-content, which at 320px pushed the footer bar 30px past the viewport on every page. Same guard .sb-inner above already uses. */}
@media (max-width:560px){.row{grid-template-columns:minmax(0,1fr)}.row .num{grid-column:1;grid-row:2;text-align:left;display:flex;gap:6px;align-items:baseline;margin-top:6px;min-width:0}.row .num small{margin:0}.row .facts{margin-top:6px}}
@media (max-width:420px){.sb-go .lbl{display:none}.sb-go{padding:0 11px}}

/* The header IS the brand at desktop, so the rail must not repeat it: the rail
   head (brand lockup + tagline) rendered directly under the header's own brand,
   two identical wordmarks stacked ~40px apart. Hidden from 861px up; kept in the
   phone drawer at <=860, where the drawer is a separate surface from the header
   and the lockup is the only branding inside it. The rail's top offset is
   unchanged - this hides the head, not the rail. */
@media (min-width:861px){.rail-head{display:none}}
