/* ==========================================================================
   jail411 — LIVE-SITE component layer (dark/light band system)
   ==========================================================================
   Standalone, additive file. Nothing here is wired into a layout yet and
   nothing in site.css (~842 rules) is edited or deleted. Load this AFTER
   site.css. Every colour below is a var() — the two exceptions are declared
   as new tokens in the reference block and called out by name.

   WHY THIS FILE EXISTS
   The retoken pass already pointed --navy/--red/--paper/etc at the live
   palette inside site.css's :root (see line ~21, the "LIVE SITE PALETTE"
   block). That pass did NOT invent the layout primitives the live site
   actually uses — full-bleed dark/light bands, a raised stat card, a dark
   map frame, pill chips. This file adds those primitives as a component
   layer so new views can assemble the live look without any more retoken
   surgery. See docs/CSS-COMPONENTS.md for full usage + supersession notes.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. TOKEN REFERENCE
   --------------------------------------------------------------------------
   (a) Aliases — the design brief names five tokens that don't exist under
       those names in site.css's :root. The values already exist under
       legacy names; these are aliases, not new colours.
         --surface        -> --paper        (#F0F4F8)
         --text-dark       -> --ink          (#0F172A)
         --text-dark-muted -> --muted        (#546378)
         --border-light    -> --rule         (#E2E8F0)
         --blue-wash       -> --red-soft     (#EFF6FF)
       Note --red is ALSO an alias already, upstream in site.css: the
       retoken pass repointed --red to #3B82F6, which is the same hex as
       --accent. They are the same colour under two names; this file always
       writes --accent for new code so intent stays legible.

   (b) Two genuinely new tokens, deliberately added here (not in the brief,
       not in site.css). Reason: solid-fill white-on-#3B82F6 buttons — the
       literal live-site look — measure ~3.7:1 contrast, below the 4.5:1 AA
       floor for normal text (a 14px bold label clears only the 3:1
       large-text bar, and the live buttons don't reliably clear even that
       at every viewport). Rather than silently soften the primary action,
       .btn--primary uses a darker fill from the same blue ramp:
         --accent-strong        #1D4ED8  (Tailwind blue-700)
         --accent-strong-hover  #1E40AF  (Tailwind blue-800)
       Full contrast figures for all of this are in docs/CSS-COMPONENTS.md
       and in the delivery summary — check ratios there before assuming a
       token is safe to reuse elsewhere.
   -------------------------------------------------------------------------- */
:root{
  --surface:var(--paper);
  --text-dark:var(--ink);
  --text-dark-muted:var(--muted);
  --border-light:var(--rule);
  --blue-wash:var(--red-soft);

  --accent-strong:#1D4ED8;
  --accent-strong-hover:#1E40AF;

  /* Ambient focus-ring colour. Bands repoint this per ground so one set of
     :focus-visible rules stays legible on both dark and light backgrounds
     instead of hand-writing two colours everywhere a focusable element
     might appear. Root default targets light-ground usage (nav, footer,
     any component used outside a band). */
  --focus-ring:var(--accent-strong);
}

/* --------------------------------------------------------------------------
   1. BAND SYSTEM — full-bleed alternating sections
   --------------------------------------------------------------------------
   DOM CONTRACT — read before wiring this in:
   .band is a direct child of <body> (a sibling of .wrap, NOT nested inside
   it), because .wrap is width:min(var(--shell),calc(100% - 32px)) and a
   block inside it can't go edge-to-edge without a bleed hack. That's how
   the live site does it (nav/ticker/hero/statbar/map/chips are all
   full-width siblings, and only their *contents* are shell-width).

   If a page template can't restructure around that (e.g. splicing a band
   into an existing .wrap-scoped view), use the .band--bleed modifier
   instead — it negative-margins out to the viewport. It works, but the
   escape trick unavoidably ignores the scrollbar gutter on desktop
   (~15-17px, browser dependent), producing a hairline that .band never has.
   Prefer the sibling-of-.wrap placement whenever the page owns its own
   structure.

   Every band's content sits in a nested .band__inner, which reproduces the
   .wrap shell (max-width:1320px via --shell, gutter clamp) so text and
   components line up with the rest of the page above/below the band.
   -------------------------------------------------------------------------- */
.band{width:100%}
.band--bleed{width:100vw;margin-inline:calc(50% - 50vw);padding-inline:calc(50vw - 50%)}
.band__inner{width:min(var(--shell),calc(100% - 32px));margin-inline:auto;padding-block:clamp(48px,7vw,88px)}
.band__inner--tight{padding-block:clamp(32px,4.5vw,56px)}

.band--dark{background:var(--navy);color:var(--text-primary)}
.band--deep{background:var(--deep);color:var(--text-primary)}
.band--light{background:var(--surface);color:var(--text-dark)}

/* Focus-visible per ground. Specificity (0,2,0) on the descendant rule
   beats the sitewide element-level `:focus-visible{outline:3px solid
   var(--gold)}` (0,1,0) in site.css regardless of load order, so every
   focusable thing inside a band gets a ring that's actually visible on
   that ground instead of gold-on-navy or gold-on-paper. */
.band--dark :focus-visible,.band--deep :focus-visible{--focus-ring:var(--cyan);outline:3px solid var(--focus-ring);outline-offset:3px;border-radius:4px}
.band--light :focus-visible{--focus-ring:var(--accent-strong);outline:3px solid var(--focus-ring);outline-offset:3px;border-radius:4px}

/* --------------------------------------------------------------------------
   2. STATE HERO — dark hero block, sits inside .band--dark .band__inner
   -------------------------------------------------------------------------- */
.state-hero{max-width:820px}
/* .state-hero__eyebrow is an alias of .eyebrow scoped to this block — both
   are supported because real usage reached for the BEM-shaped name before
   reading this file (natural guess given the rest of the block is BEM).
   Same treatment for __actions/__cta below. Keep both selectors in sync if
   either changes. */
.state-hero .eyebrow,.state-hero__eyebrow{display:block;margin:0 0 16px;color:var(--accent-light);font:700 .68rem/1.4 var(--mono);letter-spacing:.16em;text-transform:uppercase}
.state-hero h1{margin:0 0 22px;color:var(--white);font:800 clamp(2.6rem,5.6vw,3.8rem)/.98 var(--serif);letter-spacing:-.025em;text-wrap:balance}
.state-hero__lede{max-width:62ch;margin:0 0 30px;color:var(--text-primary);font-size:1.08rem;line-height:1.6}
.state-hero__actions,.state-hero__cta{display:flex;flex-wrap:wrap;gap:12px}

/* --------------------------------------------------------------------------
   3. BUTTONS — .btn / .btn--primary / .btn--ghost
   --------------------------------------------------------------------------
   NAME COLLISION, HANDLED, NOT RENAMED — site.css already declares a bare
   `.btn` (line 75) plus `.state-jump button,.btn{...}`. Loading this file
   after site.css does NOT silently change any existing page:
     - every current bare `class="btn"` markup (grep confirms just two,
       both in app/views/index-ice.php) is inside a compound selector that
       already wins on specificity: `.ice-actions .btn` and
       `.ice-filter .btn` are (0,2,0); this file's bare `.btn` is (0,1,0).
       The legacy look is untouched on both.
     - the only thing that changes is any FUTURE bare `class="btn"` used
       outside those two containers, which is exactly the intended
       component-layer takeover.
   Also worth knowing for whoever integrates: site.css's `.btn:hover`
   (line 76) sets `background:#8f392d`, a brick red left over from before
   the retoken pass — with --red now #3B82F6, that hover already reads as
   blue-to-red today, independent of anything in this file. Not this file's
   bug to fix (surgical scope), flagging so it isn't mistaken for one.
   -------------------------------------------------------------------------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:0 22px;border:1px solid transparent;border-radius:10px;font:700 .92rem/1 var(--ui);letter-spacing:.01em;text-decoration:none;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .1s ease}
.btn:active{transform:translateY(1px)}

/* Solid fill. --accent-strong, not --accent — see token reference above for
   why: white-on-#3B82F6 is ~3.7:1, this is ~6.7:1. The 1px accent-light
   border isn't decorative — it's what gets the button's own edge to a
   legible ~7:1 boundary against --navy per WCAG 1.4.11 (non-text UI
   contrast), since the fill alone only manages ~2.8:1 against navy. */
.btn--primary{background:var(--accent-strong);border-color:var(--accent-light);color:#fff}
.btn--primary:hover{background:var(--accent-strong-hover);border-color:var(--accent-light)}

/* Ghost needs to read correctly on both grounds, so its border/text ride a
   pair of local custom properties that .band--light repoints, same pattern
   as the focus-ring above. Un-banded default targets a dark ground. */
.btn--ghost{--btn-ghost-color:var(--text-primary);--btn-ghost-border:var(--border-dark);background:transparent;border-color:var(--btn-ghost-border);color:var(--btn-ghost-color)}
.btn--ghost:hover{background:rgba(255,255,255,.06);border-color:var(--accent-light);color:var(--white)}
.band--light .btn--ghost{--btn-ghost-color:var(--text-dark);--btn-ghost-border:var(--border-light)}
.band--light .btn--ghost:hover{background:rgba(29,78,216,.06);border-color:var(--accent-strong);color:var(--accent-strong)}

/* --------------------------------------------------------------------------
   4. STATBAR — raised dark card of large numerals, small-caps labels
   --------------------------------------------------------------------------
   Divider technique matches the existing .mast-meta pattern in site.css
   (line 77-78): the CONTAINER paints the divider colour, cells are opaque
   and sit in a 1px grid gap, so a divider only exists where two cells are
   actually adjacent — it disappears correctly when the grid reflows to
   fewer columns, instead of leaving an orphaned border-right hanging off
   the last cell in a row.

   Contrast note on the numerals: --accent on --card measures ~4.43:1,
   just under the 4.5:1 AA floor for NORMAL text. The numerals are set at
   clamp(1.7rem,3vw,2.3rem) (27px+) at weight 800, which clears the
   large-text threshold (18.66px bold / 24px normal needs only 3:1) with
   room to spare — so this is a large-text pass, not a near-miss. Don't
   reuse --accent-on-card at body-copy sizes elsewhere without re-checking.

   Label colour: the live site's actual muted grey-blue label
   (--text-secondary, #6b7c94) measures only ~3.83:1 on --card — fails AA
   for text this small. Labels use --text-primary instead (already the
   sitewide "on dark body copy" colour, ~13:1 on --card) and get their
   visual demotion from size/tracking/uppercase rather than a muted colour,
   since no available dark-ground muted token clears AA at this size.

   .statbar__n/.statbar__l are aliases of .statbar__num/.statbar__label —
   both class names are supported for the same reason as the state-hero
   aliases above. -------------------------------------------------------- */
.statbar{display:grid;grid-template-columns:repeat(6,1fr);gap:1px;background:var(--border-dark);border:1px solid var(--border-dark);border-radius:16px;overflow:hidden;box-shadow:0 24px 48px -24px rgba(0,0,0,.55)}
.statbar__cell{background:var(--card);padding:22px 14px;text-align:center}
.statbar__num,.statbar__n{display:block;color:var(--accent);font:800 clamp(1.7rem,3vw,2.3rem)/1 var(--serif);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.statbar__label,.statbar__l{display:block;margin-top:6px;color:var(--text-primary);font:600 .64rem/1.3 var(--mono);letter-spacing:.1em;text-transform:uppercase}

/* --------------------------------------------------------------------------
   5. CHIPS — pill links with a trailing count ("Major Cities")
   --------------------------------------------------------------------------
   Light-ground component by default (matches the live site: chips sit in
   the white "Major Cities" section, not on the dark map band). If a page
   ever needs chips on a dark band, add a .band--dark .chips override —
   not built here since nothing in the brief calls for it and speculative
   variants aren't in scope.
   -------------------------------------------------------------------------- */
.chips{display:flex;flex-wrap:wrap;gap:10px;list-style:none;margin:0;padding:0}
.chip{display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:0 16px;background:var(--white);border:1px solid var(--border-light);border-radius:999px;color:var(--text-dark);font:700 .88rem/1 var(--ui);text-decoration:none}
.chip:hover{border-color:var(--accent);color:var(--accent-strong)}
.chip__count{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:20px;padding:0 6px;background:var(--blue-wash);border-radius:999px;color:var(--accent-strong);font:700 .74rem/1 var(--sans);font-variant-numeric:tabular-nums}

/* --------------------------------------------------------------------------
   6. MAPFRAME — dark rounded container for an inline SVG map,
      with a legend block and a quick-info sidebar slot
   --------------------------------------------------------------------------
   CANONICAL markup — legend is a direct child of .mapframe, a SIBLING of
   .mapframe__stage, not nested inside it:
     <div class="mapframe">
       <aside class="mapframe__sidebar">
         <div class="mapframe__quickinfo">...</div>
         <div class="mapframe__filters">...</div>
       </aside>
       <div class="mapframe__stage"><svg>...</svg></div>
       <div class="mapframe__legend">...</div>
     </div>
   This replaces an earlier draft of this file that nested .mapframe__legend
   INSIDE .mapframe__stage and positioned it with position:absolute. That
   version shipped a real bug: .mapframe (the intended containing block)
   had no `position` set, so a legend that *wasn't* nested in the stage —
   exactly the shape first-pass integration markup reached for — had no
   positioned ancestor at all, fell back to the initial containing block,
   and rendered overlapping the sidebar instead of the map. Root cause was
   the CSS depending on unenforced DOM nesting instead of DOM-order-proof
   placement.

   Fixed with grid-template-areas: .mapframe__stage and .mapframe__legend
   are both placed into the SAME named area ("stage"). That's independent
   of source order and independent of which is nested inside the other, so
   either markup shape lands the legend over the map's lower-left corner.
   Legacy nested-in-stage markup is still supported below via an explicit
   override (kept for anything already built against the earlier draft).
   -------------------------------------------------------------------------- */
.mapframe{display:grid;grid-template-columns:280px minmax(0,1fr);grid-template-areas:"sidebar stage";gap:24px;padding:clamp(20px,3vw,32px);background:var(--navy);border:1px solid var(--border-dark);border-radius:20px}
.mapframe__sidebar{grid-area:sidebar;display:flex;flex-direction:column;gap:20px}
.mapframe__stage{grid-area:stage;position:relative;min-height:420px;background:var(--deep);border:1px solid var(--border-dark);border-radius:14px;overflow:hidden}
.mapframe__stage svg{display:block;width:100%;height:100%}

/* Legend shares the "stage" grid area and self-aligns to its lower-left
   corner — a normal grid item, not position:absolute, so it needs no
   containing-block gymnastics. This is the primary/recommended path. */
.mapframe__legend{grid-area:stage;align-self:end;justify-self:start;position:relative;z-index:2;margin:16px;background:rgba(11,18,34,.86);border:1px solid var(--border-dark);border-radius:10px;padding:12px 14px;backdrop-filter:blur(2px)}
/* Legacy path: legend nested INSIDE .mapframe__stage instead of alongside
   it. grid-area/align-self/justify-self are inert here (not a grid item of
   .mapframe once nested), so this resets to position:absolute against the
   stage's own `position:relative`, which still works correctly. */
.mapframe__stage>.mapframe__legend{position:absolute;left:16px;bottom:16px;margin:0}

.mapframe__legend h4{margin:0 0 8px;color:var(--text-primary);font:600 .64rem/1.2 var(--mono);letter-spacing:.1em;text-transform:uppercase}
.mapframe__legend-item{display:flex;align-items:center;gap:8px;color:var(--text-primary);font:600 .8rem/1.4 var(--sans)}
.mapframe__legend-item+.mapframe__legend-item{margin-top:6px}
.mapframe__legend-dot{width:9px;height:9px;flex:none;border-radius:50%}

/* .mapframe__filters / .mapframe__quickinfo text originally used
   --text-secondary (#6b7c94), which measures ~3.83:1 on --card — the same
   AA failure already identified for statbar labels above, just not yet
   applied here. Fixed the same way: --text-primary, demotion via
   size/weight instead of a muted colour that doesn't clear AA at this
   size on this background. */
.mapframe__filters{background:var(--card);border:1px solid var(--border-dark);border-radius:12px;padding:16px}
.mapframe__filters h3{margin:0 0 4px;color:var(--white);font:700 .92rem/1.3 var(--ui)}
.mapframe__filters p{margin:0 0 14px;color:var(--text-primary);font-size:.78rem}
.mapframe__filter-item{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:40px;padding:0;color:var(--text-primary);font:600 .88rem/1.3 var(--sans)}
.mapframe__filter-item input{accent-color:var(--accent)}
.mapframe__filter-item span{margin-left:auto;color:var(--text-primary);font:600 .78rem/1 var(--mono);font-variant-numeric:tabular-nums}

.mapframe__quickinfo{margin:0;background:var(--card);border:1px solid var(--border-dark);border-radius:12px;padding:18px}
.mapframe__quickinfo>div{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:34px;border-bottom:1px solid var(--border-dark)}
.mapframe__quickinfo>div:last-of-type{border-bottom:0}
.mapframe__quickinfo dt{margin:0;color:var(--text-primary);font:600 .78rem/1.3 var(--sans)}
.mapframe__quickinfo dd{margin:0;color:var(--white);font:700 .92rem/1.3 var(--ui);font-variant-numeric:tabular-nums}
.mapframe__quickinfo nav{display:flex;flex-direction:column;gap:8px;margin-top:12px}
.mapframe__quickinfo nav a{color:var(--accent-light);font:700 .82rem/1.3 var(--sans);text-decoration:none}
.mapframe__quickinfo nav a:hover{color:var(--white)}

@media(max-width:1000px){
  .mapframe{grid-template-columns:1fr;grid-template-areas:"sidebar" "stage"}
  .mapframe__sidebar{flex-direction:row;flex-wrap:wrap}
  .mapframe__filters,.mapframe__quickinfo{flex:1 1 260px}
  .statbar{grid-template-columns:repeat(3,1fr)}
}
@media(max-width:760px){
  .band__inner{width:min(100% - 16px,var(--shell))}
  .state-hero h1{font-size:clamp(2.3rem,10vw,3rem)}
  .mapframe{padding:16px}
  .mapframe__stage{min-height:320px}
  .mapframe__sidebar{flex-direction:column}
  .statbar{grid-template-columns:repeat(2,1fr)}
  .statbar__cell{padding:18px 10px}
  .statbar__num,.statbar__n{font-size:1.6rem}
}

/* .band--bleed uses width:100vw, which on desktop includes the scrollbar gutter and
   overflows the page by its width. `clip` rather than `hidden` deliberately: an ancestor
   with overflow-x:hidden silently kills position:sticky on descendants, and the spec makes
   the site header sticky on this page. */
html,body{overflow-x:clip}

/* Closed facilities. Amber rather than red: these are not warnings or errors, they are
   history. Red on this site means "do this", and there is nothing to do here. */
.state-closed>.band__inner>header{max-width:var(--measure);margin-bottom:30px}
.state-closed h2{margin:0 0 14px}
.state-closed>.band__inner>header p{color:var(--text-dark-sub)}
.state-closed h3{margin:34px 0 14px;font:700 .95rem/1.3 var(--ui);color:var(--text-dark-muted);
  text-transform:uppercase;letter-spacing:.06em}

.closed-timeline{list-style:none;margin:0;padding:0;max-width:none;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:12px}
.closed-timeline>li{padding:20px 22px;background:var(--white);
  border:1px solid var(--border-light);border-left:3px solid var(--amber);border-radius:12px}
.closed-when{display:block;margin-bottom:7px;color:var(--amber);
  font:600 .72rem/1 var(--mono);letter-spacing:.09em;text-transform:uppercase}
.closed-timeline b{display:block;font:700 1rem/1.3 var(--ui);color:var(--text-dark)}
.closed-where{display:block;margin-top:3px;color:var(--text-dark-muted);font-size:.84rem}
.closed-timeline p{margin:11px 0 0;color:var(--text-dark-sub);font-size:.87rem;line-height:1.55}

/* The undated majority. Denser, because without a date each entry carries less. */
.closed-list{list-style:none;margin:0;padding:0;max-width:none;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:7px}
.closed-list>li{padding:11px 15px;background:var(--white);
  border:1px solid var(--border-light);border-radius:9px;font-size:.88rem}
.closed-list b{font:600 .9rem/1.3 var(--ui);color:var(--text-dark)}
.closed-list span{display:block;margin-top:2px;color:var(--text-dark-muted);font-size:.8rem}

@media (max-width:760px){
  .closed-timeline,.closed-list{grid-template-columns:1fr}
}

/* The one link that replaces the verification stamp under each procedure. It is the agency's
   own page — locator, visiting scheduler, deposit vendor — and is the most actionable thing
   in the band, so it reads as an action rather than a footnote. */
.proc-official{margin:14px 0 0}
.proc-official a{color:var(--accent-light);font:600 .92rem/1.4 var(--ui);text-decoration:none;
  border-bottom:1px solid rgba(96,165,250,.4);padding-bottom:1px}
.proc-official a:hover{border-bottom-color:var(--accent-light)}

/* The escalation ladder. Tokenised rather than using the spec's literal hexes, so it
   follows the palette if the palette moves. --accent-hover (#1D4ED8) on the numeral pills
   and count figures rather than --accent (#3B82F6): white on #3B82F6 measures 3.68:1 and
   fails AA, which is the same reason .btn--primary uses the darker blue. */
.escalation h2{margin:0 0 14px}
.escalation__lede{max-width:var(--measure);color:var(--text-dark-sub)}
.escalation__steps{display:grid;gap:16px;margin:32px 0 0;padding:0;max-width:none;list-style:none;
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.escalation__step{position:relative;padding:24px 24px 24px 64px;background:var(--white);
  border:1px solid var(--border-light);border-radius:12px}
.escalation__n{position:absolute;top:24px;left:20px;width:30px;height:30px;display:grid;
  place-content:center;border-radius:50%;background:var(--accent-hover);color:#fff;
  font:800 .875rem/1 var(--ui)}
.escalation__step h3{margin:0 0 8px;font:700 1.0625rem/1.3 var(--ui);letter-spacing:-.015em}
.escalation__step p{margin:0 0 12px;font-size:.9375rem;line-height:1.6}
.escalation__count{color:var(--text-dark-sub);font-size:.875rem}
.escalation__count strong{color:var(--accent-hover);font-variant-numeric:tabular-nums}
/* The load-bearing state: a rung with no coverage says so and forwards, rather than
   leaving the reader to discover a dead end by phoning it. */
.escalation__count--absent{color:var(--text-dark-muted);font-style:italic}
.escalation__step a{display:inline-flex;min-height:44px;align-items:center;font-weight:700}
.escalation__step a:focus-visible{outline:3px solid var(--accent);outline-offset:3px}
.escalation__foot{margin:24px 0 0;color:var(--text-dark-sub);font-size:.9375rem}
@media (max-width:767px){.escalation__steps{grid-template-columns:1fr}}

/* The facility map band. Dark, because it is orientation rather than action. */
.state-mapband>.band__inner>header{max-width:var(--measure);margin-bottom:26px}
.state-mapband h2{margin:0 0 12px;color:var(--text-primary)}
.state-mapband>.band__inner>header p{color:var(--text-secondary)}
.state-mapband .mapframe__stage svg{width:100%;height:auto;display:block}
/* The completeness disclosure. Deliberately NOT styled as fine print: a partial map that
   whispers about being partial is the dishonest version. */
.state-mapband__count{max-width:var(--measure);margin:22px 0 0;color:var(--text-secondary);
  font-size:.9rem;line-height:1.6;padding-left:14px;border-left:2px solid var(--amber)}

/* County parole block ------------------------------------------------------
   Added when Roger asked why San Diego has no downtown parole office. The
   answer is that CDCR has none, and the note carries that: an explained
   absence is information, an unexplained one reads as missing data.

   Phones are the reason this block exists, so they are the loudest thing in
   each row and are tap-to-call on a phone, where most of this traffic is. */
.county-parole{margin:32px 0}
.county-parole > h2{margin-bottom:10px}

.parole-note{
  margin:0 0 16px;padding:12px 14px;max-width:var(--measure);
  background:var(--red-soft);border-left:3px solid var(--accent-hover);
  border-radius:0 6px 6px 0;color:var(--ink);font-size:.95rem;line-height:1.5;
}

.parole-list{list-style:none;margin:0;padding:0;display:grid;gap:10px;
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.parole-list > li{
  display:flex;flex-direction:column;gap:4px;
  padding:14px 16px;background:var(--paper-2);
  border:1px solid var(--rule);border-radius:8px;
}
.parole-list > li > b{font-family:var(--ui);font-weight:600;line-height:1.3}
.parole-where{color:var(--muted);font-size:.9rem;line-height:1.45}

/* Big enough to hit with a thumb without hunting for it. */
.parole-tel{
  margin-top:4px;align-self:flex-start;
  font-family:var(--mono);font-size:1.05rem;font-weight:600;
  color:var(--accent-hover);text-decoration:none;
  padding:4px 0;
}
.parole-tel:hover,.parole-tel:focus{text-decoration:underline}

.parole-foot{margin:12px 0 0;color:var(--muted);font-size:.875rem;max-width:var(--measure)}


/* County hub parole block. Retained deliberately: these belong to
   hub-county.php, not the parole hub, whose styling now lives in
   blue-pages.css as a committed world. */
/* Parole office grid -------------------------------------------------------
   Replaces a 12,820px single column in which the city name was set larger
   than the office, the address and the phone combined. 33 of California's 37
   cities hold one office each, so a full-width heading per city spent an
   entire screen row on a word.

   The card carries the reverse weight: office name loud, city demoted to a
   label, phone set in mono at tap size because ringing it is the point. */


/* site.css caps every ul at --measure (72ch) so prose lists stay readable. Correct for a
   list of sentences, wrong for a card grid -- it pinned both grids below to one column in a
   1280px viewport and left the right half of the page empty. Opt out explicitly. */

/* The official county custody search: the most useful outbound link on a county page.
   It used to be buried mid-sentence inside a provenance footnote. */
.county-official{margin:0 0 26px}
.county-official>a{font-family:var(--ui);font-weight:600;color:var(--accent-hover)}
/* CDCR unit designations. Set quieter than the office name but above it in the card, because
   it is the string on someone's paperwork -- the thing they arrive searching for, not the
   thing they browse by. */


/* City · county. The one place small-caps tracking is right: it is a locator,
   read as a label rather than as a sentence. */


/* CDCR unit designations — machine-issued identifiers, hence mono. */


/* Everything below is the action, pinned to the foot of the card. */


.office-map:hover,.office-map:focus,


/* The city jump nav sits above the grid and is a control, not prose. */

.parole-list{max-width:none}
.parole-list{max-width:none}

/* Stage 4 shared content components ---------------------------------------
   Dormant until a page-type migration supplies normalized component models.
   These selectors own modules only; site.css remains the global-system owner. */
.j411-component{color:var(--j411-copy)}
.j411-component a{color:var(--j411-blue)}
.j411-component address{font-style:normal}
.j411-component :focus-visible{outline:3px solid var(--j411-gold);outline-offset:3px}

.j411-hero{position:relative;isolation:isolate;overflow:hidden;background:var(--j411-navy);color:var(--j411-white)}
.j411-hero__media,.j411-hero__blend{position:absolute;inset:0;z-index:-1}
.j411-hero__media img{width:100%;height:100%;object-fit:cover}
.j411-hero__blend{background:linear-gradient(90deg,var(--j411-navy) 8%,rgba(6,27,58,.92) 42%,rgba(6,27,58,.24) 100%)}
.j411-hero__inner{min-height:360px;display:flex;align-items:center;padding-block:48px}
.j411-hero__content{max-width:var(--j411-measure)}
.j411-hero__title{color:var(--j411-white)}
.j411-hero__lede,.j411-hero__guardrail{color:var(--j411-sky)}
.j411-hero__actions{display:flex;flex-wrap:wrap;gap:12px}
.j411-hero--image-left .j411-hero__blend{transform:scaleX(-1)}
.j411-hero--image-left .j411-hero__content{margin-left:auto}
.j411-hero--compact .j411-hero__inner{min-height:240px}

.j411-source{padding:12px 14px;border-left:3px solid var(--j411-blue);background:var(--j411-ice)}
.j411-source__identity,.j411-source__agency,.j411-source__claim{margin:0 0 6px}
.j411-source__dates{display:flex;flex-wrap:wrap;gap:6px 18px;margin:0;padding:0;list-style:none;color:var(--j411-muted)}
.j411-source__state--due,.j411-source__state--pending{color:var(--j411-orange)}

.j411-contact{padding:24px;border:1px solid var(--j411-line);background:var(--j411-white)}
.j411-contact__details{display:grid;gap:14px;margin:0}
.j411-contact__row{display:grid;grid-template-columns:minmax(120px,.35fr) 1fr;gap:16px}
.j411-contact__row dt{color:var(--j411-muted);font-weight:700}
.j411-contact__row dd{margin:0;color:var(--j411-ink)}

.j411-status{padding:20px;border:1px solid var(--j411-line);border-left:5px solid var(--j411-blue);background:var(--j411-ice)}
.j411-status--closed{border-left-color:var(--j411-orange)}
.j411-status__label{margin:0;color:var(--j411-ink);font-weight:800;letter-spacing:.08em}
.j411-status__details{display:grid;gap:8px}
.j411-status__details div{display:grid;grid-template-columns:100px 1fr;gap:12px}
.j411-status__details dd{margin:0}

.j411-custody-search{padding:26px;border:1px solid var(--j411-line);background:var(--j411-ice)}
.j411-custody-search__agency,.j411-custody-search__caveat,.j411-custody-search__missing{color:var(--j411-muted)}

.j411-task{padding:24px;border-top:4px solid var(--j411-blue);background:var(--j411-ice)}
.j411-task__facts{display:grid;gap:10px}
.j411-task__facts div{display:grid;grid-template-columns:minmax(130px,.35fr) 1fr;gap:12px}
.j411-task__facts dd{margin:0}
.j411-task__steps,.j411-task__restrictions{display:grid;gap:8px}
.j411-task__steps span{display:block;color:var(--j411-muted)}
.j411-task__missing{padding:12px;border-left:3px solid var(--j411-orange);background:var(--j411-white)}

.j411-court-release{padding:24px;border:1px solid var(--j411-line)}
.j411-court-release__contacts{display:grid;gap:8px}
.j411-court-release__caveat{color:var(--j411-muted)}

.j411-supervision{padding-block:24px}
.j411-supervision__systems{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px}
.j411-supervision__system{padding:20px;border:1px solid var(--j411-line);background:var(--j411-white)}
.j411-supervision__assignment{font-weight:700;color:var(--j411-ink)}

.j411-juvenile{padding:24px;border-left:5px solid var(--j411-blue);background:var(--j411-ice)}
.j411-juvenile__fact{margin-block:8px}
.j411-juvenile__placements{padding-top:8px}

.j411-timeline{padding-block:24px}
.j411-timeline__events{display:grid;gap:16px;padding-left:24px}
.j411-timeline__event{padding:18px;border:1px solid var(--j411-line);background:var(--j411-white)}
.j411-timeline__event time{color:var(--j411-orange);font-weight:800}

.j411-related{padding-block:24px}
.j411-related ul{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin:0;padding:0;list-style:none}
.j411-related li{padding:18px;border:1px solid var(--j411-line);background:var(--j411-white)}
.j411-related__relationship{display:block;color:var(--j411-muted);font-size:.82rem;text-transform:uppercase;letter-spacing:.06em}

.j411-correction{padding:18px;border:1px solid var(--j411-line);background:var(--j411-ice)}
.j411-methodology{padding-block:24px;border-top:1px solid var(--j411-line);color:var(--j411-muted)}
.j411-methodology__meta{display:flex;flex-wrap:wrap;gap:8px 20px;padding:0;list-style:none}

.j411-sponsor{padding:20px;border:1px solid var(--j411-line);background:var(--j411-white)}
.j411-sponsor__disclosure{color:var(--j411-muted);font-size:.78rem;text-transform:uppercase;letter-spacing:.08em}
.j411-sponsor__business{color:var(--j411-ink);font-weight:800}
.j411-sponsor__creative{max-width:100%;height:auto}

/* Stage 7 facility-discovery primitives. These stay dormant until a page-type
   migration supplies normalized models; commercial content is never nested in
   factual result collections. */
.j411-facility-card{display:flex;flex-direction:column;gap:12px;padding:22px;border:1px solid var(--j411-line);border-radius:12px;background:var(--j411-white)}
.j411-facility-card--closed{border-left:5px solid var(--j411-orange)}
.j411-facility-card__meta{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0;color:var(--j411-muted);font-size:.8rem}
.j411-facility-card__status{color:var(--j411-ink);letter-spacing:.06em}
.j411-facility-card--closed .j411-facility-card__status{color:var(--j411-orange)}
.j411-facility-card__title,.j411-facility-card__location,.j411-facility-card__operator,.j411-facility-card__phone{margin:0}
.j411-facility-card__location,.j411-facility-card__operator{color:var(--j411-muted)}
.j411-facility-card__action{align-self:flex-start;margin-top:auto;font-weight:800}

.j411-facility-row{display:grid;grid-template-columns:minmax(220px,1.5fr) minmax(180px,1fr) auto;align-items:center;gap:18px;padding:16px 18px;border:1px solid var(--j411-line);background:var(--j411-white)}
.j411-facility-row--closed{border-left:5px solid var(--j411-orange)}
.j411-facility-row__identity,.j411-facility-row__facts{display:grid;gap:4px}
.j411-facility-row__identity>a,.j411-facility-row__action{font-weight:800}
.j411-facility-row__identity>span,.j411-facility-row__facts>span{color:var(--j411-muted);font-size:.86rem}
.j411-facility-row__facts>strong{color:var(--j411-ink);font-size:.78rem;letter-spacing:.06em}
.j411-facility-row--closed .j411-facility-row__facts>strong{color:var(--j411-orange)}

.j411-facility-types__list,.j411-county-selector__list{display:flex;flex-wrap:wrap;gap:10px;margin:0;padding:0;list-style:none}
.j411-facility-types__list a,.j411-county-selector__list a{display:flex;align-items:center;gap:10px;min-height:44px;padding:10px 14px;border:1px solid var(--j411-line);border-radius:999px;background:var(--j411-white);text-decoration:none}
.j411-facility-types__list a[aria-current="true"],.j411-county-selector__list a[aria-current="page"]{border-color:var(--j411-blue);background:var(--j411-ice)}
.j411-facility-types__list strong,.j411-county-selector__list strong{color:var(--j411-muted);font-size:.8rem}

.j411-facility-search{display:grid;gap:10px}
.j411-facility-search>label{font-weight:800;color:var(--j411-ink)}
.j411-facility-search__control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px}
.j411-facility-search__control>input{min-height:48px;padding:10px 14px;border:1px solid var(--j411-line);border-radius:8px;background:var(--j411-white);color:var(--j411-ink);font:inherit}
.j411-facility-search__count{margin:0;color:var(--j411-muted)}

@media (max-width:820px){
  .j411-hero__inner{min-height:320px}
  .j411-contact__row,.j411-task__facts div{grid-template-columns:1fr;gap:4px}
  .j411-facility-row{grid-template-columns:1fr auto}
  .j411-facility-row__facts{grid-column:1}
  .j411-facility-row__action{grid-column:2;grid-row:1 / span 2}
}
@media (max-width:560px){
  .j411-hero__inner{min-height:280px;padding-block:36px}
  .j411-hero__actions{display:grid}
  .j411-supervision__systems,.j411-related ul{grid-template-columns:1fr}
  .j411-facility-row,.j411-facility-search__control{grid-template-columns:1fr}
  .j411-facility-row__action{grid-column:1;grid-row:auto}
}
@media (max-width:360px){
  .j411-contact,.j411-task,.j411-court-release,.j411-juvenile{padding:18px}
}
/* Stage 16 exact-equivalent locale control. It appears only when both reviewed
   publications exist; absent controls do not reserve public-page space. */
.language-switcher{display:flex;align-items:center;gap:.35rem;font-size:.82rem;white-space:nowrap}
.language-switcher a{color:inherit;text-decoration:none;padding:.3rem .42rem;border-radius:.25rem}
.language-switcher a[aria-current="page"]{font-weight:750;text-decoration:underline;text-underline-offset:.2em}
.language-switcher__separator{opacity:.55}
.language-switcher a:focus-visible{outline:3px solid #f2a65a;outline-offset:2px}
@media(max-width:760px){.language-switcher{margin-left:auto;font-size:.78rem}}
