
/* PALETTE AT SOURCE, 2026-09-02. These seven values were brand_palette.css (248 B, one request, "the
   logo's colors applied to the app chrome, contrast-checked. Red stays crisis-only"), which loaded
   after this file and overrode the seven values that used to sit here (navy #2C4A6E, navy-deep
   #1F3550, gold #D9A441, gold-soft #F8EDDE, sky #2C4A6E, sky-soft #E9EEF4, gold-ink #7a5310). Those
   were dead: a future agent changing them here would have seen nothing move on screen. Folded in,
   brand_palette.css retired to _to_delete. Record: C:\!Brain\LFG4\NEVER_OVERWRITE\FOLDED_2026-09-02.md */
/* BRAND AT SOURCE 2026-09-18: values from brandskin_2026-09-17.css section 1, lifted here so the first
   painted frame is the brand. `[RYAN 2026-09-18]` "ONLY do the new branding kit app drop the other one. FIX."
   Woonsocket Burgundy #7A2636 is the signature, Warm Cream #FAF6EC the canvas, Deep Navy #102F3D the rules,
   Community Gold #C49A2A the hairlines and badge fills. Blue is demoted: no token below is blue.
   --green, --green-soft, --red, --red-soft are UNCHANGED on purpose: the 211 door and the crisis card read
   them (LAW L1). Heart Red #F00000 gets no token. Ratios beside each value (WCAG, computed 2026-09-18).
   Superseded 2026-09-02 values, kept for the undo:
   --navy:#0B4FB0; --navy-deep:#083C86; --gold:#F0CC00; --gold-soft:#FBF3D6; --sky:#0B4FB0; --sky-soft:#E4EEFB;
   --ink:#26282B; --muted:#595c61; --canvas:#F6F6F4; --border:#E4E4E0; --gold-ink:#6B4E00.
   Record: C:\!Brain\LFG4\NEVER_OVERWRITE\A1_TOKENS_2026-09-18.md. Undo: NEVER_OVERWRITE_BACKUPS\rebuild_2026-09-18\app.css.PRE_REBUILD */
:root{
  --navy:#7A2636; --navy-deep:#102F3D; --gold:#C49A2A; --gold-soft:#EDE7D9;   /* Burgundy on cream 9.02, on white 9.73; Navy on Gold 5.35 */
  --sky:#7A2636; --sky-soft:#EDE7D9; --green:#2F6B45; --green-soft:#E4EFE7;   /* green pair unchanged (crisis reads it) */
  --red:#A34A3A; --red-soft:#F6E9E5;                                          /* unchanged (crisis reads it) */
  --ink:#0D2018; --muted:#545F57; --surface:#FFFFFF; --canvas:#FAF6EC; --border:#E6DFCC;   /* Ink on cream 15.73; muted on cream 6.17, on white 6.66, on cream-deep 5.40 */
  --gold-ink:#102F3D;                                                         /* Deep Navy where small "gold" text used to sit: 13.01 on cream. Gold itself is 2.43 on cream and is never small text */
  /* BRAND 2026-09-18. The guide's palette, pixel-sampled in BRANDSPEC_2026-09-17.md. */
  --bw-burgundy:#7A2636; --bw-navy:#102F3D; --bw-gold:#C49A2A; --bw-gold-ink:#8A6A15;   /* --bw-gold-ink: small gold text, 4.69 on cream, 5.06 on white. NOT on cream-deep (4.10) */
  --bw-cream:#FAF6EC; --bw-cream-deep:#EDE7D9; --bw-ink:#0D2018; --bw-ink-muted:#545F57;
  --bw-rule:#D2CFC1; --bw-rule-burgundy:#904A57; --bw-white:#FFFFFF;
  /* callfix_2026-08-28.css reads these and nobody defined them; the blue fallbacks won. Defined now. */
  --card-alt:#FAF6EC; --card-alt-border:#D2CFC1;
  --radius:4px; --rule-w:3px; --track-label:.16em;   /* --track-label becomes .22em when Barlow Condensed lands (REBUILD_PLAN item 7) */
  /* The three voices. Stacks exactly as BRANDFONTS_2026-09-18.md section 6: 'Source Sans Pro' is the family
     name app_fonts.css registers the self-hosted Noto Arabic / Khmer / Lao / Bengali / KR / SC / TC subsets
     under, so it must sit in every stack before the generic or a Khmer heading falls to tofu. */
  --font-serif:"Cormorant Garamond",Georgia,"Source Sans Pro","Times New Roman",serif;
  --font-sans:"DM Sans","Source Sans Pro",system-ui,sans-serif;
  --font-label:"Barlow Condensed","Source Sans Pro",sans-serif;
  --serif:var(--font-serif); --sans:var(--font-sans); --label:var(--font-label);   /* BRANDFONTS names, aliased so either spelling resolves */
}
/* CRISIS PIN 2026-09-18. LAW L1: the crisis path does not change. The crisis subtree reads --ink, --muted, --surface and
   --border (crisis-when-line, .copt .csub, .cbrand, #res-helpbar-fixed), so moving the tokens above would have moved the
   crisis card's text ink and the fixed 911/988 strip's ground. Measured in a real browser 2026-09-18: 16 crisis nodes
   changed colour and #res-helpbar-fixed changed background in dark. These four rules pin every re-pointed token to its
   pre-rebuild value INSIDE the crisis containers only, the same mechanism BRANDSKIN2 used on 2026-09-17, so the computed
   style of every crisis element is what it was on 2026-09-17. Only the inherited font-family moves (to the sans, never the
   serif), as REBUILD_PLAN W6.6 allows. The 988 tile colour is a card for Ryan, not this edit. */
.res-crisis,.res-crisis-acc,#res-helpbar-fixed,#bw-safetyrow,.rrh-crisis,.res-211,#res-211,.crisis-sug,.emg-banner,.emg-call{--navy:#0B4FB0;--navy-deep:#083C86;--navy-fill:#0B4FB0;--sky:#0B4FB0;--sky-soft:#E4EEFB;--gold:#F0CC00;--gold-soft:#FBF3D6;--gold-ink:#6B4E00;--ink:#26282B;--muted:#595c61;--surface:#ffffff;--canvas:#F6F6F4;--border:#E4E4E0}
.res-crisis,.res-crisis-acc,.rrh-crisis,.crisis-sug,#res-helpbar-fixed,#bw-safetyrow{color:var(--ink)}   /* colour re-resolved here, not inherited as a computed value from .phone, so the pinned ink is what the subtree inherits. Class selectors only: an ID here (#res-211) would outrank .res-211{color:#fff} and darken the 211 door text, measured 2026-09-18 and removed. .res-211, .emg-banner, .emg-call, .rrh-* and .cq set their own colour later in the file and still win. */
.phone.dark .res-crisis,.phone.dark .res-crisis-acc,.phone.dark #res-helpbar-fixed,.phone.dark #bw-safetyrow,.phone.dark .rrh-crisis,.phone.dark .res-211,.phone.dark #res-211,.phone.dark .crisis-sug,.phone.dark .emg-banner,.phone.dark .emg-call{--ink:#e8eaed;--muted:#9aa0a6;--canvas:#16181b;--surface:#24262a;--border:#3a3d42;--sky-soft:#21354e;--gold-soft:#33301f;--navy:#7da7d9;--sky:#7da7d9;--gold-ink:#e0b25a;--navy-fill:#2C4A6E;--navy-deep:#083C86;--gold:#F0CC00}
.phone.high-contrast .res-crisis,.phone.high-contrast .res-crisis-acc,.phone.high-contrast #res-helpbar-fixed,.phone.high-contrast #bw-safetyrow,.phone.high-contrast .rrh-crisis,.phone.high-contrast .res-211,.phone.high-contrast #res-211,.phone.high-contrast .crisis-sug,.phone.high-contrast .emg-banner,.phone.high-contrast .emg-call{--ink:#000;--muted:#3a3d42;--surface:#fff;--canvas:#fff;--border:#000;--navy:#12335c;--sky:#12335c;--gold-ink:#5a3c08;--navy-fill:#0B4FB0}
.phone.dark.high-contrast .res-crisis,.phone.dark.high-contrast .res-crisis-acc,.phone.dark.high-contrast #res-helpbar-fixed,.phone.dark.high-contrast #bw-safetyrow,.phone.dark.high-contrast .rrh-crisis,.phone.dark.high-contrast .res-211,.phone.dark.high-contrast #res-211,.phone.dark.high-contrast .crisis-sug,.phone.dark.high-contrast .emg-banner,.phone.dark.high-contrast .emg-call{--ink:#fff;--muted:#d6dae0;--surface:#000;--canvas:#000;--border:#fff;--navy:#a9cdf5;--sky:#a9cdf5;--gold-ink:#ffd77a;--navy-fill:#2C4A6E}
*{box-sizing:border-box;margin:0;padding:0}
html,body{background:var(--bw-cream-deep)}   /* 2026-09-18 W3.1-19: was #dfe4e9 (blue-grey). Cream Deep, the guide's alternate band. */
/* 2026-09-04, REAL PHONE: PULL-TO-REFRESH IS OFF. `overscroll-behavior` appeared NOWHERE in any of
   the ~200 files this app serves (checked 2026-09-04), which means Android Chrome's pull-to-refresh
   was live on every page. This app pins a masthead at the top, so the gesture that reloads the whole
   page - a downward drag while already at scroll 0 - is the same gesture as "I want to see what is
   above this". An accidental reload costs her scroll position, her typed query and her open card,
   and on 2026-09-03 a reload against a stale service worker is what silently switched the shipped
   view out from under Ryan. `contain` stops the refresh and the scroll chaining; it does not stop
   any scrolling, and it has no effect at all on iOS Safari, which has no pull-to-refresh.
   Never applied to an inner scroller, only the root, so the org sheet and the suggestion list
   scroll exactly as before. Undo: delete these two lines. */
html{overscroll-behavior-y:contain}
body{overscroll-behavior-y:contain}
/* 2026-09-18 W3.1-18: was 'Source Sans Pro',sans-serif. var(--font-sans) is DM Sans, then 'Source Sans Pro' (the Noto safety net's family name), then system. */
body{font-family:var(--font-sans);color:var(--ink);-webkit-font-smoothing:antialiased;font-size:16px;line-height:1.55}
/* color set HERE (not on body) so inherited text re-resolves --ink inside .phone.dark and flips in dark mode. ROOT FIX 2026-06-26. */
/* NAVPIN 2026-09-09. [RYAN] "nav bar in the middle of the page that has to be job #1" and
   "it floats a little sometimes on my phone".
   .phone carried position:relative AND takes zoom:1.5/2 at .text-large/.text-xlarge (app.css:748-749).
   Either one can make it the containing block for a position:fixed descendant, and #bw-dock - the one
   fixed box holding the nav AND the 911/988/text-988 crisis floor - is a descendant of it
   (index.html:1508 inside index.html:615). Measured 2026-09-09 in headless Chromium: at .text-large
   #bw-dock.offsetParent became BODY and at .text-xlarge it became .phone itself, instead of null.
   Chromium still painted it pinned; Safari, which is what his phone runs, is documented to be less
   consistent with zoom and fixed positioning, which is why he sees float and no headless pass did.
   position:relative was audited before removal: zero rules in any stylesheet position an element
   absolutely against .phone. All 29 position:absolute rules across the CSS are ::before/::after
   pseudo-elements or components carrying their own positioning context.
   UNDO: NEVER_OVERWRITE_BACKUPS/navpin_2026-09-09/app.css.PRE_NAVPIN_* */
/* ==================================================================================================
   SPACE2 2026-09-28. THE FIRST SMALL-VIEWPORT UNIT IN THIS STYLESHEET, AND WHY IT IS `svh` AND NEVER
   `dvh`.
   `[RYAN 2026-09-28]` "screen space maximized even when the top bar url bar and bottom keyboard pop
   up and steal half the screen"
   SPACESCOUT_2026-09-28 section 6b: "Zero occurrences of `dvh`, `svh` or `lvh` in 568,714 bytes of
   app.css." Correct, and it matters, because `vh` IS `lvh`: it is the LARGE viewport, the height the
   screen would have if the browser chrome were gone. With the URL bar showing it is 25px bigger than
   the glass on iOS and 56px bigger on Android, so every `100vh` in this file has been quietly
   promising the layout 25 to 56 pixels that a resident cannot see.
   `svh` IS THE SMALL VIEWPORT: the height with the URL bar SHOWING. It is the honest floor.
   WHY NOT `dvh`, WHICH IS THE ONE EVERY ARTICLE RECOMMENDS. `dvh` FOLLOWS the bar as it collapses, so
   it changes value during a scroll, which means every element sized in it relayouts on the way down.
   That is the "nav floats sometimes" family this project has fought three times, and SPACESCOUT proved
   tonight that the masthead and the dock currently DO NOT float: header top 0.00 at five scroll
   positions, dock bottom equal to innerHeight at five scroll positions and four mid-session viewport
   changes, 9 of 9. `svh` IS A STATIC UNIT - it does not change when the bar collapses - so it cannot
   cause that thrash, and it is used here for that reason and no other. NO `dvh` AND NO `lvh` IS
   INTRODUCED ANYWHERE IN THIS PASS, and none should be without re-running that 9-of-9 proof.
   HOW IT IS WRITTEN: the `vh` declaration is KEPT and the `svh` one is added directly after it. A
   browser that does not know `svh` drops the second declaration and keeps the first, so nothing is
   deleted and nothing can fall back to the initial value. iOS Safari has had `svh` since 15.4.
   ================================================================================================== */
/* SPACE2 site 1 of 3. `min-height:100vh` on the app shell forced the page to be 25 to 56px taller
   than the glass on every phone even when there was nothing to scroll, which is phantom scroll room:
   a resident swipes, the page moves, and the only thing that happens is the URL bar animating. The
   `svh` line below makes the floor exactly the visible height with the bar showing. `.phone` is
   `position:static`, so nothing fixed or sticky is sized by this and the no-float proof cannot be
   touched by it. Undo: delete the second declaration. */
.phone{max-width:none;width:100%;margin:0;background:var(--canvas);min-height:100vh;min-height:100svh;color:var(--ink)}
/* 2026-09-18 W3.1-18: was h1,h2,h3,.josefin{font-family:'Josefin Sans'}. The label face carries h1, h3 and any h2 that is not a
   page heading; the page headings (.view > h2) take the serif at 28px, Ink on cream 15.73, weight 500 as BRANDSPEC A2 measures.
   A page heading carrying data-bwn (A2, index.html) draws the guide's opener above itself: 20px gold hairline, then the number
   in the label face, --bw-gold-ink 4.69 on cream. The serif is never used under 18px and never on a button, input, badge or the
   crisis strip (BRANDFONTS section 6). */
h1,h2,h3,.josefin{font-family:var(--font-label)}
.view > h2{font-family:var(--font-serif);color:var(--bw-ink);font-size:28px;line-height:1.15;font-weight:500;letter-spacing:0;text-transform:none}
.view > h2[data-bwn]{padding-top:11px;background:linear-gradient(var(--bw-gold),var(--bw-gold)) no-repeat 0 0 / 20px 1px}
/* NONUMBER 2026-09-23. `[RYAN 2026-09-23]` "dropping the 01 02 etc" and, asked which half goes,
   "Keep the word, drop digits only". The WORD is the h2's own text node, which is already in the
   page and already translated into sixteen languages, so it stays and it is untouched. Only the
   digit goes, and it goes here, at the one declaration that prints it, not by stripping data-bwn
   out of index.html: that attribute is the SELECTOR for the whole opener treatment (the gold
   hairline, the label face, the tracked caps, the serif sentence on ::after, the zero-tracking
   pair for Arabic, Khmer, Lao and Bengali) in around twenty rules across this file, and pulling it
   would take the brand off the openers along with the number. `content:none` means the
   pseudo-element is never generated, so the display, margin and tabular-nums declarations below at
   the second `.view > h2[data-bwn]::before` are inert rather than wrong, and nothing else moves.
   UNDO, one line: put `content:attr(data-bwn);` back at the front of the declaration below.
   Superseded, kept verbatim:
   .view > h2[data-bwn]::before{content:attr(data-bwn);display:block;...;margin:0 0 6px;font-variant-numeric:tabular-nums} */
.view > h2[data-bwn]::before{content:none;display:block;font-family:var(--font-label);font-weight:700;font-size:12px;line-height:1.2;letter-spacing:var(--track-label);color:var(--bw-gold-ink);margin:0 0 6px;font-variant-numeric:tabular-nums}
html[dir="rtl"] .view > h2[data-bwn]{background-position:100% 0}
.phone.dark .view > h2{color:var(--ink)}
.phone.dark .view > h2[data-bwn]::before{color:var(--bw-gold)}
.view > h2.section-label{font-family:var(--font-label);font-size:12px;line-height:1.2;font-weight:700;color:var(--bw-gold-ink);text-transform:uppercase;letter-spacing:var(--track-label)}   /* a section label that happens to be an h2 stays a label */
.view > h2.section-label:lang(ar),.view > h2.section-label:lang(km),.view > h2.section-label:lang(lo),.view > h2.section-label:lang(bn){letter-spacing:0}
.hidden{display:none!important}
/* 2026-09-18 W3.1-2: was #1668b3. The plan said gold; gold on cream is 2.43 and fails 1.4.11 (non-text 3:1).
   var(--navy) is Burgundy on cream 9.02 in light, gold on ink 6.47 in dark, #5C1C29 / #E2B85A in high contrast. */
button:focus-visible,a:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
/* 2026-08-08 (7.01): position was ABSOLUTE, whose offset parent is div.phone. With JavaScript
   OFF the <noscript> crisis block occupies ~469px above .phone, so top:-60px landed the link at
   top:409 SITTING ON TOP OF 'The rest of this app needs JavaScript. These numbers do not.' on the
   exact screen a person in crisis sees when app.js fails. Measured in a real browser in both JS
   states. FIXED is the standard skip-link pattern and is correct in both, because it stops
   depending on where .phone happens to start. It did not cover a number; it covered the sentence
   explaining why the page looks like that. */
/* SCREENSPACE 2026-09-02: top:-60px was a guess about the link's own height, and at the 200 percent
   step it is wrong: inside .phone's zoom the link wraps to 139px (English) and 189px (Greek) real px,
   so its bottom 19-69px poked into the top of the screen as a navy bar painted over the masthead
   title, unfocused, on every view (measured in a real Chromium at 320x568/200%, en and el,
   _HARNESS/_screenspace_2026-09-02/c211fix3_320_200_el.png shows it). bottom:100% parks its bottom
   edge exactly on the viewport's top edge whatever its height; :focus brings it to top:0 as before. */
.skip-link{position:fixed;inset-inline-start:8px;top:auto;bottom:100%;z-index:2000;background:var(--navy);color:#fff;padding:10px 16px;border-radius:0 0 var(--radius) var(--radius);font-weight:700;text-decoration:none;font-family:inherit}   /* SHAPE 2026-09-22: was border-radius:0 0 12px 12px */
.skip-link:focus{top:0;bottom:auto}
/* ADDED 2026-09-04. Same defect as .cl-callbtn: `background:var(--navy);color:#fff` above, and
   line 444 redefines --navy to the light #7da7d9 for `.phone.dark`, so the skip link became white
   on light blue. Measured 2026-09-04, real Chromium: 2.50:1, AA needs 4.5:1. This is the FIRST
   control a keyboard or switch user reaches on every screen, so it is not a cosmetic miss.
   #2C4A6E is the token org_card_2026-08-23.css:102 already uses for the same problem: 9.08:1. */
.phone.dark .skip-link{background:#7A2636;color:#FAF6EC}   /* 2026-09-18 W3.2: was #2C4A6E/#fff; Burgundy, cream 9.02 */
#content:focus{outline:none}
@media (prefers-reduced-motion: reduce){*{transition:none!important;animation:none!important}}

/* header */
/* 2026-09-18 W3.1-1: was background:#2C4A6E;color:#fff. Burgundy masthead, cream text 9.02 (agrees with brand.css). */
.hdr{background:var(--bw-burgundy);color:var(--bw-cream);padding:14px 16px 12px;position:sticky;top:0;z-index:40}
.hdr-row{display:flex;align-items:center;gap:10px;max-width:820px;margin:0 auto;flex-wrap:wrap}
.bw-mark{width:40px;height:40px;border-radius:var(--radius);background:rgba(255,255,255,.14);border:1.5px solid rgba(255,255,255,.5);color:#fff;display:flex;align-items:center;justify-content:center;font-family:var(--font-label);font-weight:700;font-size:15px;letter-spacing:.04em;text-align:center;line-height:1.1;flex:none;overflow:hidden}   /* 2026-09-18 W3.1-25: was border-radius:10px */
.hdr-title{font-family:var(--font-label);font-weight:700;font-size:17px;letter-spacing:.04em;text-transform:uppercase;line-height:1.1}
.hdr-sub{font-size:12px;opacity:.95;text-wrap:pretty}/* 042 (Ryan): text-wrap:pretty stops a lone last word ("place") orphaning to its own line, for any tagline in any language. */
.hdr-right{margin-inline-start:auto;display:flex;align-items:center;gap:8px}
.lang-toggle{background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.4);color:#fff;font-family:var(--font-label);font-weight:700;font-size:12px;letter-spacing:.04em;padding:6px 11px;border-radius:var(--radius);cursor:pointer;min-height:44px}   /* SHAPE 2026-09-22: was border-radius:999px (a pill BUTTON; BRANDSPEC A7 line 248: the guide has ONE pill and it is a label, not a button) */
/* 047 (0.5): dead .find-help-btn rules removed. The top-right find-help button was retired (RULING 1: find-help lives on Home + the bottom nav, Settings keeps the top-right gear). No markup references .find-help-btn/.fh-ic. */

/* layout */
/* JANK_INFO_NEWS_SETTINGS 2026-09-02: .view's bottom padding was a hand-typed 120px, guessed once
 * and never revisited, reserving space for the fixed bottom nav dock the same way .view's own
 * comment history already fixed the TOP of the screen (--bw-topfixh / stickreserve_2026-08-30.js) -
 * but the bottom used a literal number instead of the dock's own LIVE height, --bw-dockh, which
 * bottom_dock_2026-08-15.js already measures and publishes for exactly this purpose (it already
 * drives `scroll-padding-bottom` a few hundred lines below). MEASURED this session, real headless
 * Chromium, Settings page, 320x568, text size Extra Large (`_HARNESS/_jank_infonews_settings_2026-09-02/
 * probe_settings_dock.mjs`): --bw-dockh reads 170.6px - 50.6px MORE than the guessed 120px - so the
 * last ~50px of scrolled content sat behind the dock with no further scroll able to reveal it.
 * Ryan named exactly this: "The read-aloud row can sit physically behind the fixed bottom nav
 * depending on scroll position." This is not News/Info/Settings-specific; `.view` is shared by every
 * page, so this is the one shared cause and the one shared fix, forever, for all of them. 120px stays
 * as the fallback for the instant before bottom_dock_2026-08-15.js's first measurement lands (same
 * value as before, never a regression) and for a build that never loads that script.
 * Guarded by: `_HARNESS/_jank_infonews_settings_2026-09-02/probe_settings_dock.mjs`. Undo: restore
 * from app.css.bak_20260902_171642_newssearchbox. */
/* VIEWZOOM 2026-09-28 (DOCK2). DOCKFIX's row 5, closed. SUPERSEDED VALUE, UNDO IS ONE LINE:
     .view{padding:16px 16px calc(var(--bw-dockh, 120px) + 16px);max-width:820px;margin:0 auto}
   --bw-dockh is the dock's getBoundingClientRect().height, RENDERED pixels. `.view` is inside
   `.phone`, so this length is multiplied by .phone's zoom before it is painted: 1 at Normal, 1.5 at
   Large, 2 at Extra Large. It has always OVER-reserved, so it never hid anything; it is wasted
   screen space at exactly the sizes where there is least of it. --bw-viewzoom is MEASURED off
   `.phone` by bottom_dock_2026-08-15.js measure() (rendered rect width over the unzoomed computed
   width) and is NOT the same number as --bw-dockzoom: the dock is counter-zoomed by app.css:1548 at
   Extra Large and `.view` is not, so the dock's is 1.5 there and this one is 2. Nothing is typed.
   Fallback 1 is today's behaviour byte for byte. Undo: put the line above back. */
.view{padding:16px 16px calc(var(--bw-dockh, 120px) / var(--bw-viewzoom, 1) + 16px);max-width:820px;margin:0 auto}
/* 2026-09-18 W3.1-24: was border:1px solid var(--border);border-radius:16px;padding:16px;margin-bottom:12px. The guide's card:
   white on cream, 4px corners, no border, no shadow, 20px padding. In dark a 1px hairline keeps the navy card off the ink ground.
   #home-updates lost its inline blue border-left in index.html (A2); it takes the guide's Deep Navy rule here. */
.card{background:var(--surface);border:0;border-radius:var(--radius);padding:20px;margin-bottom:14px;box-shadow:none}
.phone.dark .card{border:1px solid var(--border)}
#home-updates.card{border-left:var(--rule-w) solid var(--bw-navy)}
html[dir="rtl"] #home-updates.card{border-left:0;border-right:var(--rule-w) solid var(--bw-navy)}
/* 2026-09-18 W3.1-17: THE SECTION OPENER. The guide introduces every section the same way: a short gold hairline, then a
   numbered tracked label. Was font-family:'Josefin Sans';letter-spacing:.08em;color:var(--muted);margin:18px 0 8px.
   Label in --bw-gold-ink #8A6A15 (4.69 on cream, 5.06 on white; gold itself is 2.43 and is never small text). The 20px by 1px
   Community Gold hairline is a background so no element is added. The number comes from data-bwn (digits only, index.html,
   A2) via ::before. Tracking is zeroed for Arabic, Khmer, Lao and Bengali (joining and combining marks). Dark: gold on ink 6.47.
   Copied from brandskin_2026-09-17.css section 3, de-gated, !important dropped. */
.section-label{display:block;position:relative;font-family:var(--font-label);font-weight:700;text-transform:uppercase;letter-spacing:var(--track-label);font-size:12px;line-height:1.2;color:var(--bw-gold-ink);margin:26px 0 8px;padding:11px 0 0;background:linear-gradient(var(--bw-gold),var(--bw-gold)) no-repeat 0 0 / 20px 1px}
.section-label:first-child{margin-top:4px}
.section-label:lang(ar),.section-label:lang(km),.section-label:lang(lo),.section-label:lang(bn){letter-spacing:0}
html[dir="rtl"] .section-label{background-position:100% 0}
/* NONUMBER 2026-09-23, Home's three bands. Same ruling, same one-word change as the view openers
   above: `[RYAN 2026-09-23]` "Keep the word, drop digits only". "01 NEWS", "02 COMING UP" and
   "03 QUICK LINKS" become "NEWS", "COMING UP" and "QUICK LINKS". The words are the elements' own
   translated text nodes and are untouched. The gold hairline, the label face and the tracking all
   survive, because they hang off `.section-label` and off the `[data-bwn]` selector, not off the
   digit. UNDO, one line: put `content:attr(data-bwn);` back at the front.
   Superseded, kept verbatim:
   .section-label[data-bwn]::before{content:attr(data-bwn);display:inline-block;margin-inline-end:.9em;font-variant-numeric:tabular-nums} */
.section-label[data-bwn]::before{content:none;display:inline-block;margin-inline-end:.9em;font-variant-numeric:tabular-nums}
.section-label[data-bwn]::after{content:none}
.phone.dark .section-label,.phone.dark .section-label[data-bwn]::before{color:var(--bw-gold)}
/* the News list's own sub-headings ("Urgent", "Affects you") are rendered by app.js each time, so they are numbered by CSS counter */
#updates-list{counter-reset:bwsec}
#updates-list .upd-sec{counter-increment:bwsec;display:block;position:relative;font-family:var(--font-label);font-weight:700;font-size:12px;line-height:1.2;letter-spacing:var(--track-label);text-transform:uppercase;color:var(--bw-gold-ink);margin:24px 0 8px;padding:11px 0 0;background:linear-gradient(var(--bw-gold),var(--bw-gold)) no-repeat 0 0 / 20px 1px}
#updates-list .upd-sec:first-child{margin-top:4px}
/* ONENUMBER 2026-09-23. FINAL_2026-09-23 section 8 item 8: "News runs two numbering systems on one
   screen", the page opener reading "05 NEWS" and its first group reading "01 URGENT" nine lines
   below it. BRANDSPEC A7 numbers SECTIONS, "01" to "09", one per band, and its whitespace principle
   is "one idea per band ... No two competing headings on one screen". Urgent / Affects you / Around
   town are groups inside section 05, not sections, so they keep the gold hairline and the tracked
   caps and lose the number. Nothing else changes: the counter-increment on .upd-sec is left in place
   so the numbers can be put back by uncommenting one line, and no string and no markup is touched.
   Undo: uncomment the declaration below.
#updates-list .upd-sec::before{content:counter(bwsec,decimal-leading-zero);margin-inline-end:.9em;font-variant-numeric:tabular-nums}
*/
#updates-list .upd-sec-urgent{color:var(--bw-burgundy)}   /* Burgundy carries urgency, never red. 9.02 on cream */
#updates-list .upd-sec:lang(ar),#updates-list .upd-sec:lang(km),#updates-list .upd-sec:lang(lo),#updates-list .upd-sec:lang(bn){letter-spacing:0}
html[dir="rtl"] #updates-list .upd-sec{background-position:100% 0}
.phone.dark #updates-list .upd-sec,.phone.dark #updates-list .upd-sec-urgent{color:var(--bw-gold)}

/* home */
.greet h2{font-size:22px;color:var(--navy)}
.greet .date{color:var(--muted);font-size:14px;margin-top:2px}
/* 2026-09-18 W3.1-10: was border-radius:16px and a #2C4A6E to #46688F gradient. Flat Deep Navy, the guide's mission-card idiom; gradients appear nowhere in the guide. Cream on navy 13.01. */
.hero{height:158px;border-radius:var(--radius);background:var(--bw-navy);color:var(--bw-cream);background-size:cover;background-position:center;position:relative;overflow:hidden;margin-bottom:12px;cursor:pointer}
/* NAVYFILL 2026-09-23, CONSIDERED AND DELIBERATELY NOT CHANGED. This rule is the third of the six that
   FINAL_2026-09-23 section 7 lists as pairing `background:var(--navy)` with white text, which would paint
   white on Community Gold at 2.62:1 anywhere it landed in dark mode. It was changed to
   `var(--navy-fill,var(--navy))` here and then PUT BACK, because `decision_invariants.mjs` invariant
   `red_is_alert_only` reads this exact literal, `/\.topbar\{[^}]*background:var\(--navy\)/`, to prove the
   base topbar is not red, and the edit took that gate from GREEN to RED. The gate is right to be strict and
   the edit fixed nothing real: `.phone.dark .topbar` two hundred lines below already pins this bar to
   Burgundy #7A2636 with cream #FAF6EC, 9.02:1, so the white-on-gold pairing never actually paints. Changing
   the token here would need the invariant's regex changed with it, and decision_invariants.mjs is not mine
   to open. HANDED BACK, not silently dropped. */
.topbar{display:flex;align-items:center;gap:8px;background:var(--navy);color:#fff;padding:7px 14px;font-size:13.5px;line-height:1.35;cursor:pointer;border-top:1px solid rgba(255,255,255,.16);position:sticky;top:var(--hdrh,60px);z-index:38}
/* Constrain the topbar text to the SAME 820px content column as the header + body, so it aligns with everything else: left-aligned, perfect on phone, and not floating on a maximized website. */
.topbar-inner{display:flex;align-items:center;gap:8px;width:100%;max-width:820px;margin:0 auto;box-sizing:border-box}
.topbar.alert{background:#B23A2E}
/* R123 (099, 2026-07-26): RED IS RESERVED FOR EMERGENCIES. "notice" is the prominent, non-emergency register: a deep green that reads as news rather than danger, at 6.42:1 against white, measured not estimated, so it clears WCAG AA for body text. Enforced by emergency_red_gate.mjs, which fails if any non-crisis selector claims the emergency red. */
.topbar.notice{background:var(--bw-navy)}   /* 2026-09-18 W3.1-12: was #1E6B4F (green). Cream/white on navy 13.01. .topbar.alert red is untouched. */
/* R124 (099, 2026-07-26): Ada's tap-target floor. Measured at 20 px on a real 430 px render, which
   is a hard target for an older adult with one finger on a cracked phone. 44 px is the floor, and
   it lives here rather than in an inline style so it survives the next edit. The banner close
   button .tb-x already clears the floor through its 44 x 44 ::before hit area, verified by a real
   click at the edge of that box, so it is deliberately left alone. */
.upd-open{display:inline-flex;align-items:center;min-height:44px;color:var(--navy);font-weight:700;text-decoration:none}
/* THIN BAR + WCAG 2.5.5: the visible X box is small so the announcement bar stays THIN. The 44x44 accessibility
   tap target is provided by the ::before overlay below (absolute, does NOT add height). DO NOT revert this to a
   44px width/height box - that fattens the bar (reverted 4x through 071; guarded by predeploy_gate "topbar_thin"). */
.topbar .tb-x{position:relative;margin-inline-start:auto;background:transparent;border:none;color:#fff;width:24px;height:24px;min-width:24px;min-height:24px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--radius);font-size:18px;opacity:.82;cursor:pointer;flex:none;line-height:1}   /* SHAPE 2026-09-22: was border-radius:8px */
.topbar .tb-x::before{content:"";position:absolute;top:50%;left:50%;width:44px;height:44px;transform:translate(-50%,-50%)}
.topbar .tb-x:hover,.topbar .tb-x:focus-visible{background:rgba(255,255,255,.20);opacity:1;outline:2px solid rgba(255,255,255,.55)}
.hero-ov{position:absolute;left:0;right:0;bottom:0;padding:34px 14px 11px;background:linear-gradient(transparent,rgba(12,22,36,.78));color:#fff}
.hero-date{font-family:var(--font-label);font-weight:700;font-size:18px;letter-spacing:.02em}
.hero-time{font-size:13.5px;font-weight:600;opacity:.95;margin-top:1px}
.hero-wx{position:absolute;top:10px;inset-inline-end:10px;background:rgba(12,22,36,.55);color:#fff;border-radius:var(--radius);padding:6px 12px;font-size:13px;font-weight:700}   /* SHAPE 2026-09-22: was border-radius:999px (a filled badge; BRANDSPEC line 298 sets badges at 4px) */
/* 2026-09-18 W3.1-8: was border-radius:12px;font-family:'Josefin Sans'. Outline button in Burgundy on white 9.73, label face, 2px border. */
.seeall{display:flex;align-items:center;justify-content:center;width:100%;min-height:48px;box-sizing:border-box;border:2px solid var(--navy);background:var(--surface);color:var(--navy);border-radius:var(--radius);padding:11px;font-family:var(--font-label);font-weight:700;font-size:13px;letter-spacing:var(--track-label);text-transform:uppercase;cursor:pointer;margin:2px 0 18px}
.seeall:lang(ar),.seeall:lang(km),.seeall:lang(lo),.seeall:lang(bn){letter-spacing:0}
/* 2026-09-18 W3.1-10: was a #2C4A6E to #46688F gradient with #fff text. Flat Deep Navy, cream text 13.01. The gold tag keeps Deep Navy text 5.35. */
.featured{background:var(--bw-navy);color:var(--bw-cream);border:none;position:relative;overflow:hidden}
.featured .tag{display:inline-block;background:var(--gold);color:var(--bw-navy);font-size:12px;font-weight:700;font-family:var(--font-label);letter-spacing:var(--track-label);text-transform:uppercase;padding:4px 8px;border-radius:var(--radius);margin-bottom:8px}
.featured .tag:lang(ar),.featured .tag:lang(km),.featured .tag:lang(lo),.featured .tag:lang(bn){letter-spacing:0}
.featured h3{font-size:19px;margin-bottom:4px}
.featured p{font-size:14px;opacity:.92}
.featured .readbtn{background:rgba(255,255,255,.18);border:1px solid rgba(255,255,255,.4);color:#fff;border-radius:var(--radius);width:44px;height:44px;min-width:44px;font-size:15px;cursor:pointer;flex:none;line-height:1}
.featured .whytag{font-size:12px;opacity:.95;margin-top:8px;font-style:italic}
.potm{display:block;width:100%;text-align:start;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:9px 12px;font-size:12.5px;color:var(--muted);cursor:pointer;font-family:inherit;margin:-2px 0 12px}   /* 2026-09-18 W3.1-25: was border-radius:10px */
.event-row{display:flex;gap:12px;padding:12px 0;border-bottom:1px solid var(--border);align-items:flex-start}
.event-row:last-child{border-bottom:none;padding-bottom:0}
.event-row:first-child{padding-top:0}
/* 2026-09-18 W3.1-9: was background:var(--sky-soft);border-radius:10px and 'Josefin Sans' on the day. The date is the Event
   template's eyebrow: no tinted box, label face, Burgundy on white 9.73 (gold on navy 5.35 in dark). */
.datebox{flex:none;width:48px;text-align:center;background:transparent;border-radius:0;padding:6px 0}
.datebox .m{font-size:12px;font-weight:700;font-family:var(--font-label);letter-spacing:var(--track-label);text-transform:uppercase;color:var(--bw-burgundy)}
.datebox .m:lang(ar),.datebox .m:lang(km),.datebox .m:lang(lo),.datebox .m:lang(bn){letter-spacing:0}
.datebox .d{font-family:var(--font-label);font-weight:700;font-size:22px;line-height:1;color:var(--bw-burgundy)}
.phone.dark .datebox .m,.phone.dark .datebox .d{color:var(--navy)}
.ev-official{margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}
.ev-official a{display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:10px 2px;color:var(--sky);font-weight:700;font-size:15px;text-decoration:none}
.phone.dark .ev-official{border-top-color:rgba(255,255,255,.10)}
.event-row h4{font-size:16.5px;font-weight:700}
/* READER 2026-09-23, THE SAME TEST APPLIED TO THE CARD ITSELF. What he is looking at wraps to three
   lines: "Wed - 7:00 PM-8:30 PM - Hamlet Middle School cafeteria, 60 Florence Dr, Woonsocket RI".
   READ IT AS A RESIDENT. The order is already right: what day, what time, then where, which is the
   order a person asks those questions in, so the order is NOT changed. What is wrong is the last
   part. "Woonsocket RI" is printed on every card of an app whose only subject is Woonsocket, and
   the street number is not how anyone recognises a place they already know; the venue's NAME is.
   She needs "Hamlet Middle School cafeteria" to know where she is going, and Drive / Walk in the
   drawer to be taken there. Two of the three lines were spent on the one part she needs least.
   SO THE COLLAPSED FACE IS TWO LINES, AND NOTHING IS DELETED. Two, not one, and that was decided by
   LOOKING at the render rather than by counting: a one-line clamp cut "Hamlet ..." and took the
   VENUE NAME with it, which is the one part of that string she actually needs. Two lines keep the
   day, the time and the venue whole and drop only the street number and the "Woonsocket RI" that
   every card in this app carries. The full line prints in FULL, wrapping, the moment the card is
   opened, which is now one tap on the title. No record is edited, no string is cut, no comma is
   parsed (splitting an address on a comma across sixteen languages is how you lose a venue name),
   and `evLocIsAddress()` and the Drive/Walk links still read the untouched `e.loc`. Purely
   presentational. It is the same `-webkit-line-clamp` idiom `.ev-preview` eight lines below has
   used since 2026-07-03, so it is not a new technique in this file.
   A JUDGEMENT CALL, AND HIS TO OVERRULE: delete the clamp declarations and the `.open` rule under
   it, and the line wraps to its full three again exactly as it did this morning. */
.event-row .meta{font-size:14px;color:var(--muted);display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.event-row.open .meta{display:block;overflow:visible}
/* ==============================================================================================
   BW-09, 2026-09-24. THE CLAMP HAS TO GROW WITH THE TEXT STEP, OR THE CONTROL WORKS BACKWARDS.
   ----------------------------------------------------------------------------------------------
   Text scaling in this app is CSS `zoom` on `.phone`. Zoom shrinks the LAYOUT viewport: 320px of
   glass is 213 CSS px at Large and 160 at Extra Large. The words get bigger, the box they wrap
   inside gets narrower, so the same sentence needs MORE lines. The clamp above was a fixed 2 at
   every step, so the number of lines shown stayed at 2 while the number of lines needed went up.
   MEASURED 2026-09-24 before this rule, 320px English, div.meta: 74px of content in a 50px box at
   Normal, 174 to 198px of content in the same 50px box at Extra Large. A person who asked for
   bigger text was shown a SMALLER share of the venue address. That is the opposite of what the
   control is for, and it is the accessibility heart of this app.
   THE FIX IS THE SAME SHAPE AS THE DEFECT: the clamp scales with the step, so every step shows
   strictly more than the step below it. Extra Large drops the clamp entirely, because a person on
   the largest step has told us plainly that reading the words matters more to them than the page
   being short. Nothing is deleted, no string is cut, no font shrinks, and Normal is byte-for-byte
   the behaviour Ryan accepted on 2026-09-23 (two lines: day, time, venue).
   ============================================================================================== */
.phone.text-large .event-row .meta{-webkit-line-clamp:4;line-clamp:4}
.phone.text-xlarge .event-row .meta{-webkit-line-clamp:none;line-clamp:none;display:block;overflow:visible}
.event-row{cursor:pointer}
.event-row .more{display:none;font-size:15px;line-height:1.55;margin-top:8px;background:var(--canvas);border-radius:var(--radius);padding:12px 14px}   /* 2026-09-18 W3.1-25: was border-radius:10px */
.event-row.open .more{display:block}
/* ==================================================================================================
   CONNECT 2026-09-29. THE DRAWER PARAGRAPH FINALLY HAS A BOX, SO IT CAN FINALLY TAKE A CLAMP.
   `[RYAN 2026-09-29]` the 200-word paragraph in the open drawer. BLOB measured why every earlier
   pass failed on it and the answer was not a selector and not specificity: THE DESCRIPTION WAS A
   BARE TEXT NODE. .more's element children held 1,319 characters while .more.innerText held 2,591,
   and the 1,272-character difference was the paragraph, hanging off .more with no element around
   it. A text node has no box. No clamp, no max-height, no fade and no control can ever apply to
   one. app.js now wraps it in .ev-more-descwrap > p.ev-more-desc and these rules then work the
   first time, which is the whole reason this was one line of CSS after six failed attempts.
   THE IDIOM IS THE ONE THIS FILE ALREADY USES, deliberately: the same -webkit-box / line-clamp
   pair .ev-preview uses fourteen lines up, including the standard `line-clamp` beside the prefixed
   one, and the same two text-size escalations. NOTHING IS TRUNCATED IN STORAGE: a CSS clamp is not
   a cut, the full text is in the DOM, the search index at the events filter reads e.desc and is
   untouched, and a screen reader still reads the whole paragraph.
   AT text-xlarge THE CLAMP IS OFF AND THE TOGGLE IS HIDDEN, matching what .ev-preview and
   .event-row .meta already do at that size: a reader who has asked for the largest type is not
   asking to see less text, and the two-line box would hold about nine words.
   NO CASCADE OVERRIDE KEYWORD IS USED HERE, AND THE WORD IS NOT WRITTEN IN THIS COMMENT EITHER,
   because writing it once put this file's count up by one and made the next agent's grep lie.
   THE COUNT, MEASURED TWO WAYS, BECAUSE MY FIRST MEASUREMENT WAS WRONG: 102 LINES carry it, but
   127 OCCURRENCES do, several lines carrying more than one. RENDER's 127 is the right number and
   my first 102 was a grep -c, which counts matching lines, not matches. An earlier handover's 45
   is wrong on both counts. This block adds 0, and every rule below wins on plain specificity
   because nothing else in this file selects .ev-more-desc at all - it did not exist until today.
   UNDO: delete this block. The paragraph goes back to being unclamped inside its new wrapper, which
   is still better than the text node it was this morning.
   ================================================================================================== */
.event-row .more .ev-more-descwrap{margin:0 0 8px}
.event-row .more .ev-more-desc{margin:0;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.event-row .more .ev-more-descwrap.open .ev-more-desc{-webkit-line-clamp:none;line-clamp:none;display:block;overflow:visible}
.event-row .more .ev-more-toggle{display:inline-block;min-height:44px;margin-top:6px;padding:10px 14px;border:2px solid var(--navy);border-radius:var(--radius);background:transparent;color:var(--navy);font:inherit;font-weight:700;font-size:15px;cursor:pointer}
.event-row .more .ev-more-toggle:active,.event-row .more .ev-more-toggle:hover{background:var(--sky-soft)}
.phone.dark .event-row .more .ev-more-toggle{color:var(--navy);border-color:var(--navy)}
.phone.text-large .event-row .more .ev-more-desc{-webkit-line-clamp:4;line-clamp:4}
.phone.text-xlarge .event-row .more .ev-more-desc{-webkit-line-clamp:none;line-clamp:none;display:block;overflow:visible}
.phone.text-xlarge .event-row .more .ev-more-toggle{display:none}
/* ITEM 5b (2026-07-03): collapsed 2-line clamped description preview so cards hold a consistent height; hidden on expand where the full .more shows. No text is truncated in storage - CSS clamps the preview only. */
/* 2026-09-02: `overflow-wrap:anywhere` added. The 3-line clamp handles vertical overflow, but there
   was nothing handling a single token longer than the box: a long word cut off mid-letter at the
   right edge with no ellipsis, because `overflow:hidden` clips horizontally too. Measured RED by
   FALSIFY_FONTS_2026-09-02.mjs check B at 360x640 and 390x844, 100% text, in fr, pt, lo, pl and
   worst in el and ru where four previews were cut at once - Greek and Russian words are long and
   the untranslated proper nouns ("Woonsocket", "City Council") sit inside them unbreakable.
   English never showed it, which is why five sweeps missed it. Nothing else about the clamp moves. */
/* ==================================================================================================
   SPACE2 2026-09-28. THE EVENT BLURB GOES TO ONE LINE, BECAUSE THE BRAND GUIDE SAYS ONE SENTENCE.
   `[RYAN 2026-09-28]` "the flowery text taking up screenspace, is it balanced with functionality and
   aesthetics"
   THE CLAMP BELOW WAS `3`. IT IS NOW `1`. Nothing else on this rule moves and no string is touched.

   WHY THIS ONE AND NOT THE SERIF BAND SENTENCES. The band sentences are the guide's own device and
   the VOICE 2026-09-20 block further down this file names each one as copied verbatim from
   BRANDSPEC_2026-09-17.md with its line number. This block is the opposite case: the guide ASKS for
   less than the app was printing. events_variants_2026-08-23.css:429 quotes BRANDSPEC's own Event
   template in so many words - "one italic serif sentence of why it matters, 17px, only if the data
   has one" - and then sets this preview to 18px Cormorant Garamond ITALIC 500. BRAND_KIT_2026-09-23.md
   records the Official Beloved Woonsocket Brand Guide 2.0 verbatim: Cormorant is "Italic by default
   for emphasis and ornament. NEVER FOR UI OR FINE PRINT", one italic line, not a paragraph.
   A THREE-LINE ITALIC SERIF PARAGRAPH REPEATED ON EVERY ROW OF A FIFTEEN-ROW LIST IS FINE PRINT IN A
   DISPLAY FACE. One line is the guide's own number. This edit moves the app toward the guide.

   MEASURED, REAL HEADLESS CHROMIUM 154 OVER REAL HTTP, 320px, NORMAL TEXT, NEVER A FETCH. Rendered
   ink, not computed style, because CSS `zoom` is invisible to getComputedStyle:
     one row   78.28px -> 26.09px   (18px x 1.45 line-height, exactly one line)
     Events    4942 -> 4185  = 757px recovered in en and ar, 783 ru, 783 km, 782 el
     Home      2718 -> 2509  = 209px recovered
   scrollHeight on the clamped element stays 235px in every cell, so THE TEXT IS STILL THERE and is
   clipped, not cut: the full description prints in full the moment the card is opened, one tap on
   the title, and the event's own page carries all of it. No record is edited. No string is parsed.

   A LYING INSTRUMENT, AND THIS PROJECT HAS ALREADY BEEN BITTEN BY IT. The WORDS 2026-09-23 block
   above records that `display:-webkit-box` with `-webkit-line-clamp:1` computed as `flow-root` and
   painted TWO lines in the Chromium of that day, and calls a clamp the engine silently drops "a green
   that lies". SO I MEASURED IT HERE RATHER THAN TRUSTING EITHER SIDE. In Chromium 154 the computed
   `display` on this element IS `flow-root` and the clamp IS honoured: clientHeight 78 -> 26 with
   scrollHeight held at 235, in en, ru, el, km and ar. The standard `line-clamp` property, which this
   rule has carried beside the `-webkit-` one since 2026-07-03, is what does the work now. WORDS' note
   stands for the element it was written about and is not contradicted here; the difference is the
   engine, and it is said out loud so the next agent does not have to guess which of us to believe.

   HIS TO OVERRULE IN ONE CHARACTER. `1` to `2` gives back a line and half the pixels: two lines
   measures 52.19px a row, 378px back on Events. Both states are screenshotted in SPACE2_2026-09-28.md.
   Backup: NEVER_OVERWRITE_BACKUPS/space2_2026-09-28/app.css.PRE_SPACE2.
   ================================================================================================== */
.event-row .ev-preview{font-size:13.5px;line-height:1.4;color:var(--muted);margin-top:5px;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.event-row.open .ev-preview{display:none}
/* BW-09, 2026-09-24. Same defect, same fix, same reason as `.meta` above: a fixed 3-line clamp
   against a layout viewport that `zoom` makes narrower at every step. Measured before this rule,
   320px English Normal: 104 to 365px of content in a 78px box; at Extra Large the box is still 78.
   3 lines at Normal, 6 at Large, all of it at Extra Large. Never fewer, at any step. */
/* SPACE2 2026-09-28. THESE THREE RULES ARE DEAD ON THE SHIPPED BUILD AND THEY ARE KEPT, NOT DELETED.
   PROVED FROM THE LIVE COMPUTED VALUE, not from reading the files: at 320px with `wsTextSize` large
   or xlarge, `.phone` carries `evv2`, and events_variants_2026-08-23.css:338 sets
   `.phone.text-large.evv2 .ev-preview, .phone.text-xlarge.evv2 .ev-preview { display:none }`.
   Measured: 15 previews in the DOM, 15 visible at Normal totalling 1148.13px of ink, ZERO visible at
   Large and ZERO at Extra Large. Hiding `.ev-preview` at Extra Large recovers 0px, which is exactly
   why SPACESCOUT_2026-09-28 filed "Events at Extra Large" under COULD NOT TEST and read its 0 as rows
   that had not lazy-loaded. They had loaded. The element is switched off.
   SO THE CLAMP LADDER BELOW NEVER RUNS, and BW-09 2026-09-24's own principle - "never fewer, at any
   step", because a person who asks for bigger text must not be shown a smaller share - is broken at
   the two largest steps by a different file: Normal shows one sentence, Large and Extra Large show
   nothing at all. THAT IS NOT MINE TO SETTLE TONIGHT. events_variants_2026-08-23.css was not this
   job's file, and reversing another pass's deliberate variant rule from here by override is how this
   stylesheet got 164 patch files. HANDED OVER, NAMED, WITH THE NUMBER: events_variants_2026-08-23.css
   line 338, and the question for his eye is whether a large-text reader should get the one italic
   sentence a Normal-text reader now gets. The rules below are left byte-identical so that the moment
   line 338 is narrowed, the ladder they describe is still here and still correct. NOT DELETED. */
.phone.text-large .event-row .ev-preview{-webkit-line-clamp:6;line-clamp:6}
.phone.text-xlarge .event-row .ev-preview{-webkit-line-clamp:none;line-clamp:none;display:block;overflow:visible}
.phone.text-xlarge .event-row.open .ev-preview{display:none}
/* ITEM 5a (2026-07-03): the expand control is a real <button> (was a bare .caret-wrap span with a right-triangle that looked like a play button). Label + chevron stack; the chevron points down collapsed and rotates to up on .open. >=44px tap target, keyboard-focusable natively. */
.details-btn{flex:none;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;margin-top:2px;background:none;border:none;cursor:pointer;font-family:inherit;padding:4px 6px;min-width:44px;min-height:44px;color:var(--sky)}
.caret-wrap{flex:none;display:flex;flex-direction:column;align-items:center;gap:2px;margin-top:2px}
.details-lbl{font-size:12px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--sky)}
.event-row .caret{color:var(--sky);display:flex;align-items:center;justify-content:center;transition:transform .2s}
.event-row.open .caret{transform:rotate(180deg)}
.event-row:focus-visible{outline:2px solid var(--sky);outline-offset:2px;border-radius:var(--radius)}   /* SHAPE 2026-09-22: was border-radius:8px, a focus ring rounder than the 4px card it traces */
.details-btn:focus-visible{outline:3px solid var(--sky);outline-offset:2px;border-radius:var(--radius)}   /* SHAPE 2026-09-22: was border-radius:10px */
/* ARROWWRAP 2026-09-04. `[RYAN 2026-09-04, real phone]` "the headings of titles are now often on 2 lines
   because the arrow threw it off." He is right, and the arrow is this one: .details-btn ("DETAILS" over a
   chevron, 58px wide in English, 110px in Russian and Greek) plus the .wrr-flag beside it (28px on
   2026-08-28, 44px since tapsize_2026-09-01), both flex siblings of the title column. MEASURED, real
   Chromium with the shipped fonts (_HARNESS/_arrowwrap_2026-09-04/, 240 cells: 5 phones x 16 languages
   x 3 text sizes): the <h3> had 134px at 390 (Ryan's "2 lines" is the English best case), 104px at 360,
   52px at 360 in ru/el, and 2,550 of 2,640 measured event titles wrapped, up to 7 lines. Removing the
   two controls in a scratch copy put "Statewide Primary Election" on ONE line (264px); restoring them
   put it back on three. Both directions, prove_evrow.mjs.
   THE FIX. app.js eventRow() now renders the two controls INSIDE the text column, after the meta line,
   wrapped in .ev-ctl. This rule floats that group to the inline end, so: the title and the meta take the
   whole column; the chips share the controls' line when they fit and drop under them when they do not
   (Russian); nothing is ever narrower than its own words. The controls keep their exact boxes: the
   details button is still >=44x44 and the flag is still 44x44, hit-tested, not read off the CSS.
   float:inline-end mirrors for Arabic on its own (measured, 390/ar). z-index:2 keeps both controls
   above event_landing's .el-go::after row overlay, same as the old `.event-row > .wrr-flag` rule did.
   Row heights at 390/en went 327-432px to 234-323px; at 360/ru 486-636px to 293-348px.
   Undo: app.css.bak_2026-09-04_arrowwrap, app.js.bak_2026-09-04_arrowwrap. */
/* .event-row .ev-ctl RETIRED 2026-09-09 (CARDROW). See the comment block below the ARROWWRAP one
   above and app.js eventRow() for the full story. The float rule that used to live here is gone;
   its two children (.details-btn, the report flag) are now plain members of the single .ev-tags
   flex row below. Kept as a one-line marker, not a stacked banner, because the two comments above
   this point (ARROWWRAP, ORPHANFIX) explain WHY the row got here and that history stays useful;
   only the rule itself is superseded. */
/* ORPHANFIX 2026-09-06: `[RYAN 2026-09-06]` "several places the website button doesnt fit next to
   details... orphan under it on a seperate line." MEASURED (real Chromium against belovedwoon2.netlify.app,
   320-390px, en+es): the chip/Website/cost line used to be bare inline content flowing around the floated
   .ev-ctl above it, so the last pill that didn't fit dropped alone onto its own line with dead space either
   side of it - same disease as ARROWWRAP (2026-09-04) one component over: fixed-width controls sharing a row
   with no rule for what happens when they run out of width. THE PATTERN, not the instance: any row of 2+
   controls next to a flexible neighbour must be a real flex row with wrap+gap, never bare inline content
   drifting around a float.
   SUPERSEDED IN PART 2026-09-09 (CARDROW). Clearing .ev-tags below the float solved the website-pill
   orphan but created a NEW one: Details+flag now floated alone with nothing beside them, which is what
   `[RYAN 2026-09-09, real phone]` "the details pulldown is its own line and looks odd" is describing, a
   second, independent report of the same family `[RYAN 2026-09-06]` first raised. THE FIX, ONE RULE, NOT
   TWO: no float, no clear. .details-btn, the report flag, the category chip, Website and the cost chip
   are five siblings in ONE flex-wrap row (app.js eventRow() now emits all five inside .ev-tags, .ev-ctl
   is gone from the markup). align-items:center gives them a shared baseline/rhythm (brief item 2);
   flex-wrap+gap:8px means the whole set reflows together and no single control is ever stranded alone on
   its own line (brief item 1). position:relative;z-index:2 moves from the old .ev-ctl rule onto this row
   so every child - including .ev-site, which the float-era rule never covered - stays above
   event_landing's `.el-go::after` row overlay (app.css, event_landing_2026-08-24.css), a latent gap the
   old rule left and this one closes as a side effect, not a scope change.
   MEASURED (real Chromium, _HARNESS/_cardrow_2026-09-09/, 390x844 + 320x568, normal/large/xlarge text,
   en/es/pt/ht/fr/el): zero cells produced an orphaned single control after this change, vs the pre-fix
   baseline where Details sat alone on its own line in every cell measured. Full record and the
   before/after screenshots: C:\!Brain\LFG4\NEVER_OVERWRITE\CARDROW_2026-09-09.md.
   Undo: NEVER_OVERWRITE_BACKUPS/cardrow_2026-09-09/app.css.PRE_CARDROW_20260909T030009Z +
   app.js.PRE_CARDROW_20260909T030009Z (restores the ORPHANFIX-era float/clear split, still correct on
   its own terms, just not this fix). Falsifier: _HARNESS/FALSIFY_CARDROW_2026-09-09.mjs. */
/* ================================================================================================
   ONELINE 2026-09-23. `[RYAN 2026-09-23]` "Details with the down arrow can be dropped entirely when
   you can just click the title and make sure everythign else is on one line only you can play with
   sizes and make it all on one line always."

   WHAT HE IS LOOKING AT, MEASURED BEFORE A BYTE WAS CHANGED (real headless Chromium over real HTTP,
   19 rows x 3 widths x 2 text steps x en/ru/el/ar = 456 cells): the action row was ONE LINE IN 3
   CELLS OUT OF 456. Everywhere else it was 96px tall against a 44px tallest child, i.e. wrapped to
   two or three lines, with overflow of 60 to 203px. CARDROW 2026-09-09 stopped a single control
   being STRANDED alone, which it did, and it was measured on that question only. It never made the
   row fit, and `flex-wrap:wrap` guaranteed it would keep breaking. He has now reported this family
   three times (2026-09-06, 2026-09-09, 2026-09-23) and every previous fix moved the break rather
   than removing it.

   THE FIX IS THE ONE THING NONE OF THEM DID: THE ROW CANNOT WRAP. `flex-wrap:nowrap`. From here the
   only question is whether the content fits, and that is answered by giving the row a budget it can
   meet instead of hoping.
     1. THE DETAILS CONTROL IS GONE (app.js, TITLETAP). That is 58px in English, 110px in Russian and
        Greek, plus a gap, off the row, and it is the single biggest saving here.
     2. `min-width:0` on every child, so a pill is allowed to give up width instead of forcing an
        overflow. Without it a flex item never shrinks below its content and nowrap just clips.
     3. A PRIORITY, written as two flex-shrink factors, so that what gives way is what a resident
        needs least. The category chip is 100 and the cost chip is 1: the category absorbs
        essentially the whole shortfall before the price loses a single pixel. That is `[RYAN]`'s
        own standing rule kept under pressure, "no paid event presented as free; the price goes on
        the face of it" - the price is the last thing on this row to move. The report flag does not
        shrink at all, because it is a tap target.
     4. SIZES, which he explicitly allowed, and every one of them is horizontal. gap 8 to 5, chip
        padding 7px to 5px a side, chip tracking .05em to .02em. That is about 20 CSS px back on a
        320px card, measured, and it is the difference between a category chip that reads and one
        that shows a single letter and a full stop. NO FONT-SIZE GOES BELOW 12px anywhere in this
        block: the 12px floor gate has two known pre-existing hits and this adds no third. The chips
        stay at 12px, which is BRANDSPEC's own "condensed caps 12px" for this badge.
     5. THE 44px TAP TARGET IS KEPT IN THE SIZE A FINGER MEETS, NOT IN CSS px. Large and Extra Large
        text are `zoom:1.5` and `zoom:2` on `.phone`, so a 44px CSS box is already painted 66 and 88
        real px there: double the target, for free, out of a row that has no width to spare. Under
        those two steps only, the flag and the Website pill are pinned to 44/1.5 and 44/2 CSS px,
        which renders at EXACTLY 44 visual px, unchanged for the finger and 22px back for the row.
        Hit-tested at every step, never read off the CSS.
     6. `text-overflow:ellipsis` is the floor, not the plan. A pill that still cannot fit shows an
        ellipsis rather than pushing the row to two lines or off the screen. Which cells reach that
        floor is measured and written out in outputs\CARD_2026-09-23.md, not left to be discovered.
   Undo: NEVER_OVERWRITE_BACKUPS/card_2026-09-23/app.css.PRE_CARD.
   ================================================================================================ */
.event-row .ev-tags{display:flex;flex-wrap:nowrap;align-items:center;gap:5px;margin-top:4px;position:relative;z-index:2;min-width:0}
.event-row .ev-tags > *{min-width:0}
/* ==============================================================================================
   BW-08, 2026-09-24. A CHIP THAT CANNOT SHOW ITS OWN WORD IS NOT A LABEL.
   ----------------------------------------------------------------------------------------------
   Point 6 of the block above calls `text-overflow:ellipsis` "the floor, not the plan", and it was
   right to: an ellipsis beats a row that runs off the screen. But the floor turned out to be where
   Russian LIVES, not where it lands in the worst case. Measured 2026-09-24, ru at 390 Normal:
   span.chip.arts needs 105px and gets 78, span.chip.youth needs 107 and gets 94, and seven chips
   were ellipsised on the first screen alone. "Искусство" rendered as "Искусс..." is a category
   label that does not name its category.
   `flex-wrap:nowrap` is what forces the choice between the ellipsis and the overflow. Wrapping
   removes the choice: when the row cannot hold everything, the chip drops to its own line at full
   width and reads whole. Nothing shrinks. 12px stays 12px (gate L3). The ellipsis stays in place
   underneath as the floor it was always meant to be, for the case where even a full line is short.
   `align-items:center` becomes `flex-start` so a wrapped second line sits under the first rather
   than dragging the flag and the Details control down with it.
   ============================================================================================== */
.event-row .ev-tags{flex-wrap:wrap;align-items:flex-start;row-gap:4px}
/* `min-width:max-content` is the half of this that actually does the work. `.ev-tags > *` above
   pins every child to min-width:0, which is what lets a chip be squeezed under its own word even
   when the row has room to wrap. Pinning the chip to its own content instead means the flex
   algorithm wraps it onto the next line rather than shrinking it, which is the behaviour the
   category label needs and the row can afford. A single category word is never wider than the
   card, so this cannot push the row off screen: measured at 320px in all sixteen languages.
   CORRECTED 2026-09-24, SAME DAY, BY MEASUREMENT. This rule first read `min-width:max-content` flat
   and that was wrong: at Extra Large the layout viewport is 160 CSS px wide, and a Russian category
   word at max-content is wider than that, so the chip stopped being clipped and started pushing the
   whole DOCUMENT sideways instead. Measured: html scrollWidth went from 0px over to 67px over at
   320px Russian Extra Large on both Home and Events. A sideways-scrolling page is a worse defect
   than the one being fixed, so `min()` caps the floor at the container: the chip asks for its own
   word, and never for more than the line it is on. Re-measured after: 0px over, everywhere. */
.event-row .ev-tags .chip{align-self:center;min-width:min(max-content,100%)}
.event-row .ev-tags .details-btn{margin-top:0}
.event-row .ev-tags .wrr-flag{align-self:center;margin-top:0;flex:0 0 auto}
.event-row .ev-tags .chip{margin-top:0;flex:0 100 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:3px 5px;letter-spacing:.02em}
/* THE WEBSITE PILL, AND THE LAYER THAT PUTS IT BACK. app.js eventRow() stopped emitting `.ev-site`
   on this row (ONELINE 2026-09-23: it was `safeHref(e.link)`, the SAME destination as the drawer's
   own "Official event page", printed twice under two names). Removing it from app.js was not
   enough, and that was MEASURED, not assumed: the one-line sweep came back with `ev-site` still
   truncating in 16 of 19 rows afterwards. `event_deeplink_row_2026-08-15.js` re-appends the anchor
   after paint, guarded only by `!row.querySelector(".ev-site")`, and it injects its own
   `#ev-site-style` sheet with it. That file is a layer of the exact kind `[RYAN 2026-09-02
   CARD_DECLAN]` said to delete at the source, and it is NOT this job's file, so it is named here
   rather than edited or quietly worked around. This rule is in app.css because app.css is the file
   that owns `.event-row .ev-tags` geometry, and it is scoped to that row alone: `.ev-site` on Help,
   on the resource cards and on the event landing page is untouched and still draws exactly as it
   did. THE REAL FIX, for whoever owns that file: delete the re-append. Then this line can go too.
   3 classes of specificity, so it wins over the injected `#ev-site-style` with no !important. */
.event-row .ev-tags .ev-site{display:none}
.event-row .ev-tags .chip.ev-cost{margin-left:0;flex:0 1 auto}
/* AT EXTRA LARGE TEXT THE CATEGORY YIELDS WHOLE, RATHER THAN SHOWING ONE LETTER AND A FULL STOP.
   `zoom:2` leaves this row 104 CSS px at 320 and 139 at 390. The flag and the price alone want 27
   to 112 of it. The shrink priority above already makes the category give way first, but at this
   step it gives way so far that it renders as a gold sliver reading "C..." or a single Arabic
   letter, which is not information, it is noise wearing a badge. MEASURED at 320, 375 and 390 in
   all sixteen languages before this rule was written: the category was cut in 274 of 304 cells at
   320 and 224 of 304 at 390. So at this one text step it is not drawn, and what a resident is left
   with is the price, whole, and the control to say something is wrong. The category is still on
   every card at Normal and Large, still on the Events filter row above the list, and still in the
   record. The cost chip is never hidden at any step, per `[RYAN]`'s standing rule that the price
   goes on the face of it. One rule, one step, reversible by deleting it. */
.phone.text-xlarge .event-row .ev-tags .chip:not(.ev-cost){display:none}
/* the tap target in the size a finger meets. 44 / 1.5 = 29.34, 44 / 2 = 22: both paint 44 visual px
   under .phone's own zoom. Nothing here is smaller to a finger than it was this morning. */
/* THE TWO !important HERE ARE DELIBERATE AND THEY ARE OVERRIDING ANOTHER !important, WHICH IS THE
   ONLY REASON THEY EXIST. `tapsize_2026-09-01.css` pins `.wrr-flag{width:44px !important;
   height:44px !important}`, so a plain rule cannot move it. That file grew the flag from 28px to
   44px in 2026-09-01 for a real reason: "a small target a person aims at with their eyes, not just
   their finger", and that reason is fully honoured here. 44 CSS px under `zoom:1.5` and `zoom:2` is
   painted 66 and 88 real px, which is one and a half to two times the target it was grown to meet,
   on the one row in the app that has no width left. 44/1.5 = 29.34 and 44/2 = 22 paint EXACTLY 44
   real px at those two steps and not one pixel less. Hit-tested at every step in the sweep, which
   reports zero sub-44 controls; the number is never read off the CSS. At Normal text nothing here
   applies and the flag is untouched. `.wrr-flag` everywhere else in the app is untouched: these
   selectors are three classes deep inside `.event-row .ev-tags`. */
.phone.text-large .event-row .ev-tags .wrr-flag{width:29.34px !important;height:29.34px !important;min-width:29.34px;min-height:29.34px}
.phone.text-xlarge .event-row .ev-tags .wrr-flag{width:22px !important;height:22px !important;min-width:22px;min-height:22px}

/* THE ONE CELL THAT CANNOT FIT, AND WHAT IT DOES INSTEAD. ARITHMETIC, NOT OPINION: at 320px with
   Extra Large text the row's column is 104 CSS px (320, less 32 of view padding, less the 48px
   datebox and its 12px gap, all divided by `zoom:2`). The category chip needs 45 and the cost chip
   48; with the flag at its smallest honest size, 22, and one 6px gap, the three need 121. There is
   no font size, padding or tracking that closes 17px without going under the 12px floor, and he was
   explicit that a language which genuinely cannot fit is to be SAID, not silently wrapped.
   SO ONE THING LEAVES THE FACE, AND IT IS THE ONE A RESIDENT DOES NOT READ: the report flag. What
   stays is what she came for, the category and what it costs, both whole, on one line. This is the
   narrowest phone at the largest text, one cell of the ninety-six measured, and the flag is
   untouched at every other width and step and on every other card in the app. It is written up in
   outputs\CARD_2026-09-23.md rather than left to be found. */
/* SUPERSEDED THE SAME NIGHT, AND KEPT VERBATIM RATHER THAN DELETED, because it was the right
   reading of a wrong premise. It hid the report flag at 320 Extra Large:
     @media (max-width:360px){ .phone.text-xlarge .event-row .ev-tags .wrr-flag{display:none} }
   The premise was that both chips had to survive whole or one control had to leave the row. They do
   not. A PRIORITY, expressed in the two flex-shrink factors above, does the job without taking any
   control off any card at any size: the category yields first and the price is the last thing to
   move. The flag is now drawn at every width and every text step, which is where a control that
   lets a resident say "this is wrong" belongs. */


/* TITLETAP 2026-09-23, the CSS half. The <h3> title is now the only expand control on a Home event
   card, and it is a real <button>. A button carries a UA font, background, border, padding and
   centre alignment, none of which the title had, so every one of them is handed back to the h3 the
   button sits inside: `font:inherit` picks up the serif 22px from events_variants_2026-08-23.css
   (and `.el-go` beside it) with no size named twice and nothing to keep in sync. The title looks
   and wraps EXACTLY as it did; it is only now focusable, Enter/Space operable and announced with
   its own aria-expanded. display:block + width:100% keeps the whole title line tappable rather than
   just the glyphs, which is the finger target a 22px serif line already was.
   NO CHEVRON, HIS CALL. See app.js eventRow() for why one was not attached to the title. */
.event-row .ev-main h3 > .ev-title-btn{display:block;width:100%;margin:0;padding:0;border:0;background:none;font:inherit;color:inherit;letter-spacing:inherit;text-align:start;cursor:pointer;-webkit-appearance:none;appearance:none}
.event-row .ev-main h3 > .ev-title-btn:focus-visible{outline:3px solid var(--sky);outline-offset:2px;border-radius:var(--radius)}
.dirs{display:flex;gap:8px;margin-top:9px}
.dirs a{flex:1;text-align:center;background:var(--surface);border:1.5px solid var(--navy);color:var(--navy);border-radius:var(--radius);padding:8px 4px;font-weight:700;font-size:12px;text-decoration:none;font-family:var(--font-label);text-transform:uppercase;letter-spacing:.04em;display:inline-flex;align-items:center;justify-content:center;min-height:44px}   /* 2026-09-18 W3.1-25: was border-radius:9px */
.chip{display:inline-block;font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:3px 7px;border-radius:var(--radius);margin-top:4px}   /* SHAPE 2026-09-22: was border-radius:999px. This is the event card badge ("55+", "FREE", the category). BRANDSPEC line 298 names it: "Badges: 55+ and FREE become one row of gold badges: #C49A2A fill, #102F3D text, condensed caps 12px, 4 px radius, 28 px tall". Explicit, so it flattens; the LABEL pills the guide keeps fully rounded are listed in the SHAPE block near the end of this file */
/* 2026-09-18 W3.1-5: every chip is a Community Gold badge with Deep Navy text, 5.35 (BRANDSPEC: gold "accents highlights,
   badges, and key moments"; no pastel rainbow). Was: youth green-soft/green, family gold-soft/gold-ink, music red-soft/red,
   city sky-soft/sky, seniors #EFE9F7/#6B4FA1. */
.chip.youth,.chip.family,.chip.music,.chip.city,.chip.seniors,.chip.arts{background:var(--gold);color:var(--bw-navy)}
/* 2026-09-24 DATAFIX: `.chip.arts` ADDED TO THIS LIST AND TO THE ONE AT LINE 2043. It was the one
   category chip the brand rebuild missed, so the Arts chip has been wearing the pre-brand lavender
   (#EFE9F7 on #6B4FA1, from events_variants_2026-08-23.css) while Youth, Family, Music, City and
   Seniors are all Community Gold. Measured on the Russian Events screen, where it reads
   "Искусство". 54 Stadium Theatre rows carry this chip, which is the largest single category in
   the file, so it was also the most visible thing still on the old palette. Gold on Deep Navy is
   5.35, the same pair every other chip already passes with; no new colour value is introduced and
   no contrast is lowered. The lavender rules in events_variants_2026-08-23.css are NOT deleted:
   they are scoped to .phone.evv2 / .phone.evv3, which are variant bodies, and they still win there
   if anyone turns a variant on. */

.quick-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}
.quick{background:var(--surface);border:0;border-radius:var(--radius);padding:14px 8px;   /* 2026-09-18 W3.1-24: was border 1px, radius 14px, padding 12px 8px */text-align:center;cursor:pointer;font-family:inherit;color:var(--ink);width:100%}
.quick .qi{font-size:22px}
.quick .ql{font-size:13px;font-weight:600;margin-top:6px;line-height:1.25}
/* PHONEFIT 2026-09-24. THE QUICK LINK LABELS WERE BREAKING IN THE MIDDLE OF WORDS.
   Handed over by the agent before this one and re-measured here, real Chromium at 390 wide: the
   label box is 96.7px, `overflow-wrap:break-word` was in force and `hyphens` was `manual`, which
   together mean "when a word does not fit, chop it at whatever character lands on the edge". In
   Russian, Greek, Khmer and Lao that is most of the list: "Записаться", "Голосование",
   "Βιβλιοθήκη", "Ψηφοφορία" and the Khmer "report a concern" among them.
   IT IS ONE BUG WITH TWO CORRECT ANSWERS, WHICH IS WHY THE FIX IS LANGUAGE-AWARE AND NOT ONE LINE.
     Languages that hyphenate (everything Latin, Cyrillic and Greek here): `hyphens:auto` lets the
     engine break at a real syllable boundary and draw the hyphen that tells the reader the word
     continues. "Запи-саться", not "Записат/ься". `break-word` stays behind it as the last resort
     for a token even hyphenation cannot place.
     Languages with no spaces and no hyphens (Khmer, Lao, Thai, Burmese, Chinese, Japanese): a
     hyphen would be wrong and `break-word` is actively harmful, because it overrides the
     dictionary line-breaking ICU already does for these scripts and can split a base character
     off its own combining mark - a broken glyph, not just a bad break. They get
     `overflow-wrap:normal` so ICU's own syllable boundaries govern, and `hyphens:none` so no
     hyphen is ever drawn.
   No font-size is touched, so the 12px gate floor is nowhere near this (13px here, 16px at the
   large text step, app.css:2316). No box is widened, so the three-across grid is unchanged.
   Counts before and after, per language, are in outputs/PHONEFIT_2026-09-24.md.
   Undo: delete these two rules. */
.quick .ql{hyphens:auto;-webkit-hyphens:auto;overflow-wrap:break-word}
html[lang="km"] .quick .ql,
html[lang="lo"] .quick .ql,
html[lang="th"] .quick .ql,
html[lang="my"] .quick .ql,
html[lang|="zh"] .quick .ql,
html[lang="ja"] .quick .ql{hyphens:none;-webkit-hyphens:none;overflow-wrap:normal;word-break:normal;line-break:normal}

/* great things bright spot */
/* ITEM 6b (2026-07-03): the spotlight holds a CONSISTENT height whatever the entry length. .great gets a min-height so a short entry does not shrink the card, and .gt-txt reserves its 3-line clamp box (min-height = 3 x line-height) so a 1-line and a 4-line entry occupy the same space. The full text stays reachable - the card is still tappable. Fred: consistency, no jumping. */
/* 2026-09-18 W3.1-11: was a var(--green) to #3f8a5c gradient, radius 16px. Flat Deep Navy, cream text 13.01. The 211 door keeps its green; this card is not the 211 door. */
.great{background:var(--bw-navy);color:var(--bw-cream);border-radius:var(--radius);padding:13px 15px;margin-bottom:12px;display:flex;align-items:center;gap:11px;min-height:104px}
.great .gt-ic{font-size:24px;flex:none}
.great .gt-lbl{font-family:var(--font-label);font-weight:700;font-size:12px;letter-spacing:.08em;text-transform:uppercase;opacity:.92}
.great .gt-txt{font-size:14px;font-weight:600;line-height:1.35;margin-top:2px;display:-webkit-box;-webkit-line-clamp:3;line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;min-height:calc(1.35em * 3)}
/* the event-kind spotlight clamps the title to 2 lines and adds a subline, so it reserves 2 lines to sit level with the 3-line text-only cards */
.great .gt-txt.gt-txt-2{-webkit-line-clamp:3;line-clamp:3;min-height:calc(1.35em * 2)}
.great .gt-sub{font-size:12.5px;opacity:.92;margin-top:3px}
.great .gt-read{background:rgba(255,255,255,.2);border:1px solid rgba(255,255,255,.45);color:#fff;border-radius:var(--radius);width:44px;height:44px;min-width:44px;font-size:15px;cursor:pointer;flex:none;line-height:1}
/* CUTCOPY 2026-09-09: !important added to both the hide and the show side of this pair only.
   Real-Chromium render found .res-read (a <button> inside .res-actions) was ALWAYS visible
   regardless of .read-on, because .res-actions a,.res-actions button (below, and again more
   specifically in org_card_2026-08-23.css's .res-item.oc-card .res-actions button) sets
   display:inline-flex at equal-or-higher specificity and later in the cascade, defeating the
   off-by-default rule on every org-card-style resource - the actual mechanism behind [RYAN
   2026-09-09] "Read aloud buttons are on but i dont see them" (inconsistent, card-shape-
   dependent visibility, not a clean on/off). This makes OFF unbeatable; ON (.phone.read-on)
   still works identically, both sides symmetric so a future session flips ONE class again to
   restore, same as before. See NEVER_OVERWRITE\CUTCOPY_2026-09-09.md. */
.gt-read,.readbtn{display:none!important}
.phone.read-on .gt-read,.phone.read-on .readbtn{display:inline-flex!important;align-items:center;justify-content:center}
.phone .res-read{display:none!important}
.phone.read-on .res-read{display:inline-flex!important}

/* get help CTA + screen */
.help-cta{display:flex;align-items:center;gap:12px;width:100%;text-align:start;background:var(--gold-soft);border:1.5px solid var(--gold);border-radius:var(--radius);padding:14px 15px;margin-bottom:12px;cursor:pointer;font-family:inherit;color:var(--ink)}   /* 2026-09-18 W3.1-25: was border-radius:16px */
.help-ic{font-size:26px;flex:none}
.help-cta-tx{flex:1;display:flex;flex-direction:column}
.help-cta-t{font-family:var(--font-label);font-weight:700;font-size:15px;color:var(--bw-burgundy)}   /* 2026-09-18 W3.1: was #8a5e12 (old gold ink); Burgundy 7.89 on cream-deep, 9.02 on cream */
.help-cta-s{font-size:13px;color:var(--muted);margin-top:2px;line-height:1.35}
.help-arrow{color:var(--gold-ink);font-size:18px;flex:none}
.emerg{background:var(--red-soft);border:1.5px solid var(--red);border-radius:14px;padding:14px;margin-bottom:14px}
.emerg h3{color:var(--red);font-size:15px;margin-bottom:6px}
.emerg p{font-size:14px;line-height:1.5;color:var(--ink);margin-bottom:4px}
.emerg .tel{font-weight:700;color:var(--red)}
/* find help now (quiet, read-only resource finder) */
.btn.small{padding:9px 14px;font-size:12px;min-height:44px;display:inline-flex;align-items:center;justify-content:center}
.findnow{display:flex;align-items:center;gap:11px;width:100%;text-align:start;background:var(--green);color:#fff;border:none;border-radius:var(--radius);padding:14px;margin-bottom:10px;cursor:pointer;font-family:inherit}   /* SHAPE 2026-09-22: was border-radius:14px */
.findnow .fi{font-size:24px;flex:none}
.findnow .t{font-family:var(--font-label);font-weight:700;font-size:15px;display:block}
.findnow .s{font-size:13px;opacity:.93;margin-top:2px;display:block}
.privacy{background:var(--green-soft);color:var(--green);border-radius:var(--radius);padding:10px 12px;font-size:13px;font-weight:600;margin-bottom:12px;display:flex;gap:8px;align-items:flex-start;line-height:1.4}
.fh-cats{display:flex;flex-wrap:wrap;gap:8px;padding-bottom:4px;margin-bottom:12px}
.fh-cat{flex:none;border:1px solid var(--border);background:var(--surface);border-radius:999px;padding:8px 14px;min-height:44px;display:inline-flex;align-items:center;white-space:nowrap;font-size:13px;font-weight:700;cursor:pointer;font-family:inherit;color:var(--ink)}
.fh-cat.active{background:var(--green);color:#fff;border-color:var(--green)}
/* Info / Find Help resource page (dignity-first front door, real verified resources) */
.res-intro{font-size:15px;line-height:1.55;color:var(--ink);margin-bottom:14px}
.res-crisis{background:var(--red-soft);border:1.5px solid var(--red);border-radius:14px;padding:14px;margin-bottom:14px}
.res-crisis h3{color:var(--red);font-size:15px;margin-bottom:10px}
.res-crisis .row2{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.callbtn{display:flex;flex-direction:column;justify-content:center;text-decoration:none;background:#fff;border:1.5px solid var(--red);color:var(--red);border-radius:11px;padding:10px 12px;min-height:56px;font-weight:800;font-size:14.5px;font-family:inherit;min-width:0;overflow-wrap:break-word;word-break:break-word}
.callbtn .cs{font-size:12px;font-weight:600;opacity:.95;margin-top:2px}
/* On small phones (the devices most vulnerable users carry), the 2-up crisis buttons get cramped and long non-English text crowds the edges. Drop to one full-width column so 911/988 stay big and readable. Added 2026-06-22 after a render check found Spanish/Portuguese overflow. */
@media (max-width:380px){.res-crisis .row2{grid-template-columns:1fr}}
/* Crisis bar v2 (2026-06-26): stacked option cards, each with its own Call/Text/Chat tap pills. Butt-dial safe: only the pills are links, the card label is plain text. */
.res-crisis .copt{background:#fff;border:1.5px solid var(--red);border-radius:12px;padding:11px 12px;margin-bottom:9px}
.res-crisis .copt .clbl{font-weight:800;font-size:15px;color:var(--red)}
.res-crisis .copt .csub{font-size:12.5px;color:var(--muted);font-weight:600;margin:2px 0 9px}
.res-crisis .cacts{display:flex;gap:8px;flex-wrap:wrap}
.res-crisis .cpill{flex:1 1 96px;text-align:center;text-decoration:none;border-radius:9px;padding:9px 6px;font-weight:800;font-size:14px;min-height:46px;display:flex;align-items:center;justify-content:center;font-family:inherit;min-width:0;overflow-wrap:break-word}
.res-crisis .cpill.call{background:var(--red);color:#fff;border:1.5px solid var(--red)}
.res-crisis .cpill.alt,.res-crisis .cpill.alt2{background:#fff;color:var(--red);border:1.5px solid var(--red)}
.res-crisis .cbnote{font-size:12px;color:var(--muted);font-weight:600;margin:-2px 0 11px}
/* 047: crisis-card ACCORDION (Ryan's design + CRISIS_CARD_UX_LITREVIEW_047). Three primary actions always visible,
   the two confusing specialized lines behind a descriptive closed-by-default pulldown. Big targets, high contrast. */
.res-crisis-acc .cprim{display:flex;flex-direction:column;gap:10px}
.res-crisis-acc .cbtn{display:flex;flex-direction:column;justify-content:center;text-decoration:none;border-radius:12px;padding:12px 14px;min-height:64px;font-family:inherit;border:1.5px solid var(--red);overflow-wrap:break-word;word-break:break-word}
.res-crisis-acc .cbtn.call{background:var(--red);color:#fff}
.res-crisis-acc .cbtn.alt{background:#fff;color:var(--red)}
.res-crisis-acc .cbtn .cbtn-lb{font-size:19px;font-weight:800;letter-spacing:.01em}
.res-crisis-acc .cbtn .cbtn-cs{font-size:14px;font-weight:600;opacity:.95;margin-top:3px}
.res-crisis-acc .cmore{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;margin-top:12px;background:#fff;color:var(--red);border:1.5px dashed var(--red);border-radius:12px;padding:12px 14px;min-height:56px;font-family:inherit;cursor:pointer;text-align:left}
.res-crisis-acc .cmore .cmore-main{display:flex;flex-direction:column;min-width:0}
.res-crisis-acc .cmore .cmore-lb{font-size:16px;font-weight:800}
.res-crisis-acc .cmore .cmore-sub{font-size:13px;font-weight:600;opacity:.9;margin-top:2px}
.res-crisis-acc .cmore .cmore-chev{font-size:15px;flex:0 0 auto;transition:transform .15s}
.res-crisis-acc .cmore-body{margin-top:10px}
.res-crisis-acc .cmore-body[hidden]{display:none}
.res-crisis-acc .copt .cbrand{font-size:12px;color:var(--muted);font-weight:700;margin-top:7px}
.phone.dark .res-crisis-acc .cbtn.call{background:#A34A3A;border-color:#A34A3A}
.phone.dark .res-crisis-acc .cbtn.alt,.phone.dark .res-crisis-acc .cmore{color:#A34A3A;border-color:#A34A3A}
/* Dark mode: the cards stay white, so pin text + the filled pill to the deep red (not the lightened --red), same dual-token fix as .callbtn. Keeps white-on-red and red-on-white both high-contrast. */
.phone.dark .res-crisis .copt{border-color:#A34A3A}
.phone.dark .res-crisis .copt .clbl{color:#A34A3A}
.phone.dark .res-crisis .cpill.call{background:#A34A3A;color:#fff;border-color:#A34A3A}
.phone.dark .res-crisis .cpill.alt,.phone.dark .res-crisis .cpill.alt2{color:#A34A3A;border-color:#A34A3A}
/* A11Y 2026-09-05, WCAG 1.4.3 Contrast (Minimum) AA. The rule directly above pins .clbl and both
   pills for dark mode "because the cards stay white" - and then stops. .csub (line 240) and .cbrand
   (line 261) also sit on that still-white card and were left on var(--muted), which flips at line 494
   from the light #595c61 to the dark #9aa0a6. #9aa0a6 is chosen against the dark canvas #16181b; on
   the white card it MEASURES 2.64:1 where AA needs 4.5:1.
   MEASURED, not reasoned: 8 elements at 2.64:1 in dark mode, 390x844, en - the names and descriptions
   of BH Link, Safe from abuse (RI domestic violence), RAINN and the National Human Trafficking
   Hotline. That is the whole crisis More panel: in dark mode a low-vision reader could not read WHO
   she was about to call, only the red Call pill. It survived the 2026-09-04 dark-mode contrast pass
   because #cmore-body ships with the `hidden` attribute (index.html:693) and no sweep had opened it.
   Pinned to the LIGHT muted #595c61, which measures 6.71:1 on that white card - the same value light
   mode already has, so the two themes now read identically here, which is the intent of the block.
   Proof: _A11Y_2026-09-05/probe7.mjs prints every text node in this panel with its measured ratio,
   in both themes. Undo: delete these two rules and the two in the @media block near line 588. */
.phone.dark .res-crisis .copt .csub,
.phone.dark .res-crisis-acc .copt .cbrand{color:#595c61}
.res-211{display:flex;gap:13px;align-items:flex-start;background:var(--green);color:#fff;border-radius:14px;padding:15px;margin-bottom:18px}
/* FIX 2026-08-30: the flex item wrapping the title/sub/Call-211 button had no min-width, so its
   automatic minimum size defaulted to its content's min-content - and bar_variants_2026-08-28.css's
   own white-space:nowrap on the title (its own comment: "measured: wrapping ADDED height") makes
   that min-content equal to the FULL unwrapped sentence, which forces this item, and the whole
   #res-211 row, wider than the viewport. Measured live: French title "Vous ne savez pas par où
   commencer ? Composez le 211." rendered at 381.7px unbroken, pushing <html>.scrollWidth to 455px
   at every width from 320 to 430 - the single largest sideways-scroll source measured this session
   (fr/bn worst). This is the exact fix r211_2026-08-29.css already wrote for itself, gated behind
   an `html[data-r211]` attribute nothing ever sets (r211_2026-08-29.js is not wired into
   index.html) - applied here, unconditionally, to the box every resident actually sees, so
   bar_variants' own nowrap+ellipsis rule can actually truncate instead of forcing the row wide.
   Backup: app.css.bak_BESTHELP_20260830_231714.
   NOTE: `:last-child` deliberately NOT used - bar_variants_2026-08-28.js appends a third child,
   <button class="bar-211-toggle">, at runtime for its bar=2/bar=3 variants, which would make the
   wrapper div no longer the last child. `:not(.big)` matches it regardless of what siblings get
   appended after it. */
.res-211 > div:not(.big){min-width:0}
.res-211 .big{font-size:32px;font-family:'Josefin Sans',sans-serif;font-weight:700;line-height:.95;flex:none}
.res-211 a{display:inline-flex;align-items:center;margin-top:9px;background:#fff;color:var(--green);font-weight:800;text-decoration:none;border-radius:10px;padding:9px 16px;font-size:14px;min-height:44px}
.res-cat{margin-bottom:18px}
.res-cat>h3{font-size:16px;color:var(--navy);margin-bottom:9px}
.res-sub{margin-bottom:4px}
.res-sub-h{font-size:13px;font-weight:800;color:var(--navy);opacity:.92;margin:13px 2px 8px;display:flex;align-items:center;gap:7px;line-height:1.25}
/* 2026-09-24 BW sweep: the category heading is a flex row and its text is a flex item, so it asked
   for its longest word and pushed `.res-sub` past its own box (121 in 116 at 320px Russian Extra
   Large, and the same shape in Greek). `min-width:0` plus `anywhere` is the pair that ends it. The
   icon beside it keeps `flex:0 0 auto` and its own size; only the words give. */
.res-sub{min-width:0}
.res-sub-h{min-width:0;overflow-wrap:anywhere}
.res-sub-h > svg{flex:0 0 auto}
.res-cat>.res-sub:first-of-type .res-sub-h{margin-top:3px}
/* SEARCHSHIP 2026-09-06: was position:sticky;top:0;z-index:6. Sticky at top:0 put this strip UNDER the 56px
   sticky masthead once she scrolled (measured 2026-09-05: Clear at y=8 behind .hdr, elementFromPoint returned
   the masthead, the button could not be tapped). `[RYAN 2026-09-06 CARD_SEARCHMENU item 5]` "0px, let the
   Showing/Clear strip scroll away." So it scrolls with the results. A new search is the masthead tap, then the
   box. Undo: NEVER_OVERWRITE_BACKUPS/app.css.PRE_SEARCHSHIP_20260906_132914 line 310. */
.res-resulthead{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;position:static;background:var(--canvas);padding:8px 0 9px;margin:0 0 8px}
.res-resulthead .rrh-left{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.res-resulthead .rrh-n{font-weight:800;font-size:14px;color:var(--navy)}
.res-resulthead .rrh-crisis{display:flex;gap:8px;flex:1 1 100%}
.res-resulthead .rrh-crisis a{flex:1 1 0;min-width:0;display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;text-decoration:none;border-radius:13px;padding:8px 4px;min-height:52px;box-sizing:border-box}
.res-resulthead .rrh-crisis .rrh-num{font-weight:800;font-size:18px;line-height:1}
.res-resulthead .rrh-crisis .rrh-cs{font-size:12px;font-weight:700;line-height:1.12;text-align:center;overflow-wrap:break-word;word-break:break-word;hyphens:auto}
.rrh-911{background:#A34A3A;color:#fff}
.rrh-988{background:#2C4A6E;color:#fff}
/* [RYAN 2026-08-12] "text 988 needs a coloir something looks bad". He was right and it was worse
   than cosmetic: .rrh-988t had NO rule at all, so the THIRD DOOR OF THE CRISIS BAR rendered with a
   transparent background and rgb(0,0,238), the raw browser-default link blue. Measured in a real
   browser, not guessed. On the crisis floor, of all places.
   The three doors now read as a family: 911 in the alert red-brown, 988 CALL in navy, 988 TEXT in
   the deep navy one step down. Same destination as the call, plainly a different action. White on
   #1F3550 is about 12:1, well past AA, which matters because this is the button for a person who
   will not speak. One line to change the colour if he wants a different one. */
.rrh-988t{background:#1F3550;color:#fff}
.rrh-211{background:#3B6D11;color:#fff}
/* FIXED 911/988/211 BAR at the bottom, above the nav (Ryan 2026-06-24) */
.res-helpbar{position:fixed;left:0;right:0;z-index:55;background:var(--surface);border-top:1px solid var(--border);box-shadow:0 -3px 12px rgba(0,0,0,.10);padding:7px 12px calc(7px + env(safe-area-inset-bottom));max-width:620px;margin:0 auto;display:none}
.res-helpbar .rrh-crisis{display:flex;gap:8px}
.res-helpbar .rrh-crisis a{flex:1 1 0;min-width:0;display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;text-decoration:none;border-radius:13px;padding:8px 4px;min-height:52px;box-sizing:border-box}
.res-helpbar .rrh-crisis .rrh-num{font-weight:800;font-size:18px;line-height:1}
.res-helpbar .rrh-crisis .rrh-cs{font-size:12px;font-weight:700;line-height:1.12;text-align:center;overflow-wrap:break-word;word-break:break-word;hyphens:auto}
.res-haspad{padding-bottom:86px}
/* Help page leaves room for the always-on fixed 911/988/211 bar so nothing hides behind it */
/* RETIRED 2026-08-15. This was hardcoding 1 of 7 of ONE quantity: how tall the bottom furniture is.
   Real furniture measured in Chromium at 360x640: 144.4px. This reserved 104. Forty pixels of the
   bottom of the Help page were written into space the crisis bar was standing on, and nothing here
   could ever know, because a number in a stylesheet cannot measure anything.
   ONE SOURCE NOW: bottom_dock_2026-08-15.css derives the reserve from --bw-dockh, which is the
   bottom dock's own getBoundingClientRect().height, republished by a ResizeObserver.
   Not deleted. Un-comment this line, or restore app.css.bak_2026-08-15_dock, to undo.
#view-community.has-helpbar{padding-bottom:104px} */
/* COLOR-CODED RESULT SECTIONS so they are easy to follow (Ryan 2026-06-24) */
/* 2026-09-18 W3.1-4: the pastel rainbow goes. Every category is a white card on cream with the guide's 3px Deep Navy
   rule and a Burgundy title (9.73 on white). Was: border-left:6px solid transparent;border-radius:12px, and one tint per
   category, kept below for the undo. .rescat-safe is left byte-identical: its rule is the crisis red #A34A3A, which is
   never touched here (REBUILD_PLAN Tier 0); whether a Safety door keeps a red rule is a card for Ryan, not this edit. */
.res-cat{border-left:var(--rule-w) solid var(--bw-navy);border-radius:var(--radius);padding:10px 13px 8px;margin-bottom:14px;background:var(--surface)}
.res-cat>h3{display:block;padding:0 0 3px;background:none;color:var(--bw-burgundy)}
.res-cat .res-item{background:var(--surface)}
.phone.dark .res-cat .res-item{background:var(--surface)}   /* 2026-09-18: was #1f2d3d */
/* superseded 2026-09-18, kept for the undo:
.rescat-start{background:#edf0f3;border-left-color:#5B6B7B}.rescat-start>h3{color:#3a4654}
.rescat-home{background:#ebeffa;border-left-color:#4B5BA6}.rescat-home>h3{color:#33408a}
.rescat-stay{background:#e8f0f8;border-left-color:#2C4A6E}.rescat-stay>h3{color:#213a58}
.rescat-food{background:#eef6e1;border-left-color:#3B6D11}.rescat-food>h3{color:#2c520d}
.rescat-health{background:#e4f3f1;border-left-color:#147a72}.rescat-health>h3{color:#0e5a55}
.rescat-money{background:#fbf1dc;border-left-color:#B5791F}.rescat-money>h3{color:#8a5b13}
.rescat-vote{background:#f3ece1;border-left-color:#7a5a2e}.rescat-vote>h3{color:#5d4522}
.rescat-seniors{background:#f0eaf8;border-left-color:#6B4E9E}.rescat-seniors>h3{color:#523a7c}
.rescat-youth{background:#fdf0e3;border-left-color:#C56A1A}.rescat-youth>h3{color:#9a4f12}
.rescat-veterans{background:#e9f0ea;border-left-color:#3a5a40}.rescat-veterans>h3{color:#2b4430}
*/
.rescat-start,.rescat-home,.rescat-stay,.rescat-food,.rescat-health,.rescat-money,.rescat-vote,.rescat-seniors,.rescat-youth,.rescat-veterans{background:var(--surface);border-left-color:var(--bw-navy)}
.rescat-start>h3,.rescat-home>h3,.rescat-stay>h3,.rescat-food>h3,.rescat-health>h3,.rescat-money>h3,.rescat-vote>h3,.rescat-seniors>h3,.rescat-youth>h3,.rescat-veterans>h3{color:var(--bw-burgundy)}
.rescat-safe{background:#f8eae6;border-left-color:#A34A3A}.rescat-safe>h3{color:#83392c}
/* ROOT FIX 2026-06-26: real dark bg + light heading instead of a brightness() filter (which axe cannot read and is not a reliable contrast method). The colored left border keeps category identity. */
.phone.dark .res-cat{background:var(--surface);border-left-color:var(--navy)}   /* 2026-09-18: was #1f2430; navy card, gold rule */
.phone.dark .res-cat>h3,.phone.dark .rescat-start>h3,.phone.dark .rescat-home>h3,.phone.dark .rescat-stay>h3,.phone.dark .rescat-food>h3,.phone.dark .rescat-health>h3,.phone.dark .rescat-money>h3,.phone.dark .rescat-vote>h3,.phone.dark .rescat-seniors>h3,.phone.dark .rescat-youth>h3,.phone.dark .rescat-veterans>h3{color:var(--navy)}   /* 2026-09-18: was #dfe8f2; gold on navy 5.35 */
/* 2026-09-18 W3.1-14: was background:#eef2f7;border:1px solid #d6deea;border-radius:20px and hover #e2e8f1. Burgundy on cream-deep 7.89. */
.res-clearbtn{background:var(--bw-cream-deep);border:1px solid var(--border);color:var(--navy);font-weight:700;font-size:14px;border-radius:var(--radius);padding:8px 16px;min-height:44px;cursor:pointer}
.res-clearbtn:active,.res-clearbtn:hover{background:var(--border)}
.phone.dark .res-clearbtn{background:#7A2636;border-color:#7A2636;color:#FAF6EC}   /* 2026-09-18: was #2C4A6E; 9.02 */
.res-item{border:1px solid var(--border);border-radius:var(--radius);padding:13px 14px;margin-bottom:9px;background:var(--surface)}   /* 2026-09-18 W3.1-24: was radius 13px */
.res-item .rn{font-weight:800;font-size:15.5px;color:var(--navy)}
.res-item .rw{font-size:14px;color:var(--ink);line-height:1.45;margin:3px 0 4px}
.res-item .ra{font-size:12.5px;color:var(--muted)}
.res-item .es{display:inline-block;font-size:12px;font-weight:800;color:var(--green);margin-inline-start:7px;vertical-align:middle}
.res-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.res-actions a,.res-actions button{text-decoration:none;border-radius:var(--radius);padding:9px 15px;font-weight:800;font-size:13.5px;min-height:44px;display:inline-flex;align-items:center;gap:6px;font-family:inherit;cursor:pointer;border:none}   /* 2026-09-18 W3.1-25: was border-radius:9px */
.res-call{background:var(--navy-fill);color:var(--bw-cream)}   /* 2026-09-18 W3.1-24: was var(--navy) / #fff; Burgundy fill in both themes, cream 9.02 */
.res-map,.res-web,.res-read,.res-mail,.res-emaillink{background:#fff;border:1.5px solid var(--border)!important;color:var(--navy)}
/* TOP-3 VERTICAL (Ryan 2026-06-24): the first three result cards stand out and stack their buttons full-width
   so they are easy to read and tap on a small/cracked phone. Below the top three, the compact row layout stays. */
.res-item-big{border-width:2px;padding:15px 15px}
.res-item-big .rn{font-size:17px}
.res-item-big .rw{font-size:15px;margin:5px 0 6px}
.res-item-big .res-actions{flex-direction:column;flex-wrap:nowrap;gap:8px;margin-top:12px}
.res-item-big .res-actions a,.res-item-big .res-actions button{width:100%;justify-content:center;min-height:48px;font-size:15px}
.res-item .rh{font-size:12.5px;color:var(--muted);margin-top:2px}
/* Find Help search = a navy call-out BLOCK (option C), so it reads as a clear place to start instead of a too-white field that blends into the page. On-brand (sky-soft fill + navy). Reversible: restore the old single-input rule + the old one-line markup. */
/* 2026-09-18 W3.1-3: was background:var(--sky-soft);border:1px solid #C9D6E6;border-radius:14px;padding:14px.
   The tinted panel goes; the field stands on the cream by itself (brandskin section 4, BRANDSPEC search block). */
.res-search{margin:2px 0 18px;background:transparent;border:none;border-radius:0;padding:0}
.res-search-head{font-size:16px;font-weight:700;color:var(--navy);margin-bottom:10px;display:flex;align-items:center;gap:7px;line-height:1.3}
.res-search-head .ic{font-size:18px;flex:none}
.res-search-row{display:flex;gap:8px}
.res-combo{position:relative;flex:1;min-width:0}
/* 2026-09-18 W3.1-3: was border:1.5px solid #B9C6D6;border-radius:10px;font-size:16px;padding:13px 15px. Deep Navy 2px border
   on white 14.04 (non-text). Ink in the field 16.98. Dark twin below: the border goes muted #C9C3B4 on navy 7.99 or it vanishes. */
.res-search input{width:100%;box-sizing:border-box;padding:12px 14px;font-size:18px;border:2px solid var(--bw-navy);border-radius:var(--radius);font-family:inherit;background:var(--surface);color:var(--ink);min-height:56px}
.phone.dark .res-search input,.phone.dark .res-search .res-fauxinput{border-color:var(--muted)}
/* ==================================================================================================
   WORDS 2026-09-23. THE HOME SEARCH BOX IS ONE LINE AGAIN, AND HIS EYE IS WHY.
   `[RYAN 2026-09-23]` "voting onto a second line i nthe search X that one line"
   He was looking at Home. `-webkit-line-clamp:3` below was `3` and the placeholder wrapped, leaving
   the single word "voting..." alone on a second line inside the box. Measured before the change in a
   real Chromium at 390x844: the div is 290px wide with 28px of padding, the English line needs 306px,
   so it took two line boxes and the box stood 69.6px tall instead of 56.
   IT IS NOW `white-space:nowrap; overflow:hidden; text-overflow:ellipsis`. One line, in all sixteen
   languages, at 320, 375 and 390, at Normal and Extra Large, which is the only shape that can hold at
   Extra Large where no sentence in any language fits the field. The ellipsis is what a real input
   does, and this DIV is the stand-in for one.
   `-webkit-line-clamp:1` WAS TRIED FIRST AND IT DOES NOT WORK HERE, MEASURED, NOT ASSUMED. In the
   Chromium this was proved in, the element with `display:-webkit-box; -webkit-line-clamp:1` computed
   its display as `flow-root` and painted TWO lines at 320 Extra Large: read back live, computed
   display "flow-root", clamp "1", scrollHeight 66 against clientHeight 52. A clamp that the engine
   silently drops is a green that lies, so the rule is the plain nowrap ellipsis instead, which is
   what this line already was before 2026-09-02 and which was screenshotted one line at 320 Extra
   Large before this note was written.
   THE 2026-09-02 NOTE BELOW ARGUED THE OPPOSITE AND IT IS KEPT WHOLE, NOT DELETED. It is right that
   an ellipsis eats the examples, which is why the examples were SHORTENED at the same time rather
   than left to be cut: `fhSearchPh` went from "Search for help: food, rent, doctor, voting..." to
   "Search for help: food, rent" + U+2026 in every one of the sixteen, so the whole line now fits
   inside the box at Normal text at all three widths instead of being clipped at every one of them.
   The purpose sits first and the examples last, so the part an ellipsis can ever reach is the part
   that matters least. LAW: his eye decides what correct is.
   Undo: restore the `white-space:normal; overflow-wrap:break-word; display:-webkit-box;
   -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3; overflow:hidden` run in the rule
   below, from NEVER_OVERWRITE_BACKUPS/words_2026-09-23/app.css.PRE_WORDS.
   ================================================================================================== */
/* THE CLIPPED PLACEHOLDER, FIXED 2026-09-02.
   This box is a DIV, not an input - renderHomeHelp() in app.js builds `.res-fauxinput` as a
   decorative stand-in for the real search field, aria-hidden, with no caret and no typing. It was
   `white-space:nowrap;overflow:hidden;text-overflow:ellipsis`, so at 320px it showed 194px of a
   line that needs 315px in English and 358px in Spanish, and the person read
   "Search for help: food, r..." - the examples that tell her WHAT to type were the part cut off.
   Measured in a real browser (clipscan.mjs): scrollWidth/clientWidth was 315/192 en, 358/192 es,
   320/192 km, and it was clipped in every one of the sixteen languages at 320px.
   A nowrap ellipsis is the right behaviour for a real input, where a caret must stay on one line.
   It is the wrong behaviour for a label. This lets it use up to three lines and no more, so it can
   never grow unbounded in a long language, and keeps min-height:56px so the tap target is
   unchanged - `[RYAN 2026-09-02]` "Keep 44px. Screen space matters more." It costs nothing at all
   until the line does not fit, and about 20px per extra line when it does. Three is what the longest of the sixteen (es, fr, pt, lo) actually need at 320px; measured, not chosen.
   Undo: restore the single-line rule from app.css.bak_2026-09-02_pre_fontjank. */
/* 2026-09-18 W3.1-3: was border:1.5px solid #B9C6D6;border-radius:10px;font-size:16px;padding:13px 15px. Muted on white 6.66. */
/* ------------------------------------------------------------------------------------------------
   HOMEUX 2026-10-01. THE CLAMP THE BLOCK ABOVE DESCRIBES WAS NOT IN THE RULE. PUT BACK.

   THE COMMENT DIRECTLY ABOVE THIS ONE IS DATED 2026-09-02 AND IT SAYS, OF THIS VERY RULE: "This
   lets it use up to three lines and no more". IT DID NOT. The declaration shipped
   `white-space:nowrap;overflow:hidden;text-overflow:ellipsis`, which is the single-line behaviour
   that block was written to REMOVE, and its closing line still reads "Undo: restore the single-line
   rule" as though the single-line rule were the thing that had been taken out.

   HOW IT CAME BACK. The `2026-09-18 W3.1-3` line immediately above records a rewrite of this same
   declaration sixteen days later, for a border, a radius, a font-size and a padding. That rewrite
   re-typed the whole declaration and carried the three pre-09-02 properties back in with it. Not a
   disagreement, not a ruling: a declaration re-typed from the wrong copy. Two comments in this file
   have cited the missing clamp as live ever since, and app.css:1336 goes further and names it as the
   precedent it copied its own technique from, "`.res-search .res-fauxinput` (line-clamp, three lines
   up in this file)". The precedent was not there.

   WHAT THE RESIDENT SAW, MEASURED TODAY IN A REAL RENDER, NOT INFERRED: at 320 wide and Extra
   Large the one and only piece of text on Home's search control read "Search for help: f" and then
   an ellipsis. Live `white-space` computed `nowrap`. That is WHOUSES 2026-10-01's "truncated fake
   search box", and the 2026-09-02 block had already measured and fixed it once: scrollWidth over
   clientWidth 315/192 en, 358/192 es, 320/192 km, clipped in every one of the sixteen languages.

   EXACTLY WHAT 2026-09-02 SPECIFIED, NOTHING ADDED. Three lines, never more, so a long language can
   never grow it without bound. `min-height:56px` is untouched, so the tap target does not move:
   `[RYAN 2026-09-02]` "Keep 44px. Screen space matters more." It costs nothing until the line does
   not fit.

   IT TOUCHES HOME ONLY, AND THAT IS CHECKED RATHER THAN HOPED: `.res-fauxinput` is emitted at
   exactly one place in index.html, line 1346, inside `#view-home`, and by renderHomeHelp() in
   app.js, which paints the same element into `#home-help`. Help, Events, Info and News use a REAL
   `<input>`, which still gets a single line and a caret, which is correct for an input and is the
   distinction the 09-02 block drew: "A nowrap ellipsis is the right behaviour for a real input. It
   is the wrong behaviour for a label."
   Undo, one line: put `white-space:nowrap;overflow:hidden;text-overflow:ellipsis` back in place of
   the five clamp properties.
   Pre-change file: NEVER_OVERWRITE_BACKUPS/homeux_2026-10-01/app.css.PRE_HOMEUX
   ------------------------------------------------------------------------------------------------ */
.res-search .res-fauxinput{width:100%;box-sizing:border-box;padding:12px 14px;font-size:18px;min-height:56px;border:2px solid var(--bw-navy);border-radius:var(--radius);font-family:inherit;background:var(--surface);color:var(--muted);text-align:start;overflow-wrap:break-word;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;line-clamp:3;overflow:hidden}
.res-search input:focus{outline:3px solid var(--navy);outline-offset:1px}
/* DESIGNSYSTEM 2026-09-04 [A4-resgo-font]: font-family:inherit added. A <button> gets the browser default face
   unless told otherwise. .res-go is a <span> on Home (inherits Source Sans Pro) and a <button> on
   Events, Help, Info and News (computed Arial). Same class, same size, two typefaces, four screens.
   Undo: delete `font-family:inherit;`. */
.res-go{flex:none;display:flex;align-items:center;justify-content:center;background:var(--navy-fill);color:var(--bw-cream);border:none;border-radius:var(--radius);width:56px;min-width:56px;min-height:56px;   /* 2026-09-18 W3.1-24: was var(--navy)/#fff, radius 10px, 54x48. Burgundy 9.02, 56 to match the field */font-size:22px;line-height:1;cursor:pointer;font-family:inherit}
.res-go:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
/* Info tab: prominent Mayor card + expandable officials pulldown (Ryan 2026-07-04). */
.mayor-card{padding:0;overflow:hidden}
.mayor-photo{width:100%;aspect-ratio:1/1;max-height:340px;background-size:cover;background-position:center top;background-color:var(--sky-soft)}
/* NAVYFILL 2026-09-23: was background:var(--navy), which is Community Gold in dark: white on gold 2.62:1.
   --navy-fill is Burgundy in both themes, white 9.73. This is a 64px initial, so it was large text and
   never body, but it is the same defect and the same one-token fix. Undo: var(--navy). */
.mayor-photo-none{display:flex;align-items:center;justify-content:center;aspect-ratio:1/1;max-height:200px;background:var(--navy-fill,var(--navy));color:#fff;font-family:var(--font-label);font-weight:700;font-size:64px}
.mayor-body{padding:14px 16px 16px}
.mayor-role{font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-weight:700}
.mayor-name{font-family:var(--font-label);font-weight:700;font-size:22px;color:var(--navy);margin:2px 0 10px;line-height:1.15}
.mayor-btns{display:flex;gap:8px}
/* BW-19, 2026-09-24. The two mayor buttons are `flex:1 1 0` in a row that cannot wrap, so at
   320px on the Extra Large step the pair needed 76px of a 70px box in English, 81 of 72 in Spanish
   ("Llamar Correo") and 78 of 72 in Khmer. Wrapping is the whole fix: when both labels will not
   share a line they stack, each at full width, which is also the easier pair to hit. Nothing
   shrinks and neither label is cut. `min-width:0` lets a button narrow to its box before the row
   decides to wrap, so the common case still shows them side by side. */
.mayor-btns{flex-wrap:wrap;row-gap:8px;min-width:0}
/* Letting the ROW wrap is only half of it: measured after the first version of this rule, the row
   stopped overflowing and `a.mayor-btn` kept overflowing inside it, 109 flagged boxes across the
   1,728-cell sweep. A button whose label is one word ("Llamar", "Correo") still asks for that
   whole word, so it needs permission to break it when the line it has is narrower than the word.
   `anywhere` and not `break-word`, for the reason written out at line 1316: only `anywhere` takes
   part in intrinsic sizing. `min-height:48px` above is untouched. */
.mayor-btn{min-width:0;flex:1 1 120px;white-space:normal;overflow-wrap:anywhere;text-align:center}
.mayor-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;min-height:48px;border-radius:var(--radius);border:2px solid var(--navy);   /* 2026-09-18 W3.1-24: was radius 10px, 1.5px */color:var(--navy);background:var(--surface);font-weight:700;font-size:15px;text-decoration:none}
.mayor-btn:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
.off-roster{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);margin-bottom:14px;overflow:hidden}   /* 2026-09-18 W3.1-24: was radius 14px */
.off-roster>summary{list-style:none;cursor:pointer;padding:14px 16px;font-family:var(--font-label);font-weight:700;font-size:16px;color:var(--navy);display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:52px;box-sizing:border-box}
.off-roster>summary::-webkit-details-marker{display:none}
.off-roster>summary::marker{content:""}
.off-chev{transition:transform .2s ease;font-size:13px;color:var(--muted);flex:none}
.off-roster[open]>summary .off-chev{transform:rotate(180deg)}
.off-roster>summary:focus-visible{outline:3px solid var(--navy);outline-offset:-3px}
/* ==================================================================================================
   CONSIDERED 2026-09-25. THE OFFICIALS ROSTER'S CONTROL IS A BUTTON NOW, AND IT CARRIES THE COUNT.
   `[RYAN 2026-09-23]` "Details with the down arrow can be dropped entirely when you can just click
   the title". app.js now emits a <button class="off-roster-h"> and a <div class="off-roster-b">
   instead of <summary>/<details>, for the reasons written in full at the call site in renderAbout().

   THE FOUR SUMMARY RULES ABOVE ARE KEPT, NOT DELETED, AND THEY ARE NOT DEAD CODE ON PURPOSE.
   `.off-roster` is still the wrapper class, so its box above still paints this control. The
   `>summary` and `.off-chev` rules simply match nothing on Info any more. They are left in place
   because a town that has not taken this change, and any older copy of app.js replayed against this
   sheet, still emits a <details>, and LAW L3 says never delete. Restoring the old markup restores
   the old look with no CSS edit at all. `.off-chev` is also still drawn by nothing else: grep says
   app.js was its only writer, and that line is gone from app.js as of tonight.

   THE BADGE IS NOT A NEW OBJECT. `.off-roster-n` is `.csv-accn` (index.html:1583, the gold count on
   the seven service doors twelve hundred pixels up the same screen) declaration for declaration:
   transparent fill, 1px --bw-gold border, --bw-gold-ink text, --font-label, .1em tracking, 12px 700,
   999px radius, tabular numerals. Two differences, both because the two controls are laid out
   differently: this one is a flex sibling rather than absolutely positioned against a padded button,
   and it has no inset-inline-end. The COLOURS AND THE TYPE ARE IDENTICAL, so the contrast measured
   on the doors is the contrast here, and a resident reads one kind of badge on this screen, not two.

   12px MINIMUM (the app.css gate): the badge is exactly 12px, which is the gate's floor and the same
   size the doors' badges are. Nothing here is smaller. No third sub-12px rule is added to this file.

   44px: the button is min-height 52px, the same 52px the <summary> had, and it is full width, so its
   own centre is a 44x44 target with room to spare. The badge is not a target; it is inside the
   button, and a tap on the badge's centre is a tap on the button.

   UNDO: delete this block and the .off-roster-* rules under it. With the app.js block also undone,
   the four rules above take over again unchanged.
   Backup: NEVER_OVERWRITE_BACKUPS/considered_2026-09-25/app.css.PRE_CONSIDERED
   ================================================================================================== */
.off-roster-h{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:10px;
  min-height:52px;padding:14px 16px;margin:0;text-align:start;cursor:pointer;
  background:none;border:0;border-radius:var(--radius);
  font-family:var(--font-label);font-weight:700;font-size:16px;color:var(--navy);
  -webkit-tap-highlight-color:transparent}
.off-roster-lbl{flex:1 1 auto;min-width:0}
.off-roster-n{flex:none;min-width:18px;padding:2px 8px 1px;border-radius:999px;background:transparent;
  border:1px solid var(--bw-gold);color:var(--bw-gold-ink);
  font-family:var(--font-label);letter-spacing:.1em;font-size:12px;font-weight:700;line-height:1.5;
  text-align:center;font-variant-numeric:tabular-nums}
.off-roster-h:focus-visible{outline:3px solid var(--navy);outline-offset:-3px}
.off-roster-b[hidden]{display:none}
.phone.dark .off-roster-h{color:var(--ink)}
.phone.dark .off-roster-n{color:var(--bw-gold);border-color:var(--bw-gold)}
.off-inner{padding:0 16px 14px}
/* STACKORDER 2026-09-09 (NEVER_OVERWRITE/STACKORDER_2026-09-09.md). THE ONE CANONICAL STACKING
   ORDER FOR THIS APP, LOW TO HIGH. Every z-index in app.css/brand*.css should be explainable as one
   of these six tiers; if it is not, that is a bug, not a new tier.
   1. page content - default paint order, no z-index.
   2. ordinary (non-crisis) suggestion dropdown - `.res-suggest.open` base 50, `.res-search`/
      `#home-help` ancestor base 36.
   3. nav / bottom dock, at rest - `.nav` 50, `#bw-dock` 60. Deliberately ABOVE tier 2: an open,
      non-crisis dropdown may be covered at its very bottom edge by the dock; that is accepted, not a
      bug (nothing crisis-relevant is ever in that dropdown).
   4. modals / sheets / overlays - 65 (settings sheet) up to 400 (`.wrr-overlay`, the highest normal
      overlay). Above the dock at rest, so a modal is never trapped under nav chrome.
   5. THE CRISIS DOCK WHILE SHOWING - `#bw-dock[data-help="1"]` at 401. Above every tier-4 modal,
      no exceptions: "no modal may ever cover a crisis control" (this brief, LAW.md L1).
   6. CRISIS SUGGESTION ROWS, ONLY WHILE A CRISIS ROW IS THE THING ON SCREEN - `.res-suggest.open`
      itself at 402 (below), PLUS its ancestor `.res-search`/`#home-help` promoted to 402 so the
      descendant's rank can actually be compared against tier 5 instead of being trapped under the
      ancestor's own tier-2 stacking context (see SEARCHUX comment below for why the ancestor, not
      the descendant, has to move). Above tier 5 so a crisis-sug row painted inside an open dropdown
      is never lost under the crisis dock sitting behind it.
   THE RULE THAT MUST NEVER REGRESS: tier 6's ancestor promotion is scoped to `:has(.res-suggest.open
   .crisis-sug)`, NOT `:has(.res-suggest.open)` alone. An ordinary dropdown (tier 2) must never climb
   to tier 6 just because it is open - if it did, an everyday, non-crisis search would sit ABOVE the
   crisis dock (tier 5) and cover 911/988/text-988 while typing. THIS IS THE P0 THIS COMMENT BLOCK
   WAS WRITTEN TO CLOSE: the 2026-09-09 04:47 SEARCHUX rule below originally scoped on
   `:has(.res-suggest.open)` with no crisis-sug qualifier, which correctly fixed crisis-row
   reachability but silently let every ordinary dropdown ride the same promotion and black out the
   crisis floor whenever both were open at once - found by the REPEATS audit ~05:50. Fixed by adding
   the `.crisis-sug` descendant qualifier below; the fix is one word-class narrower, not a new
   mechanism. Falsifier: `_HARNESS/FALSIFY_STACKORDER_2026-09-09.mjs`. */
/* Autosuggest dropdown (W3C editable-combobox, list autocomplete). Icon + text rows (text-only fails low-literacy users), 48px targets, shows on focus so people who do not know the word still get options. Reversible: remove these rules + the combo JS + the listbox markup. */
/* 2026-09-18 W3.1-3: was background:#fff;border:1.5px solid #B9C6D6;border-radius:12px. Deep Navy frame so the list reads as the field's own. */
.res-suggest{list-style:none;margin:6px 0 0;padding:6px;position:absolute;left:0;right:0;top:100%;z-index:50;background:var(--surface);border:2px solid var(--bw-navy);border-radius:var(--radius);box-shadow:0 6px 18px rgba(13,32,24,.16);display:none;max-height:330px;overflow:auto}
.res-suggest.open{display:block}
/* CRISISBAR 2026-09-09: z-index 50 was BELOW #bw-dock's 60 (bottom_dock_2026-08-15.css) and its
   401 while Help's crisis bar shows (this file, "CRISIS DOCK, INVIOLABLE TOP STACKING"). Found
   while proving the new slim-mode crisis-suggestion rows hit-testable under CSS zoom: at
   390x844/text-large and text-xlarge with the keyboard open, the OPEN dropdown's crisis-sug rows
   (still >=44px tall, per SLIM MODE above) can extend far enough down the screen at that zoom to
   reach the fixed dock's own on-screen band, and the dock - painted on top at a higher z-index -
   answered the tap instead of the row underneath it (elementFromPoint resolved `.nbtn`, the nav
   button, not the crisis-sug list item). A search suggestion list should never lose a tap to the
   nav sitting behind it, in slim mode or not, at any zoom; z-index 50 has always been below the
   dock's 60 baseline, this just was not exercised by zoom + a tall dropdown before tonight. Undo:
   delete this rule (z-index falls back to the base 50 above). */
.res-suggest.open{ z-index: 402; }
/* SEARCHUX 2026-09-09 (NEVER_OVERWRITE/SEARCHUX_2026-09-09.md). The rule above raises the LIST
   itself to z-index:402, but a descendant's z-index cannot escape an ancestor that already opened
   its own stacking context. `.res-search` (and `#home-help`, same shape) carries z-index:36 -
   normally inert at position:static, EXCEPT `#view-community`/`#ev-search-wrap`/`#news-search-wrap`
   /`#home-help` are flex items or flex/sticky containers, and CSS honours z-index on a flex item
   (or a position:sticky element) even without position:relative/absolute. That makes `.res-search`
   its OWN stacking context ranked at 36 among its siblings, and everything inside it - including
   the crisis-sug rows' z-index 402 two lines up - is capped under that ceiling. Bumping the LIST's
   z-index further (tested to 999999) could never fix this: the list was never competing with
   #bw-dock's 401 in the first place, `.res-search` was, at 36. Measured real Chromium, 390x844,
   text-large, keyboard open, typing a crisis phrase into #res-q: elementFromPoint on both crisis-sug
   rows resolved `.nbtn` (the nav dock's button), not the row underneath, until this rule; GREEN
   after (_HARNESS/FALSIFY_CRISISBAR_2026-09-09.mjs, gate A-slim, community/res-q 390x844
   text-large - the one case the same file's own comment had named "KNOWN GAP, NOT FIXED TONIGHT").
   Scoped to :has(.res-suggest.open) so a search box only outranks the dock while its own suggestion
   list is actually open, never at rest and never mid-keystroke before the list renders. :has() is
   already load-bearing elsewhere in this file (app.css:1339-1340), so no new browser-support floor
   is introduced. `!important` needed: `#view-community > .res-search{z-index:36}` (app.css:2559ish)
   carries an ID selector, (1,1,0), which beats this class+:has rule's (0,3,0) on specificity alone
   even though this rule matches - measured RED with no !important, GREEN with it. Same file already
   uses !important on sibling rules in that exact ID block (app.css ~2566, ~2650) for the same
   "this one box, this one state" reason. Undo: delete this rule; `.res-search`/`#home-help` fall
   back to z-index:36.
   CORRECTED 2026-09-09 ~10:10 UTC (STACKORDER, NEVER_OVERWRITE/STACKORDER_2026-09-09.md), P0: this
   rule originally read `:has(.res-suggest.open)` with no further qualifier, which promotes the WHOLE
   search box - and everything painted inside it, not just the crisis-sug rows this rule exists for -
   to z-index 402 the instant ANY suggestion list is open, crisis-related or not. 402 is above
   `#bw-dock[data-help="1"]`'s 401, so an ordinary, non-crisis dropdown (e.g. typing "food" or "poll")
   was silently outranking the crisis dock and could paint over 911/988/text-988 at the exact sizes
   where a tall dropdown and the fixed dock's on-screen band overlap (Large/XLarge text, 390 and 320
   wide) - found by the REPEATS audit. Added the `.crisis-sug` descendant qualifier so the ancestor
   only promotes when the open list actually contains a crisis row, matching the ONLY case this rule
   was ever meant to cover (see the CRISISBAR comment two rules up: "the OPEN dropdown's crisis-sug
   rows... reach the fixed dock's own on-screen band"). Ordinary dropdowns now stay at their base 36
   and correctly remain BELOW the dock, tier 3 over tier 2, per the canonical order above. Falsifier:
   `_HARNESS/FALSIFY_STACKORDER_2026-09-09.mjs`, gate A (planted RED on the old unqualified selector,
   GREEN on this one). Undo: delete `.crisis-sug` from both selectors below to revert to the
   SEARCHUX-only behavior (re-introduces this P0). */
/* RESULTSTOP 2026-09-09. [RYAN] "the help search error with the nav buttons on top of the
   results is back and has regressed... Search results can go on top i think so they can be
   seen on a small phone if you agree". I agree, and this was my own regression from an hour
   earlier: narrowing the promotion to crisis rows only (STACKORDER, above) fixed the crisis
   bar being covered but handed the nav dock the power to cover ORDINARY results, which on a
   320px phone with the keyboard up is most of what she can see.
   BOTH are now true, because they are different elements and do not have to compete:
     - the suggestion list is promoted above the dock whenever it is open, crisis row or not,
       so results are always readable. The nav is not needed while she is typing.
     - the crisis bar inside the dock is lifted above the list, so 911 / 988 / text-988 stay
       on top of everything and stay tappable. LAW L1 is not traded away for this.
   UNDO: NEVER_OVERWRITE_BACKUPS/navpin_2026-09-09/app.css.PRE_RESULTSTOP_* */
.res-search:has(.res-suggest.open),#home-help:has(.res-suggest.open){z-index:402 !important}
/* SUGCAP 2026-09-09. The list may sit ABOVE the nav (his ask, so results are readable on a
   small phone) but it must never grow so tall that it swallows the crisis floor. Measured
   2026-09-09: at .text-large and .text-xlarge the open list reached 42 rows and covered
   911/988/text-988 (0 of 3 reachable) while ordinary results were fine at normal size.
   He rejected the alternative of floating the crisis bar over the results: "wait crisis bar
   with keyboard out on top is wrong i think will look really weird". He was right.
   So the LIST is bounded instead of the bar being lifted. --bw-dockh is the dock's own live
   measured height, published by bottom_dock_2026-08-15.js, so this tracks the slim/full
   crisis bar automatically instead of hardcoding a number. 24px of breathing room below.
   The list scrolls, so no result becomes unreachable - it just stops before the floor.
   UNDO: delete this rule and the two zoom-tier rules below it.

   SUGFLOOR 2026-09-09 (NEVER_OVERWRITE/SUGFLOOR_2026-09-09.md), WHY THE CAP ABOVE NEVER TOOK
   EFFECT AT LARGE/XLARGE - TWO STACKED BUGS, NOT ONE.
   BUG 1, THE REAL ONE LIVE (THIS RULE WAS DEAD CODE): `#res-suggest` already carries an INLINE
   `style="max-height:...px"` written by JS - `fitToCrisisBar()` in
   `autopopulate_names_2026-08-17.js` (primary) and `capAgainstCrisisCard()` in
   `autopop_2026-08-28.js` (a further min() on top of it). An inline style always wins over any
   external stylesheet rule, calc() or not, regardless of specificity - this CSS rule was never
   in the fight. MEASURED: with the fix below applied to app.css alone, computed max-height was
   byte-identical to before (408px/451px at 390 text-large/xlarge) because `ul.getAttribute(
   'style')` still read `"max-height: 408px;"`. Confirmed by reading the live inline attribute,
   not assumed.
   BUG 2, THE SAME ROOT CAUSE IN BOTH PLACES: both JS functions measure the real, on-screen gap
   with `getBoundingClientRect()` (already zoom-scaled, correct) and then assign that REAL number
   straight to `.style.maxHeight` as if it were a LOCAL (pre-zoom) length. `.res-suggest` sits
   inside `.phone`, which carries CSS `zoom:1.5`/`zoom:2` at `.text-large`/`.text-xlarge`
   (app.css ~876-877); zoom scales an element's OWN used length values at render time, so the
   real on-screen box ends up `assigned-value * zoom` - 1.5x/2x taller than the real gap that was
   actually measured (`.phone.text-large{zoom:1.5}` / `.phone.text-xlarge{zoom:2}`, app.css
   877-879). `getComputedStyle` reports the pre-scale number back, which is why 408px
   (computed) rendered as a 612px-tall box (408 * 1.5, measured via `getBoundingClientRect()`).
   THE FIX, both bugs: `autopopulate_names_2026-08-17.js` `fitToCrisisBar()` and
   `autopop_2026-08-28.js` `capAgainstCrisisCard()` now divide their real-px measurement by the
   live `.phone` zoom factor (`getComputedStyle(document.querySelector('.phone')).zoom`, the one
   CSS property `getComputedStyle` DOES report honestly even under `zoom` - confirmed live,
   read back "1.5"/"2" exactly) before writing `.style.maxHeight`, so `assigned * zoom` lands
   back on the real gap that was actually measured. See those two files for the diff; this
   comment documents the shared root cause so a future zoom-scaled inline-style write does not
   reintroduce the same class of bug a third time.
   THIS RULE STAYS as the CSS-only fallback for the no-JavaScript path (the file header above
   already promises the plain box degrades gracefully with JS dead), now zoom-corrected the same
   way as the JS fix, on the same reasoning: the real-px target this calc produces is consumed
   inside the same zoomed subtree, so it is divided by the same two zoom tiers before use. Only
   two zoom values exist for `.phone` (1.5, 2), so two tier rules, source order after the base
   rule so cascade (not specificity) picks the right one per class. `.phone.bigtext` is the same
   zoom:1.5 as `.phone.text-large` (app.css 877-878) and shares its divisor.
   MEASURED AFTER (both JS fixes + this CSS fallback), real Chromium, typing "food" into
   `#res-q`, no crisis phrase (the crisis bar stays full height - the harder case): see
   NEVER_OVERWRITE/SUGFLOOR_2026-09-09.md for the full 6-cell table.
   Falsifier: `_HARNESS/FALSIFY_SUGFLOOR_2026-09-09.mjs`. */
/* PHONEFIT 2026-09-24, THE `100vh` BUG THESE THREE RULES SHIPPED WITH.
   `[RYAN 2026-09-24]` "it is all a bit smooshed on a phone with the keyboard open".
   `100vh` is the LARGE viewport. It does not shrink when the on-screen keyboard opens, so with the
   keyboard up these three subtracted the dock and the top bar from a height that is hundreds of px
   taller than anything she can see. MEASURED, real Chromium, 390x844 with the visual viewport at
   400 (the keyboard case), typing "food" into `#res-q`: the open dropdown was given
   844-120-140-24 = 560px and ran to y=401, which is 1px past the last pixel of the screen AND
   straight over the top of the crisis bar. `elementFromPoint` at the centre of the 911 door
   returned the dropdown, not the door. THAT IS LAW L1: with the keyboard open, 911 got HARDER to
   reach. It is fixed by subtracting the ONE keyboard number this app already publishes and already
   trusts everywhere else, `--bw-kbdlift` (bottom_dock_2026-08-15.js, keyboardHiddenPx(), the same
   value the dock itself is lifted by and the same value html[data-crisis-slim="1"] is gated on).
   No second detector, no screen-height guess, no new script, no new sheet: one added term.
   Keyboard closed, `--bw-kbdlift` is unset, the fallback is 0px and all three calc()s are
   byte-equivalent to what they computed before this edit. MEASURED AFTER, same cell: 116px, the
   dropdown ends above the crisis bar and 911 hit-tests to the 911 door again.
   Undo: delete the three ` - var(--bw-kbdlift, 0px)` terms below. */
/* SUGROOM 2026-09-28 (DOCKFIX). SUPERSEDED VALUES, KEPT HERE SO THE UNDO IS A COPY AND PASTE:
     .res-suggest.open{max-height:calc(100vh - var(--bw-kbdlift, 0px) - var(--bw-dockh, 120px) - var(--bw-topfixh, 140px) - 24px);...}
     .phone.bigtext .res-suggest.open,
     .phone.text-large .res-suggest.open{max-height:calc((same) / 1.5)}
     .phone.text-xlarge .res-suggest.open{max-height:calc((same) / 2)}
   THE ARITHMETIC ABOVE IS NOT WRONG, IT IS INCOMPLETE, AND THE THREE NEW `min(` TERMS ARE THE ONLY
   CHANGE: every original term is still there, in the same order, with the same fallbacks.
   `--bw-topfixh` is the bottom of the top FIXED stack (app.js:6467 and stickreserve_2026-08-30.js:188
   both skip any box whose computed position is not `fixed`). The Help search field is NOT fixed, it
   is in flow inside `.res-combo`, and the list hangs off it at `top:100%`. So everything between the
   sticky header and the top of the list is being charged to the flat 24px at the end of the sum.
   MEASURED 2026-09-28, real Chromium, Korean, 320x568, Large text, keyboard 260px shimmed on
   visualViewport and the lift computed by the app's own liftForKeyboard(): the header ends at 83.5,
   the list starts at 169.2, so 85.7 rendered pixels live in that 24px budget. The cap computed 124.5
   rendered pixels of list where 62.8 existed; the list ran 61.7px past the top of the dock and all
   three of 911, 988 and text-988 hit-tested to the list instead of to themselves. Same in Arabic.
   NOT FIXED BY RESTACKING. `.res-suggest.open{z-index:402}` above is ONE above the dock's 401 on
   purpose, and FALSIFY_STACKORDER_2026-09-09.mjs guards it, because the list's own crisis-sug rows
   carry 911 and 988 and the dock used to steal their taps. The list stays on top and is made to fit.
   --bw-sugroom is the rendered gap between the open list's own top and the top of the dock, measured
   live by bottom_dock_2026-08-15.js measure() (rAF-coalesced, re-read on every DOM mutation, scroll,
   resize and viewport change, removed the moment the list closes). It is RENDERED pixels, the same
   arena 100vh, --bw-dockh, --bw-topfixh and --bw-kbdlift are already in here, so it goes inside the
   same bracket and inherits the same / 1.5 and / 2. Fallback 100vh means min() picks the old term
   and the rules are byte-equivalent to the three above if the script never runs.
   KEYBOARD CLOSED THIS STILL APPLIES, and it should: a list that reaches the crisis floor is wrong
   with or without a keyboard. Measured keyboard-closed at 390x844 Normal it changes 594.5 to 533,
   which is the 61.5px it was already overlapping the dock by.
   Undo: paste the three lines above back over the three below. */
/* SPACE2 site 2 of 3, and the one that costs a resident the most. This is the box that covers her
   results while she types. PHONEFIT 2026-09-24's own note directly above says `100vh` is the LARGE
   viewport and does not shrink for the keyboard, and it fixed the keyboard half with
   `- var(--bw-kbdlift)`. It could not fix the URL-BAR half, because there was no unit for it in 2026.
   There is: `svh`. The three rules below are duplicated with `100svh` so the arena the suggestion
   list is allowed to fill is the glass she can see and not the glass she would have if the URL bar
   went away. `--bw-sugroom`'s own fallback is changed with it, so the `min()` still picks the arena
   term when the script has published nothing. This is a `max-height` on a `position:absolute` box
   inside a scroller, not on anything fixed, so it cannot relayout the dock or the masthead.
   MEASURED: at 320 the iOS URL bar is worth 25px of this box and the Android omnibox 56px.
   Undo: delete the three added declarations. */
.res-suggest.open{max-height:calc(min(100vh - var(--bw-kbdlift, 0px) - var(--bw-dockh, 120px) - var(--bw-topfixh, 140px) - 24px, var(--bw-sugroom, 100vh)));overflow-y:auto;-webkit-overflow-scrolling:touch}
.res-suggest.open{max-height:calc(min(100svh - var(--bw-kbdlift, 0px) - var(--bw-dockh, 120px) - var(--bw-topfixh, 140px) - 24px, var(--bw-sugroom, 100svh)));overflow-y:auto;-webkit-overflow-scrolling:touch}
.phone.bigtext .res-suggest.open,
.phone.text-large .res-suggest.open{max-height:calc(min(100vh - var(--bw-kbdlift, 0px) - var(--bw-dockh, 120px) - var(--bw-topfixh, 140px) - 24px, var(--bw-sugroom, 100vh)) / 1.5)}
.phone.text-large .res-suggest.open{max-height:calc(min(100svh - var(--bw-kbdlift, 0px) - var(--bw-dockh, 120px) - var(--bw-topfixh, 140px) - 24px, var(--bw-sugroom, 100svh)) / 1.5)}
.phone.text-xlarge .res-suggest.open{max-height:calc(min(100vh - var(--bw-kbdlift, 0px) - var(--bw-dockh, 120px) - var(--bw-topfixh, 140px) - 24px, var(--bw-sugroom, 100vh)) / 2)}
.phone.text-xlarge .res-suggest.open{max-height:calc(min(100svh - var(--bw-kbdlift, 0px) - var(--bw-dockh, 120px) - var(--bw-topfixh, 140px) - 24px, var(--bw-sugroom, 100svh)) / 2)}
/* [RYAN 2026-09-09] "wait crisis bar with keyboard out on top is wrong i think will look
   really weird". He is right, and it was my line, added minutes earlier. Lifting the crisis
   bar above an open dropdown made it float over the results, which reads as broken.
   REMOVED. The results now sit above the dock (his ask) and the crisis bar stays exactly
   where it has always been. It is still reachable: while typing it is already collapsed to
   the slim 44px row by his own earlier ruling, and the dropdown grows downward from the
   search box rather than over the floor. */
.res-suggest li{display:flex;align-items:center;gap:11px;padding:12px;border-radius:9px;font-size:16px;color:var(--ink);cursor:pointer;min-height:48px;line-height:1.3}   /* 2026-09-18: radius LEFT at 9px on purpose: li.crisis-sug inherits it and has no radius of its own (crisis computed style must not move) */
.res-suggest li .ic{font-size:20px;flex:none}
/* SUGFLOOR 2026-09-09 (NEVER_OVERWRITE/SUGFLOOR_2026-09-09.md). SEPARATE, PRE-EXISTING BUG
   SURFACED WHILE PROVING THE CAP ABOVE, NOT CAUSED BY IT (reproduced against the pre-session
   backup too): autopopulate name-match rows (`.ap-row .ap-nm`, autopopulate_names_2026-08-17.js;
   `.ap2-row .ap2-nm`, autopop_2026-08-28.js) have no width/wrap rule of their own, so at
   `.text-large`/`.text-xlarge` a real name wraps across many lines inside a real on-screen row
   width that does NOT grow with zoom (bounded by the physical screen, ~228px real at 320 wide)
   while the icon+gap+padding overhead DOES grow with zoom (all specified in local px, scaled
   1.5x/2x at render) - MEASURED live: "Find food near you" (19 chars) rendered a 239px-tall row
   at 320x568/text-xlarge, one of several matches seen from 147px to 833px, which by itself can
   crowd the SUGCAP-capped area down to 0-2 fully visible rows even though the cap itself is
   correctly sized and the crisis floor stays reachable throughout (a taller-but-still-capped
   list scrolls, it does not cover the dock). THE FIX, same technique this file already uses for
   `.res-search .res-fauxinput` (line-clamp, three lines up in this file): cap the name text at
   two lines with an ellipsis rather than letting it wrap indefinitely, so one row's real height
   stays bounded and predictable at every zoom instead of occasionally consuming most of the
   capped area by itself. Never hides or truncates the underlying data - `data-label`/`data-q`
   still carry the full name for `resSugChoose()`, only the ON-SCREEN text is clamped, and a
   screen reader still gets the full name via the `<li>`'s accessible name (text content is
   unchanged in the DOM, only visually clipped). Undo: delete these two rules. */
.res-suggest li.ap-row,.res-suggest li.ap2-row{align-items:flex-start}
.res-suggest li .ap-nm,.res-suggest li .ap2-nm{overflow-wrap:break-word;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden}
/* 2026-09-04, REAL PHONE, TAP FEEDBACK. `:active` added alongside `:hover`. A phone has no hover:
   `matchMedia('(hover:none)')` is TRUE on every real handset, so on her phone this rule fired for
   nobody and a tapped suggestion changed colour not at all between the touch and the navigation.
   Measured 2026-09-04, _REALPHONE_2026-09-04/: 40 `:hover` selectors ship; this one and .csv-item
   below were the two with NO `:focus`/`:active` twin that sit on something a finger actually
   presses. Nothing moves and nothing resizes - the same one background colour the mouse got.
   Undo: delete `.res-suggest li:active,` from the selector list. */
.res-suggest li:hover,.res-suggest li:active,.res-suggest li.active,.res-suggest li[aria-selected="true"]{background:var(--sky-soft)}
.res-suggest li.sug-sub{padding:9px 12px 9px 34px;min-height:44px;font-size:14.5px;font-weight:600;color:var(--navy)}
.res-suggest li.sug-sub .ic{font-size:16px}
.res-suggest li.crisis-sug{background:#fbeaea;align-items:flex-start;border-left:4px solid var(--red,#a32525)}
.res-suggest li.crisis-sug .cs-main{display:flex;flex-direction:column;gap:2px;flex:1}
.res-suggest li.crisis-sug .cs-lbl{font-weight:800;color:var(--red,#a32525)}
.res-suggest li.crisis-sug .cs-desc{font-size:12.5px;color:var(--ink);font-weight:500;line-height:1.35}
.res-suggest li.city-sug{background:var(--sky-soft);align-items:flex-start}   /* 2026-09-18 W3.1: was #e9f1fb (blue tint); Cream Deep */
.res-suggest li.city-sug .cs-main{display:flex;flex-direction:column;gap:2px;flex:1}
.res-suggest li.city-sug .cs-lbl{font-weight:650}
.res-suggest li.city-sug .cs-desc{font-size:12.5px;color:var(--muted);font-weight:400;line-height:1.35}
.res-suggest li.city-sug::after{content:"\2197";color:var(--navy);font-weight:700;margin-left:6px}
.home-help{margin:2px 0 16px}
/* 042 (Ryan): when the alert bar is showing, tighten the space above the help/search bar (was 18px -> ~9px). Structural, every city. */
#topbar-below:not(:empty) + main .view{padding-top:9px}
#topbar-above:not(:empty) ~ main #view-home .home-help{margin-top:0}
/* 047 (0.5): dead .hh-bar/.hh-txt/.hh-chips/.hh-chip rules removed. These were fossils of the old home-help bar; renderHomeHelp now draws the .res-search twin (the Home->Help continuity, D1) and the .hh-chips block it once computed is discarded, never inserted. The live .home-help container + .res-search styling stay. */
.help-cats{display:flex;flex-wrap:wrap;gap:8px;margin:2px 0 16px}
.help-cat{background:var(--sky-soft);color:var(--navy);border:1.5px solid var(--border);border-radius:99px;padding:7px 11px;font-size:13px;font-weight:650;cursor:pointer;min-height:40px;text-align:left}
.res-suggest li.help-sug{background:var(--canvas);align-items:flex-start}   /* 2026-09-18 W3.1: was #eafaf1 (green tint); cream */
.res-suggest li.help-sug .cs-main{display:flex;flex-direction:column;gap:2px;flex:1}
.res-suggest li.help-sug .cs-lbl{font-weight:700;color:var(--green)}
.res-suggest li.help-sug .cs-desc{font-size:12.5px;color:var(--muted);font-weight:400;line-height:1.35}
.res-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.res-spec{display:inline-block;margin-inline-start:7px;background:var(--green-soft);color:var(--green);font-size:12px;font-weight:700;letter-spacing:.02em;padding:2px 9px;border-radius:999px;vertical-align:middle}
.res-lang{display:inline-block;margin-inline-start:7px;background:var(--gold);color:var(--bw-navy);font-size:12px;font-weight:800;letter-spacing:.02em;padding:2px 9px;border-radius:999px;   /* 2026-09-18 W3.1: was #3d2c06; Deep Navy on Gold 5.35. Pill radius kept: the guide draws count pills fully rounded */vertical-align:middle}
.res-nodeadend{border:1.5px solid var(--gold);background:var(--gold-soft);border-radius:var(--radius);padding:14px}   /* 2026-09-18 W3.1-25: was border-radius:13px */
.res-nodeadend .rn{font-weight:800;font-size:15.5px;color:var(--navy)}
.res-nodeadend .rw{font-size:14px;color:var(--ink);line-height:1.45;margin-top:4px}
/* EMERGENCY MODE (Home only): city-declared, transforms the Home layout. Red, unmissable, leads with what to do. */
.emg-banner{background:var(--red);color:#fff;border-radius:16px;padding:16px;margin-bottom:14px;box-shadow:0 4px 14px rgba(163,74,58,.3)}
.emg-banner .emg-tag{display:inline-block;background:#fff;color:var(--red);font-weight:800;font-size:12px;letter-spacing:.08em;text-transform:uppercase;padding:3px 9px;border-radius:999px;margin-bottom:8px}
.emg-banner h2{color:#fff;font-size:21px;line-height:1.2;margin:0 0 6px}
.emg-banner p{font-size:15px;line-height:1.5;margin:0;opacity:.97}
.emg-sec{font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--red);margin:16px 2px 8px}
.emg-item{border:1.5px solid var(--red-soft);border-inline-start:5px solid var(--red);border-radius:13px;padding:13px 14px;margin-bottom:9px;background:var(--surface)}
.emg-item .en{font-weight:800;font-size:15.5px;color:var(--navy)}
.emg-item .ew{font-size:14px;color:var(--ink);line-height:1.45;margin:3px 0 4px}
.emg-item .ea{font-size:12.5px;color:var(--muted)}
.emg-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:9px}
.emg-actions a{text-decoration:none;border-radius:9px;padding:9px 15px;font-weight:800;font-size:13.5px;min-height:44px;display:inline-flex;align-items:center;gap:6px;font-family:inherit}
.emg-call{background:var(--red);color:#fff}
.emg-map{background:#fff;border:1.5px solid var(--border);color:var(--navy)}
.emg-note{background:var(--canvas);border:1px dashed var(--border);border-radius:12px;padding:13px;margin-top:18px;font-size:13.5px;color:var(--muted);text-align:center}
.emg-radio{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:12px}
.emg-radio .erl{font-weight:700;font-size:14px;color:var(--navy)}
.radio-live{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:13px;margin-bottom:14px}   /* 2026-09-18 W3.1-25: was border-radius:14px */
.radio-live .rl-top{display:flex;align-items:center;gap:8px}
.radio-live .rl-dot{width:9px;height:9px;border-radius:50%;background:var(--red);box-shadow:0 0 0 0 rgba(178,58,46,.5);animation:rlpulse 2s infinite}
@media (prefers-reduced-motion:reduce){.radio-live .rl-dot{animation:none}}
@keyframes rlpulse{0%{box-shadow:0 0 0 0 rgba(178,58,46,.5)}70%{box-shadow:0 0 0 8px rgba(178,58,46,0)}100%{box-shadow:0 0 0 0 rgba(178,58,46,0)}}
.radio-live .rl-name{font-weight:700;font-size:15px;color:var(--navy)}
.radio-live .rl-note{font-size:13px;color:var(--muted);margin-top:4px}
.res{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px;margin-bottom:10px}   /* 2026-09-18 W3.1-25: was border-radius:14px */
.res h4{font-size:15.5px;color:var(--navy);margin-bottom:2px}
.res .openpill{display:inline-block;font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:3px 8px;border-radius:999px;margin-inline-start:6px;vertical-align:middle}
.res .open{background:var(--green-soft);color:var(--green)}
.res .always{background:var(--sky-soft);color:var(--sky)}
.res .meta{font-size:13px;color:var(--muted);margin-top:4px;line-height:1.45}
.res .who{font-size:13px;margin-top:6px;line-height:1.45}
.res .acts{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.res .acts a{flex:1 1 72px;min-width:0;overflow-wrap:break-word;text-align:center;background:var(--surface);border:1.5px solid var(--navy);color:var(--navy);border-radius:var(--radius);padding:8px 4px;font-weight:700;font-size:12px;text-decoration:none;font-family:var(--font-label);text-transform:uppercase;letter-spacing:.04em;display:inline-flex;align-items:center;justify-content:center;min-height:44px}   /* 2026-09-18 W3.1-25: was border-radius:9px */
.fh-bridge{background:var(--gold-soft);border:1px solid var(--gold);border-radius:var(--radius);padding:13px 14px;margin-top:12px;font-size:14px;line-height:1.45}   /* 2026-09-18 W3.1-25: was border-radius:12px */
/* settings */
.set-row{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--border)}
.set-row:last-child{border-bottom:none}
.set-row>span{font-weight:600;font-size:14.5px}
/* BRAND AT SOURCE 2026-09-18, DARK. Ink #0D2018 is the ground, Deep Navy #102F3D the card, cream the text,
   gold the links. --green, --green-soft, --red, --red-soft unchanged (crisis). Ratios: cream on ink 15.73,
   cream on navy 13.01, muted #C9C3B4 on navy 7.99 / on ink 9.66 / on #1A3A4A 6.84, gold on ink 6.47,
   gold on navy 5.35, --gold-ink #E2B85A on #1A3A4A 6.43 / on navy 7.51 (the plan's #102F3D would be 1.17
   on the gold-soft panel it sits on, so it is not used). --navy-deep stays Deep Navy in dark because it
   is TEXT on the gold button and tag (5.35); Burgundy there would be 3.71. Superseded, kept for the undo:
   --ink:#e8eaed;--muted:#9aa0a6;--canvas:#16181b;--surface:#24262a;--border:#3a3d42;--sky-soft:#21354e;
   --gold-soft:#33301f;--navy:#7da7d9;--sky:#7da7d9;--gold-ink:#e0b25a; background:#16181b */
.phone.dark{--ink:#FAF6EC;--muted:#C9C3B4;--canvas:#0D2018;--surface:#102F3D;--border:#2A4A5C;--sky-soft:#1A3A4A;--gold-soft:#1A3A4A;--green-soft:#1d3326;--red-soft:#3a2420;--navy:#C49A2A;--sky:#C49A2A;--red:#e0897b;--gold-ink:#E2B85A;--navy-deep:#102F3D;--gold:#C49A2A;--bw-cream-deep:#1A3A4A;--bw-ink-muted:#C9C3B4;--bw-rule:#2A4A5C;--bw-gold-ink:#C49A2A;--card-alt:#102F3D;--card-alt-border:#2A4A5C}
.phone.dark{background:#0D2018}
.phone.dark .civic-link:hover{color:var(--navy)}   /* 2026-09-18: hover read --navy-deep, which is card-navy in dark (1.17 on ink). Gold 6.47. */
/* ================================================================================================
   DESIGNSYSTEM 2026-09-04 [A2-navy-fill-token]. --navy-fill: NAVY USED AS A FILL UNDER WHITE TEXT.
   --navy above is lightened in dark mode (#0B4FB0 -> #7DA7D9) because the same token is also the
   link/heading TEXT colour, and dark text has to lighten. Anything that uses navy as a BACKGROUND
   with white on top therefore inverts into a light fill with white text: measured 2.5:1 on
   .skip-link, .cl-callbtn and .inst-set-btn. The existing repair for that is the hand-written
   selector list two lines below, and three components added after it was written were never added
   to it. A token cannot be forgotten the way a selector list can. Light and dark values are the
   two navies the app already ships; no new hex.
   Undo: delete these two declarations; nothing breaks, the fills fall back to var(--navy).
   ================================================================================================ */
/* BRAND AT SOURCE 2026-09-18: the fill is Burgundy in both themes; cream on it is 9.02. Was #0B4FB0 / #2C4A6E. */
:root{--navy-fill:#7A2636}
.phone.dark{--navy-fill:#7A2636}
/* Dark mode: --navy/--sky/--red are lightened above so navy/sky/red TEXT is readable on dark. But the same tokens fill some BACKGROUNDS (search bar, GO, active nav/segment, emergency banner) which carry white text; pin those back to the deep brand colors so white-on-fill stays high-contrast. Added 2026-06-22 after a dark-mode render found navy titles/buttons near-invisible. */
/* 2026-09-18: was background:#2C4A6E;border-color:#2C4A6E;color:#fff. Burgundy with cream, 9.02. */
.phone.dark .topbar,.phone.dark .res-go,.phone.dark .res-call,.phone.dark .seg-btn.on,.phone.dark .btn.primary,.phone.dark .fbtn.active{background:#7A2636;border-color:#7A2636;color:#FAF6EC}
.phone.dark .btn.gold{color:#102F3D}   /* 2026-09-18: Deep Navy on Gold 5.35 (button text is 14px) */
/* 2026-09-18: was .ic background:#3f6fa6 and color:#9cc2ec. Ink well under the gold icon; gold label on navy 5.35. */
.phone.dark .nbtn.active .ic{background:#0D2018}
.phone.dark .nbtn.active{color:#C49A2A}
/* RETIRED AT SOURCE 2026-09-02, NAV COPY. `[RYAN 2026-09-02]` "Open the wall files up. Go fix
   things at the source and delete the patches that exist only to work around them" + "Just copy
   the whole thing as closely as you can" (agingwellinc.org's bottom nav). Their nav gives NO tab
   any special treatment, and `[RYAN 2026-08-13]` "Selected is red, Help is like the others" says
   the same thing in his own words. These eight declarations singled Help out: a red drop-shadow
   glow under the heart, a red label, a red active colour, a pink pill behind the icon, and four
   dark-mode twins of the same. Every one of them was ALREADY being overridden, invisibly, by
   nav_rebuild_2026-08-30.css's higher-specificity `.ic svg{fill:none}` and `.lb{color:inherit
   !important}` -- so they were dead weight generating override rules elsewhere, which is exactly
   the kind of patch he told us to remove. Kept on disk, commented, never deleted. Undo: uncomment
   these eight lines. Backup: app.css.BAK_NAVCOPY_20260902.
.phone.dark .nbtn-help.active .ic{background:#4a2c27}
.phone.dark .nbtn-help svg{fill:#ff7d70}
.phone.dark .nbtn-help .lb{color:#f59384}
.nbtn-help svg{filter:drop-shadow(0 1px 2px rgba(214,40,40,.3))}
.nbtn-help .lb{color:#d62828}
.nbtn-help.active{color:#d62828}
.nbtn-help.active .ic{background:#fdeaea}
   ---- END RETIRED 2026-09-02 ---- */
.phone.dark .emg-banner,.phone.dark .emg-call{background:#A34A3A;border-color:#A34A3A;color:#fff}
/* ===== DARK-MODE CONTRAST PASS (2026-06-25, Ryan: "need the dark mode more readable") =====
   Mirrors the existing navy fix: lighten brand tokens used as TEXT; pin brand colors used as
   white-text BACKGROUNDS back to deep shades; darken the few hardcoded-light surfaces that hid
   light text. Goal: every text/background pair >= WCAG AA. Reversible: delete this block. */
.phone.dark{--green:#69bd86}
.phone.dark .findnow,.phone.dark .fh-cat.active,.phone.dark .res-211{background:#2F6B45;color:#fff}
.phone.dark .fh-cat.active{border-color:#2F6B45}
.phone.dark .great{background:var(--bw-navy);border:1px solid var(--border)}   /* 2026-09-18: was the #2F6B45 to #3f8a5c gradient */
.phone.dark .res-211 a{color:#2F6B45}
.phone.dark .help-cta-t{color:#E2B85A}   /* 2026-09-18: was #e6b85e; light gold on navy 7.51 */
.phone.dark .chip.family{color:#102F3D}   /* 2026-09-18: was #e0b25a; chips are gold badges now, navy text 5.35 */
.phone.dark .featured .tag{color:#102F3D}   /* 2026-09-18: was #1f3550; Navy on Gold 5.35 */
.phone.dark .chip.seniors{background:#C49A2A;color:#102F3D}   /* 2026-09-18: was #332b45 / #cdb8f0 (purple) */
.phone.dark .res-suggest{background:var(--surface);border-color:var(--muted);box-shadow:0 6px 18px rgba(0,0,0,.5)}   /* 2026-09-18: was #24262a / #3a3d42 */
.phone.dark .res-suggest li.crisis-sug{background:#3a2420}
.phone.dark .res-suggest li.crisis-sug .cs-lbl{color:#f0a08e}
.phone.dark .res-suggest li.city-sug{background:var(--sky-soft)}   /* 2026-09-18 W3.2: was #1f2d3d */
.phone.dark .res-suggest li.help-sug{background:#1d3326}
.phone.dark .callbtn{color:#A34A3A}
.phone.dark .emg-banner .emg-tag{color:#A34A3A}
.phone.dark .res-map,.phone.dark .res-web,.phone.dark .res-read,.phone.dark .res-mail,.phone.dark .res-emaillink,.phone.dark .emg-map{background:#2a3a4d;color:#9cc2ec;border-color:#3a3d42!important}
/* AUTO-DARK (2026-07-02, a11y): a phone whose OS is in dark mode opens the app dark, with NO new colors -
   it re-applies the SAME dark theme the .dark class applies (tokens from .phone.dark above + every pin/fix).
   The manual Settings toggle OVERRIDES: tapping Light or Dark adds .appearance-set to .phone (setAppearance),
   and the boot restore adds it too, so :not(.appearance-set) means "user has not chosen". A user who picks
   Light stays light; a user who picks Dark gets the identical .dark theme. Reversible: delete this @media block.

   CONFIRMED DEAD, ONETHEME 2026-09-07: index.html's `.phone` div (7.00, 2026-08-07, "RESTORED") has
   carried `class="phone appearance-set ..."` baked into the STATIC markup ever since, so every `.phone`
   on this app has `.appearance-set` from before any script runs, on every load, for every visitor.
   `:not(.appearance-set)` below therefore NEVER MATCHES, for anyone, ever - measured by reading the
   served HTML directly, live, 2026-09-07 (`https://belovedwoon2.netlify.app` and the WALL source agree).
   This is not a bug: it is what stops the incomplete auto-dark twin set (133 `.dark` rules vs 67 twins
   here, per `darkdefault_2026-08-27.js`'s own header count) from ever double-applying alongside the JS-
   driven `.dark` class that `index.html`'s first-paint script and `darkdefault_2026-08-27.js` now own.
   ONE OWNER FOR THE THEME is `.dark`, added/removed by script only, per NEVER_OVERWRITE/ONETHEME_2026-09-07.md.
   Kept, not deleted (L5): a future index.html that drops the static class would silently reawaken this
   block, so leave it inert-and-documented rather than pretend it does not exist. Undo/remove only together
   with removing the static class in index.html:530, never one without the other. */
@media (prefers-color-scheme: dark){
  /* BRAND AT SOURCE 2026-09-18: this block is DEAD (see above) but its blues are mapped to the same values
     as .phone.dark so a future un-deadening cannot resurrect blue. Greens, reds and every crisis line below
     are byte-identical. Was: --ink:#e8eaed;--muted:#9aa0a6;--canvas:#16181b;--surface:#24262a;--border:#3a3d42;
     --sky-soft:#21354e;--gold-soft:#33301f;--navy:#7da7d9;--sky:#7da7d9;--gold-ink:#e0b25a;background:#16181b */
  .phone:not(.appearance-set){--ink:#FAF6EC;--muted:#C9C3B4;--canvas:#0D2018;--surface:#102F3D;--border:#2A4A5C;--sky-soft:#1A3A4A;--gold-soft:#1A3A4A;--green-soft:#1d3326;--red-soft:#3a2420;--navy:#C49A2A;--sky:#C49A2A;--red:#e0897b;--gold-ink:#E2B85A;--green:#69bd86;--navy-deep:#102F3D;--gold:#C49A2A;--bw-cream-deep:#1A3A4A;--bw-ink-muted:#C9C3B4;--bw-rule:#2A4A5C;--bw-gold-ink:#C49A2A;--card-alt:#102F3D;--card-alt-border:#2A4A5C;--navy-fill:#7A2636;background:#0D2018}
  .phone:not(.appearance-set) .topbar,.phone:not(.appearance-set) .res-go,.phone:not(.appearance-set) .res-call,.phone:not(.appearance-set) .seg-btn.on,.phone:not(.appearance-set) .btn.primary,.phone:not(.appearance-set) .fbtn.active{background:#7A2636;border-color:#7A2636;color:#FAF6EC}
  .phone:not(.appearance-set) .nbtn.active .ic{background:#0D2018}
  .phone:not(.appearance-set) .nbtn.active{color:#C49A2A}
  /* RETIRED AT SOURCE 2026-09-02, NAV COPY -- the system-dark twins of the three Help-only rules
     retired above at app.css:399. Same reason, same undo.
  .phone:not(.appearance-set) .nbtn-help.active .ic{background:#4a2c27}
  .phone:not(.appearance-set) .nbtn-help svg{fill:#ff7d70}
  .phone:not(.appearance-set) .nbtn-help .lb{color:#f59384}
     ---- END RETIRED 2026-09-02 ---- */
  .phone:not(.appearance-set) .emg-banner,.phone:not(.appearance-set) .emg-call{background:#A34A3A;border-color:#A34A3A;color:#fff}
  .phone:not(.appearance-set) .findnow,.phone:not(.appearance-set) .fh-cat.active,.phone:not(.appearance-set) .res-211{background:#2F6B45;color:#fff}
  .phone:not(.appearance-set) .fh-cat.active{border-color:#2F6B45}
  .phone:not(.appearance-set) .great{background:var(--bw-navy);border:1px solid var(--border)}   /* 2026-09-18: was the #2F6B45 to #3f8a5c gradient */
  .phone:not(.appearance-set) .res-211 a{color:#2F6B45}
  .phone:not(.appearance-set) .help-cta-t{color:#E2B85A}   /* 2026-09-18: was #e6b85e */
  .phone:not(.appearance-set) .chip.family{color:#102F3D}   /* 2026-09-18: was #e0b25a; navy on the gold badge 5.35 */
  .phone:not(.appearance-set) .featured .tag{color:#102F3D}   /* 2026-09-18: was #1f3550; Navy on Gold 5.35 */
  .phone:not(.appearance-set) .chip.seniors{background:#C49A2A;color:#102F3D}   /* 2026-09-18: was #332b45 / #cdb8f0 (purple); gold badge, navy text 5.35 */
  .phone:not(.appearance-set) .res-suggest{background:#24262a;border-color:#3a3d42;box-shadow:0 6px 18px rgba(0,0,0,.5)}
  .phone:not(.appearance-set) .res-suggest li.crisis-sug{background:#3a2420}
  .phone:not(.appearance-set) .res-suggest li.crisis-sug .cs-lbl{color:#f0a08e}
  .phone:not(.appearance-set) .res-suggest li.city-sug{background:#1A3A4A}   /* 2026-09-18: was #1f2d3d (blue-black); cream-deep dark */
  .phone:not(.appearance-set) .res-suggest li.help-sug{background:#1d3326}
  .phone:not(.appearance-set) .callbtn{color:#A34A3A}
  .phone:not(.appearance-set) .emg-banner .emg-tag{color:#A34A3A}
  .phone:not(.appearance-set) .res-map,.phone:not(.appearance-set) .res-web,.phone:not(.appearance-set) .res-read,.phone:not(.appearance-set) .res-mail,.phone:not(.appearance-set) .res-emaillink,.phone:not(.appearance-set) .emg-map{background:#2a3a4d;color:#9cc2ec;border-color:#3a3d42!important}
  .phone:not(.appearance-set) .res-crisis .copt{border-color:#A34A3A}
  .phone:not(.appearance-set) .res-crisis .copt .clbl{color:#A34A3A}
  /* A11Y 2026-09-05: the auto-dark twin of the 1.4.3 fix near line 268. Someone on a dark-mode phone
     who has never opened Settings never gets .appearance-set, so she takes THIS branch, and without
     these two lines she hits the same 2.64:1 crisis panel. Same colour, same reason. */
  .phone:not(.appearance-set) .res-crisis .copt .csub,
  .phone:not(.appearance-set) .res-crisis-acc .copt .cbrand{color:#595c61}
  .phone:not(.appearance-set) .res-crisis .cpill.call{background:#A34A3A;color:#fff;border-color:#A34A3A}
  .phone:not(.appearance-set) .res-crisis .cpill.alt,.phone:not(.appearance-set) .res-crisis .cpill.alt2{color:#A34A3A;border-color:#A34A3A}
  .phone:not(.appearance-set) .res-cat .res-item{background:#102F3D}   /* 2026-09-18: was #1f2d3d */
  .phone:not(.appearance-set) .res-cat{background:#0D2018}   /* 2026-09-18: was #1f2430 */
  .phone:not(.appearance-set) .res-cat>h3{color:#FAF6EC}   /* 2026-09-18: was #dfe8f2; 15.73 on ink */
  .phone:not(.appearance-set) .res-clearbtn{background:#7A2636;border-color:#7A2636;color:#FAF6EC}   /* 2026-09-18: was #2C4A6E; 9.02 */
}
/* ==================================================================================================
 * TEXT SIZE. WCAG 2.1 SC 1.4.4 Resize Text, Level AA, requires 200 percent.
 * https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html
 *
 * WHAT WAS HERE, 2026-08-22: `Large` was zoom:1.12 and `Extra large` was zoom:1.26. That is 112
 * and 126 percent. A control that is called Extra large, on an app built for older adults, reached
 * less than a quarter of the range the standard asks for. Measured in a real Chromium render, not
 * read off the source: the fixed crisis bar was 47px tall at Normal, 53px at Large and 59px at
 * Extra large on a 390px viewport, ratios 1.128 and 1.255.
 *
 * IT IS NOW 100 / 150 / 200. Every step was rendered at 320, 360 and 390 CSS px across all six
 * views before it was written here. `zoom` stays the primitive because this stylesheet is
 * px-based end to end; a rem conversion is a rewrite of all 1160 lines and is not the smallest
 * change that reaches the number.
 *
 * RAISING THE SCALE BREAKS LAYOUTS, WHICH IS WHY THE GUARDS BELOW EXIST. With the values raised
 * and nothing else changed, a 320px render at 200 percent produced 279px of horizontal scroll on
 * Events, the five nav labels truncated to "HO" "EV" "HEL" "INF" "NE", and "Crisis/Mental Health"
 * broken into "Menta l Healt h" ON THE CRISIS FLOOR. That is F69, the named failure of this very
 * criterion. https://www.w3.org/WAI/WCAG21/Techniques/failures/F69
 * With the guards: 0 findings in 36 cells (3 widths x 2 steps x 6 views). Falsifier:
 * _TOOLS/falsify_textsize.mjs, which goes red when any one guard is removed.
 *
 * THE GUARDS ARE CLASS-SCOPED, NEVER @media-SCOPED, AND THAT IS LOAD-BEARING. A media query reads
 * the REAL viewport. zoom shrinks the EFFECTIVE one. At 320px and 200 percent the layout has 160
 * effective px to work with while `@media (max-width:374px)` still believes it has 320. Every
 * `@media` rule in this file is now firing with half the room it was written for. That is the
 * single root cause of most of what is repaired below, and it cannot be fixed with another
 * media query.
 * ============================================================================================== */
.phone.bigtext{zoom:1.5}
.phone.text-large{zoom:1.5}
.phone.text-xlarge{zoom:2}

/* --- reflow guards for the two big steps. SC 1.4.10 Reflow, no 2-D scrolling at 320 CSS px.
       https://www.w3.org/WAI/WCAG22/Understanding/reflow.html --- */
/* ARROWWRAP 2026-09-04: `anywhere` became `break-word`. The two differ in ONE thing that matters here:
   `anywhere` tells layout that every word's min-content width is one letter, so any auto-sized flex or
   grid track may legally shrink to a single character and paint a letter tower; `break-word` keeps a
   word's min-content at the word, and still breaks it when it cannot fit a line alone, so nothing can
   overflow that could not overflow before. MEASURED before the change (towers_A.json) and after
   (towers_B*.json), real Chromium, shipped fonts, 320x568 and 360x640, 150 and 200 percent, en km lo
   ru el ar bn, Home Events Help Info News Settings: horizontal overflow 0px in every cell both before
   and after. hdrwrap_2026-09-01.css and the five `.res-211 a *{overflow-wrap:break-word}` rules below
   were each written to undo `anywhere` on one element; they are now redundant, and harmless, and kept.
   The letter towers themselves (Help result cards, 6,066px at 320/150) were NOT this rule: removing
   it AND the min-width reset below in a scratch copy left them identical (towers_C.json). Their cause
   was the card's own flex row, fixed at source in card_2026-08-27.css the same day. */
.phone.text-large,.phone.text-xlarge{overflow-wrap:break-word}
.phone.text-large *,.phone.text-xlarge *{min-width:0}
/* ==============================================================================================
   2026-09-24, BW SWEEP. `break-word` ON THE LINE ABOVE IS NOT A SIZING RULE, AND THAT IS THE WHOLE
   REASON THE HELP CARDS STILL OVERFLOWED AT THE TWO ZOOMED STEPS.
   ----------------------------------------------------------------------------------------------
   The note above is right about what it measured and right to have chosen `break-word` over
   `anywhere` GLOBALLY. What it could not have known is the half of the spec that decides this:
   `overflow-wrap:break-word` takes NO part in intrinsic sizing. When a flex or grid algorithm asks
   a box how narrow it can be, a `break-word` box still answers with its longest unbroken word, and
   only breaks that word afterwards, if the width it was finally handed is too small. So on a row
   that can shrink, the box never gets offered the small width in the first place. `anywhere` is
   the one value that answers "one character", which is what lets the row hand it the width it
   actually has. Two rules across this app carried `break-word` and were read as fixes for years
   without ever changing a pixel: this one and `.res-item.oc-card .res-actions a` in
   org_card_2026-08-23.css.
   THE SCOPE IS DELIBERATELY NARROW. `anywhere` everywhere would be a blunt instrument, and the
   note above explains exactly why the global rule is `break-word`; that global rule is NOT
   changed. This adds `anywhere` inside the Help RESULT CARDS only, at the two zoomed steps only,
   which is where every remaining measured overflow lived: at 320px Russian Extra Large the whole
   layout viewport is 160 CSS px and a card's content box is 45.
   THE CRISIS PATH IS EXCLUDED BY NAME AND BY MEASUREMENT. `.res-crisis`, `.res-crisis-acc`,
   `.rrh-crisis`, `.res-211`, `.callbtn`, `.cbtn`, `.cpill` and `#res-helpbar-fixed` keep the
   `break-word` they were given, every one of them deliberately, and none of them is a `.res-item`.
   The floor bar's rects and colours were measured before and after this rule and are identical.
   ============================================================================================== */
.phone.text-large .res-item:not(.res-crisis):not(.res-crisis-acc) *,
.phone.text-xlarge .res-item:not(.res-crisis):not(.res-crisis-acc) *{overflow-wrap:anywhere}
/* the 3-up quick grid was the first thing to overflow: +28px of horizontal scroll at 320/126%. */
.phone.text-large .quick-grid{grid-template-columns:1fr 1fr}
.phone.text-xlarge .quick-grid{grid-template-columns:1fr}
.phone.text-large .card,.phone.text-xlarge .card{padding:12px}
/* .event-row is a 3-column flex row (datebox / text / details button). At 200 percent the button
   was pushed to right=599 on a 320px screen. Let the row wrap instead. */
.phone.text-large .event-row,.phone.text-xlarge .event-row{flex-wrap:wrap}
/* ARROWWRAP 2026-09-04: `.phone.text-large .details-btn{margin-inline-start:auto}` retired. The button is no longer a flex sibling of the title (it sits inside .ev-ctl, floated), so pushing it to the row end has nothing to act on. Kept as a comment so the undo path is one line. */
/* JANK_HELP_EVENTS 2026-09-02: SAME BUG AS ABOVE, HIT AT 100% TEXT ON A NARROW PHYSICAL SCREEN, NOT
   ONLY AT 200% ZOOM. MEASURED, REAL CHROMIUM, 320x844, 100% text, Home's event rows
   (crisis_and_layout.mjs / diag_eventrow.mjs, WALL/_HARNESS/_jankhelpevents_2026-09-02/): the row is
   datebox(48px, flex:none) + title/meta column (inline style="flex:1") + .details-btn(~58px) +
   .wrr-flag(44px), 3 gaps of 12px, in a 254px-wide row. With plain flex:1 and no floor on the title
   column's own width, flexbox obeys the CSS to the letter and gives the three flex:none/near-fixed
   siblings their full requested width FIRST, leaving whatever is left over for the only column that
   can shrink -- measured at 64px, narrower than the 58px "Details" button sitting right next to it,
   for a title like "Harris Public Library closed for Labor Day". One line of a ~30-character title
   wrapped word-by-word into a column 64px wide reflows into 8+ short lines; measured row heights were
   588px, 434px, 564px and 506px for the four events on Home at this width.
   THE FIX. Same technique already proven and shipped above for 200% zoom: let the row wrap
   (flex-wrap:wrap). The difference from the zoom rule is what floor the title column gets: the zoom
   rule relies on the app-wide `.phone.text-large *,.phone.text-xlarge *{min-width:0}` reset (line
   527) plus `overflow-wrap:anywhere`, which is right for that case (every element on the page is
   already being asked to shrink at 200%). At 100% text this rule instead gives the title column an
   explicit 150px floor (min-width, plus flex-basis 150px so it does not start from 0 and get shrunk
   straight back down). Below that floor there is provably not enough of the 254px row left for a
   44px+ details button and a 44px wrr-flag to sit on the same line without going under 44px
   themselves (254 - 48 datebox - 12 gap - 150 title - 12 gap = 32px, less than either control alone),
   so the wrap the browser was already capable of doing finally has a reason to fire: datebox+title
   share the first line, details-btn+wrr-flag drop to a second line beneath, left-aligned under the
   datebox (a small `margin-top` only, no inline-start push -- measured cleaner than pushing the pair
   to the right edge, which pulls them away from the row they belong to). RE-MEASURED after this rule:
   title column 194px (up from 64px, and now wider than the 58px button, not narrower), row heights
   301px/313px/338px/300px (down from 588/434/564/506 -- roughly half). NEVER makes a control smaller:
   datebox, .details-btn and .wrr-flag keep their exact current width and height; only which row they
   land on changes, and only under 360px, where the zoom rule above does not otherwise apply (that one
   is gated on `.phone.text-large`/`.phone.text-xlarge`, i.e. 150%/200% text, not on physical width).
   390px and wider are untouched: measured title column there was already 134px, judged adequate.
   Scope is `#home-events .event-row` AND `#event-list .event-row` (eventRow() in app.js is the one
   template both views share, app.js:366-437) -- this rule targets `.event-row` unscoped so both pick
   it up identically, same as the zoom rule one line up already does.
   BACKUP: app.css.bak_2026-09-02_eventrow320fix (full file, before this rule existed).
   UNDO: delete this rule block. Nothing else on the wall references it. */
@media (max-width:359px){
  .event-row{flex-wrap:wrap}
  .event-row > div[style*="flex:1"]{flex:1 1 150px;min-width:150px}
  /* ARROWWRAP 2026-09-04: `.event-row > .details-btn,.event-row > .wrr-flag{margin-top:6px}` retired, same reason as the text-large line above: neither is a direct child of the row any more. The flex-wrap and the 150px floor above stay: they still protect the title column beside the datebox at 320. */
}
/* the language and text-size segmented controls: equal-width cells cannot hold a word at 200%. */
.phone.text-large .seg,.phone.text-xlarge .seg{flex-wrap:wrap}
.phone.text-large .seg-btn,.phone.text-xlarge .seg-btn{flex:1 1 100%;border-inline-end:none;border-bottom:1.5px solid var(--navy)}
.phone.text-large .seg-btn:last-child,.phone.text-xlarge .seg-btn:last-child{border-bottom:none}
/* THE LANGUAGE PICKER IS NOT A FLEX ROW AND THAT IS WHY IT NEEDED A SEPARATE FIX. app.css:829
   makes #lang-seg a two-column GRID, so every flex rule above it is inert there and both cells are
   locked to 1fr whatever the name inside them is. Measured at 390px and 200 percent: each cell was
   62px and "Portugues" needed 67, so the app rendered a language picker with the language names
   sliced through the middle of the word. That is the ONE string a person must be able to read,
   and if they cannot read it they cannot read the rest of the app to work out what happened.
   Found by the falsifier's mid-word check, not by eye. One column at the big steps. */
.phone.text-large #lang-seg,.phone.text-xlarge #lang-seg{grid-template-columns:1fr}
.phone.text-large #lang-seg .seg-btn,.phone.text-xlarge #lang-seg .seg-btn{border-bottom:1.5px solid var(--navy)}
.phone.text-large .dirs,.phone.text-xlarge .dirs{flex-wrap:wrap}
/* the search box is the thing Camela came for. Side by side, the input collapsed to a ~70px stub
   next to a 56px button. Stack them: full-width field, full-width button under it. */
.phone.text-large .res-search-row,.phone.text-xlarge .res-search-row{flex-wrap:wrap}
.phone.text-large .res-search-row .res-combo,.phone.text-xlarge .res-search-row .res-combo{flex:1 1 100%;min-width:100%}
.phone.text-large .res-search-row .res-go,.phone.text-xlarge .res-search-row .res-go{flex:1 1 100%;width:100%;margin-top:8px}
/* ------------------------------------------------------------------------------------------------
   HOMEUX 2026-10-01. ON HOME ONLY, THE WORDLESS BLOCK ABOVE STANDS DOWN AT THE TWO BIG TEXT STEPS.
   `[RYAN 2026-10-01]` "Just because the flowery language is in the brand kit doesnt mean it fits
   this app be smart about it."

   WHAT IT IS ON HOME. renderHomeHelp() in app.js builds `.res-go` as
   `<span class="res-go" aria-hidden="true">` with an SVG magnifier inside and NO TEXT NODE AT ALL.
   On Help, Events, Info and News the same class is a real `<button>` that submits a real input.
   Same class, two different things, and this rule names only the Home one.

   WHAT IT COSTS, MEASURED IN A REAL RENDER TODAY AT 320 AND 390, EXTRA LARGE, NOT ESTIMATED: the
   rule immediately above gives it `flex:1 1 100%` at the two big steps, so it stops being a 48px
   square beside the field and becomes a full-width slab on its own row: 256x72 at 320 and 326x72 at
   390, plus its own 8px margin-top. EIGHTY PIXELS OF AN 844px SCREEN, carrying one icon that is
   already `aria-hidden`, attached to a control that is already a labelled button, sitting directly
   above the lead story. WHOUSES 2026-10-01 looked at it and wrote it down as "a large empty maroon
   button with a magnifier icon and no words at all". It is the same icon that is already drawn
   inside `.res-search-head`, one element up.

   WHY THIS IS THE RIGHT 80 PIXELS TO TAKE, AND WHY IT SERVES A RULING RATHER THAN FIGHTING ONE.
   `[RYAN 2026-09-29]` "the main story should show fully on every mode", about Candidate
   Connections. RENDER 2026-09-29 answered it by taking the line-clamp OFF the Home news lead
   (app.css, the `.phone.nf-grid #home-updates ... .nf-row-what` rule). Measured today, that lead
   paragraph is 753px tall at 320 Extra Large against 747px of usable screen, so the resident's
   first screen cannot complete a row of answer while his ruling holds, AND IT SHOULD HOLD. The one
   thing that still helps her is to hand the furniture's pixels to the story he asked to show
   fully. These 80 are the only ones above it that carry no word and no function.

   NOTHING IS LOST, CHECKED NOT ASSUMED. Zero accessible content: the element is `aria-hidden="true"`
   so it was never in the accessibility tree. Zero function: the whole `.res-search` block is one
   `role="button"` with `onclick="homeHelpGo('')"` and a matching `onkeydown`, and the span has no
   handler of its own, so the tap target is the entire control and is unchanged. Zero at Normal:
   this rule is gated on `.text-large` and `.text-xlarge` only, so the 48px square beside the field
   at Normal is exactly as it was.

   THIS IS NOT A NEW PATTERN IN THIS FILE. `app.css:2734` already ships
   `html[data-crisis-slim="1"] .phone .res-search-row > .res-go{ display:none }`, which hides this
   same element outright, on every surface, for screen space. This does less than that rule does and
   on one surface only.

   DISPLAY:NONE AND NOT VISIBILITY OR WIDTH, deliberately, because the point is to reclaim the row:
   `visibility:hidden` would leave all 80px behind. Scoped by `#home-help`, which is Home's
   container in index.html line 1346, so the Help page's real submit button is untouched; the
   crisis path is not reachable from this rule.
   Undo, one line: delete the rule below.
   Pre-change file: NEVER_OVERWRITE_BACKUPS/homeux_2026-10-01/app.css.PRE_HOMEUX
   ------------------------------------------------------------------------------------------------ */
.phone.text-large #home-help .res-search-row > .res-go,
.phone.text-xlarge #home-help .res-search-row > .res-go{display:none}
/* NAV LABELS. brand.css:28 pins `.nbtn .lb` to position:absolute + white-space:nowrap, so it can
   never wrap and never widens its button: at 320px it was ALREADY truncating to "EV" at the
   shipped 126 percent. Put it back in normal flow at the big steps only, where it wraps to two
   lines and stays whole. (0,4,0) beats brand.css's (0,2,0) without !important. */
/* RETIRED AT SOURCE 2026-09-02, NAV COPY. Its own first line says what it was for: "brand.css pins
   `.nbtn .lb` to position:absolute + white-space:nowrap". That pin is retired at its source today,
   and the label is in normal flow at EVERY text size now, not only at the two big steps -- so this
   rule has nothing left to undo. It also fought the copy: `white-space:normal` would wrap a nav
   label to two lines at 150/200 percent, which agingwellinc.org's own nav never does (theirs is
   nowrap + ellipsis, and so is ours). Kept on disk, commented. Undo: uncomment these three lines.
.phone.text-large .nav .nbtn .lb,.phone.text-xlarge .nav .nbtn .lb{
  position:static;transform:none;white-space:normal;overflow-wrap:anywhere;
  letter-spacing:0;max-width:100%;line-height:1.05;padding:1px 3px}
   ---- END RETIRED 2026-09-02 ---- */
.phone.text-large .nav-inner,.phone.text-xlarge .nav-inner{gap:2px;padding-left:4px;padding-right:4px}
.phone.text-xlarge .nbtn .ic{width:30px;height:30px}
.phone.text-xlarge .navico{width:24px;height:24px}
/* THE FIXED BOTTOM DOCK IS CAPPED AT 150 PERCENT AND THIS IS THE ONE DELIBERATE SHORTFALL.
   #bw-dock is the one fixed box holding the crisis floor and the nav. Scaled the full 200 percent
   it measured 359px of a 780px viewport, 46 percent of the screen, and the 911/988/Text-988 row
   overflowed a 320px screen by 28px. A fixed box cannot be scrolled to. Nested zoom multiplies,
   so .75 x 2 = 1.5. At 1.5 the floor was clean at 320, 360 and 390, the three buttons stay whole
   and the dock is 226px, 29 percent. NOT ONE CRISIS DECLARATION IS EDITED BY THIS: the rule names
   the container, and app.css's crisis lines hash byte-identical before and after. */
.phone.text-xlarge #bw-dock{zoom:.75}
/* The @media (max-width:374px) compaction near app.css:989 lays the 211 call button out as a
   non-shrinking flex item. It fires on a 320px phone believing it has 320px; at 200 percent it
   has 160. Generic, names no component and no crisis class: let a link that is a flex item inside
   a view shrink. Without it, Help scrolled sideways 40px at 320/200%. */
.phone.text-large .view a,.phone.text-xlarge .view a{flex-shrink:1}
.set-intro{color:var(--muted);font-size:14px;margin-bottom:14px}
/* 2026-09-18 W3.1-17: Settings card eyebrows take the same opener, no number. Was font-size:13px;letter-spacing:.06em;color:var(--muted). */
.set-h{font-family:var(--font-label);font-size:12px;line-height:1.2;font-weight:700;text-transform:uppercase;letter-spacing:var(--track-label);color:var(--bw-gold-ink);margin:0 0 12px;padding:11px 0 0;background:linear-gradient(var(--bw-gold),var(--bw-gold)) no-repeat 0 0 / 20px 1px}
.set-h:lang(ar),.set-h:lang(km),.set-h:lang(lo),.set-h:lang(bn){letter-spacing:0}
html[dir="rtl"] .set-h{background-position:100% 0}
.phone.dark .set-h{color:var(--bw-gold)}
.set-block{margin-bottom:18px}
.set-block:last-child{margin-bottom:0}
.set-label{font-weight:700;font-size:15px;margin-bottom:8px}
/* SERIFSMALL 2026-09-23. FINAL_2026-09-23 section 8 item 6, and the last line of its Settings
   paragraph: "the spec asks for, Cormorant italic at 16 to 18px in #545F57. That is the one place
   the guide allows the serif small and the app declines it." BRANDSPEC's Settings entry, verbatim:
   "The helper lines ('Light, you chose this...') in serif italic 16 px #545F57; this is the one
   place the serif is used small, and it is ornament not UI, which the guide allows."
   13px -> 16px also lifts these lines three pixels clear of the 12px floor, and --muted already
   computes to #545F57 on cream, so no colour moves. .set-sub goes to the subtitle spec, serif italic
   18px, and keeps Burgundy (9.02 on cream) because it labels the block beneath it.
   font-style:normal for the scripts with no italic: a browser SYNTHESISES a slant for Arabic,
   Khmer, Lao, Bengali, Chinese, Korean and Greek, which is bad typography and hurts legibility, and
   the same :lang guard is already used on .upd-sec and .upd-tag in this file.
   Undo: restore the two one-line rules; they are printed here.
   .set-hint{font-size:13px;color:var(--muted);margin-top:8px;line-height:1.4}
   .set-sub{font-weight:700;font-size:14.5px;color:var(--navy);margin:14px 0 4px} */
.set-hint{font-family:var(--font-serif);font-style:italic;font-size:16px;color:var(--muted);margin-top:8px;line-height:1.45}
.set-sub{font-family:var(--font-serif);font-style:italic;font-weight:500;font-size:18px;color:var(--navy);margin:14px 0 4px;line-height:1.3}
.set-hint:lang(ar),.set-hint:lang(km),.set-hint:lang(lo),.set-hint:lang(bn),.set-hint:lang(zh),.set-hint:lang(ko),.set-hint:lang(el),.set-hint:lang(ru),
.set-sub:lang(ar),.set-sub:lang(km),.set-sub:lang(lo),.set-sub:lang(bn),.set-sub:lang(zh),.set-sub:lang(ko),.set-sub:lang(el),.set-sub:lang(ru){font-style:normal}
.set-body{font-size:14px;color:var(--ink);line-height:1.55;margin-bottom:8px}
.seg{display:flex;border:2px solid var(--navy);border-radius:var(--radius);overflow:hidden}   /* 2026-09-18 W3.1-24: was 1.5px, radius 12px */
.seg-btn{flex:1;border:none;background:var(--surface);color:var(--navy);padding:12px 8px;font-family:var(--font-sans);font-weight:600;font-size:16px;cursor:pointer;min-height:48px;border-inline-end:2px solid var(--navy)}   /* 2026-09-18 W3.1-24: was label face 700 14px, 1.5px divider. Burgundy on white 9.73 */
.seg-btn:last-child{border-inline-end:none}
.seg-btn.on{background:var(--navy-fill);color:var(--bw-cream)}   /* 2026-09-18 W3.1-24: was var(--navy)/#fff; Burgundy fill, cream 9.02 */
/* Language picker scales to MANY languages: its buttons size to their own name and wrap as chips, instead of cramming into equal narrow cells (which stacked CJK names vertically at 8+ languages). Scoped to #lang-seg so the text-size/appearance segments keep equal-width. Added 2026-06-22 after a growth render. */
#lang-seg .seg-btn{flex:0 1 auto}

/* trash day lookup */
.lk-input{width:100%;border:1.5px solid var(--border);border-radius:var(--radius);padding:13px;font-size:16px;font-family:inherit;background:var(--surface);color:var(--ink)}   /* 2026-09-18 W3.1-25: was border-radius:12px */
.lk-input:focus{outline:2px solid var(--sky);border-color:var(--sky)}
.lk-list{margin-top:10px}
.lk-street{display:block;width:100%;text-align:start;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:15px 16px;font-size:18px;font-weight:700;margin-bottom:8px;cursor:pointer;font-family:var(--font-label);color:var(--bw-navy)}   /* 2026-09-18 W3.1-24 (A7 handoff): app.js line 3643 dropped its inline 'Josefin Sans' and var(--green); label face, Deep Navy on white 14.04. Was radius 12px, 16.5px 600, inherit, var(--ink) */
.phone.dark .lk-street{color:var(--ink)}
.lk-ans{text-align:center;padding:6px 0 2px}
.lk-ans .street{font-family:var(--font-label);font-weight:700;font-size:20px;color:var(--navy)}
.lk-day{font-family:var(--font-label);font-weight:700;font-size:30px;color:var(--green);margin:6px 0 2px}
.lk-sub{font-size:14px;color:var(--muted);margin-top:3px}
.backbtn{border:none;background:transparent;color:var(--sky);font-weight:700;font-size:14px;cursor:pointer;font-family:inherit;padding:0 10px 0 0;margin-bottom:10px;min-height:44px;display:inline-flex;align-items:center}

/* filter chips */
/* CHIPLINE 2026-09-06: `[RYAN 2026-09-06]` "I wan the all seniors youth etc buttons on one line...
   consider dropping one?" Shortened evcSeniors -> "55+" (language-neutral, all 16 languages, his pick)
   and evcArts -> "Arts"/"Artes"/"Arti"/"Τέχνες" in the 6 languages with a droppable article; dropped the
   Youth chip at the data source (WS_CATS/app_data_findhelp.js) since EVENTCATS 2026-09-06 confirmed it
   is 4 records, 0 upcoming, always empty - a chip that always shows nothing teaches people the app is
   broken. MEASURED (real canvas, live "Source Sans Pro" 600 13px, all 16 languages, all 6 remaining
   chips): even after both changes the row is 359-485px wide depending on language (worst: Vietnamese
   485, Russian/Greek ~451) - it does NOT fit inside any phone viewport (320-430px) as a single wrapped
   line in every language, so flex-wrap:wrap would still silently drop to 2 rows for es/vi/ru/el/pl/km/lo
   while looking fine in en/ko/zh/ar/ht. Rather than claim "one line" and be wrong in 6 of 16 languages,
   this is a real single-row rail: flex-wrap:nowrap + horizontal scroll. English/Korean/Chinese/Arabic/
   Haitian need no scroll (they fit). Longer languages get one true line reachable by a swipe, never a
   second stacked row. Chips stay flex:none (natural width, no squeezing) and 44px (untouched). Undo:
   NEVER_OVERWRITE_BACKUPS/app.css.PRE_ORPHANFIX_20260906_223148 (pre-dates this block; the block itself
   can simply be deleted to restore flex-wrap:wrap). */
/* GATECLEAR 2026-09-07/08: gap 8px -> 6px. `[RYAN 2026-09-06]` "I want the all seniors youth etc
   buttons on one line, say 55+, Arts to save space, consider dropping one?" - restoring the Youth
   chip (real data now exists, see app_data_findhelp.js) needs the row as tight as it can safely get.
   Measured at 320px (the hard case): the seven chips (All/55+/Youth/Arts/Family/Music/City) total
   ~390px of content before this change vs 288px of visible filterbar width - even the six-chip,
   post-CHIPLINE state measured 368px content, ALSO more than 288px, so this row has needed horizontal
   scroll at 320px regardless of chip count; it was never truly single-screen even with Youth dropped.
   Padding (7px 14px) was NOT reduced: the "All" chip is already only 0.3px above the 44px WCAG/project
   tap-target floor (`.fbtn{min-height:44px}` + its own width), so shrinking its padding further would
   push a real tap target under 44px, which this project measures and treats as a defect elsewhere
   (SHIPPICKS/DOTANDDATE) - not done here for a cosmetic width win. Gap is the only safe lever: it
   never touches an individual chip's own box. This still does not make 7 chips fit at 320px without
   any scroll (saves ~12px of ~150px needed) - `.filters` stays `overflow-x:auto` (pre-existing,
   unchanged), a single never-wrapping row a thumb can swipe, which the "one line" instruction is read
   as meaning here (never two lines) since true zero-scroll fit is not reachable without an unsafe tap
   target or dropping a chip - see GATECLEAR_2026-09-07.md for the exact measurements and the Music
   (1 event) chip named as the only same-shape candidate if he wants zero-scroll enough to drop one. */
.filters{display:flex;flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:thin;gap:6px;padding:2px 2px 8px;margin-bottom:12px}
.fbtn{flex:none;border:1.5px solid var(--navy);background:transparent;border-radius:var(--radius);padding:7px 14px;min-height:44px;   /* 2026-09-18 W3.1-24: was 1px var(--border), var(--surface), 999px. Burgundy outline chip */display:inline-flex;align-items:center;white-space:nowrap;font-size:13px;font-weight:600;cursor:pointer;font-family:var(--font-label);text-transform:uppercase;letter-spacing:var(--track-label);color:var(--ink)}
/* LABELFACE 2026-09-23. FINAL_2026-09-23 section 8 item 11: "Events filter labels are sentence-case
   DM Sans, spec asks condensed caps." BRANDSPEC A2: the LABEL voice is "Used uppercase, with
   generous letter-spacing", and its Events entry asks for "#7A2636 condensed caps 13 px, 44 px
   tall". Size, weight, colour, border, padding and the 44px tap target are all untouched, so no
   contrast pair and no tap target moves; only the face, the case and the tracking change. Barlow
   Condensed is narrower than DM Sans, so caps at 13px take LESS width than the sentence case did.
   Undo: `font-family:inherit` and delete the text-transform and letter-spacing.
   The :lang guard is the app's standing rule, already on .upd-sec, .upd-tag and .evl-more:
   letter-spacing breaks Indic conjuncts and Arabic joining. */
.fbtn:lang(ar),.fbtn:lang(km),.fbtn:lang(lo),.fbtn:lang(bn){letter-spacing:0}
.fbtn.active{background:var(--navy-fill);color:var(--bw-cream);border-color:var(--navy-fill)}   /* 2026-09-18 W3.1-24: was var(--navy)/#fff; Burgundy, cream 9.02 */
.fbtn:focus-visible{outline:3px solid var(--sky);outline-offset:2px}

/* youth */
.prog .head{display:flex;justify-content:space-between;align-items:flex-start;gap:8px}
.prog h3{font-size:17px;color:var(--navy)}
.status{flex:none;font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 8px;border-radius:999px}
.status.open{background:var(--green-soft);color:var(--green)}
.status.wait{background:var(--gold-soft);color:var(--gold-ink)}
.prog .det{font-size:13.5px;color:var(--muted);margin:6px 0 10px;line-height:1.45}
.btn{display:block;width:100%;border:none;border-radius:var(--radius);padding:13px;min-height:48px;font-family:var(--font-sans);font-weight:600;font-size:17px;letter-spacing:0;text-transform:none;cursor:pointer}   /* 2026-09-18 W3.1-24: was radius 12px, label face 700 14px tracked uppercase. BRANDSPEC: buttons in the sans, sentence case */
.btn.primary{background:var(--navy-fill);color:var(--bw-cream)}   /* 2026-09-18 W3.1-24: was var(--navy)/#fff; Burgundy, cream 9.02 */
.btn.gold{background:var(--gold);color:var(--navy-deep)}
.btn.ghost{background:var(--surface);color:var(--navy);border:2px solid var(--navy)}   /* 2026-09-18 W3.1-24: was 1.5px */

/* accordions */
.acc{background:var(--surface);border:0;border-radius:var(--radius);margin-bottom:10px;overflow:hidden}   /* 2026-09-18 W3.1-24: was 1px border, radius 14px */
.phone.dark .acc{border:1px solid var(--border)}
.acc-h{width:100%;display:flex;justify-content:space-between;align-items:center;padding:14px 16px;background:none;border:none;font-family:var(--font-label);font-weight:700;font-size:14.5px;color:var(--navy);cursor:pointer;text-align:start}
.acc-h .arrow{transition:transform .2s;color:var(--muted);display:inline-flex;align-items:center}
.acc.openacc .arrow{transform:rotate(180deg)}
.acc-b{display:none;padding:0 16px 16px;font-size:15.5px;line-height:1.55;color:var(--ink)}
.acc.openacc .acc-b{display:block}
.acc-b p{margin-bottom:8px}
.acc-b .tel{font-weight:700;color:var(--sky)}
/* WORDMARK 2026-09-24. THE LAST SUB-44 CONTROL IN THE APP, AND IT WAS IN A PHONE NUMBER.
   An independent audit found exactly one interactive element left under 44px anywhere: the inline
   phone link inside `<p class="tel">`, which app.js linkifies out of the City Hall info card's own
   body HTML ("<p class='tel'>(401) 762-6400</p>", app_data_seed.js:17, all sixteen languages).
   Measured in a real headless Chromium at 390x844 on the Help view with every drawer force-opened:
   98.00 x 19.00. Nineteen pixels. Every other dial control in the app is 44 or better and several
   are 56.
   THE FIX IS THE APP'S OWN PATTERN, NOT A NEW ONE. tapsize_2026-09-01.css already grows Settings'
   inline "911"/"988" text links with `display:inline-block` plus padding and an exactly equal
   negative margin, so the tap target grows and the line box does not move. Same technique here:
   19 + 12.5 + 12.5 = 44.00 exactly, and the -12.5px margins give every one of those pixels back to
   the paragraph, so the sentence it sits in is byte-for-byte the same shape it was. Measured after:
   the anchor is 110.00 x 44.00, the paragraph's own height and top are unchanged, and
   elementFromPoint at the anchor's centre resolves to the anchor.
   THE HREF IS NOT TOUCHED AND WAS NEVER DIALLED. It is read as a string only. The dial-target set
   is identical before and after across all ten views and all sixteen languages (LAW L4).
   Undo: delete this rule, or restore
   NEVER_OVERWRITE_BACKUPS/wordmark_2026-09-24/app.css.PRE_WORDMARK. */
p.tel>a[href^="tel:"],p.tel>a[href^="sms:"]{
  display:inline-block;min-height:44px;min-width:44px;
  padding:12.5px 6px;margin:-12.5px -6px;
}
/* NOT FIXED TONIGHT, AND THE MEASUREMENT THAT SAYS WHY. The same sweep found Settings' two inline
   crisis links, "911" and "988" in `#view-settings .set-body`, at 27.00 x 49.69 and 35.00 x 49.69.
   They clear 44 in height (tapsize_2026-09-01.css won that on 2026-09-01) and they do not clear it
   in width, because the target is the width of the word.
   GROWING THEM MAKES IT WORSE, AND THAT IS MEASURED, NOT FEARED. The two links sit on CONSECUTIVE
   LINES of one paragraph, 21.68px apart, and the 2026-09-01 rule's own 14px of vertical padding
   already makes each box 49.69px tall. Two 49.69px boxes 21.68px apart OVERLAP BY 28px, and 988 is
   later in the DOM, so it paints on top: elementFromPoint at the CENTRE OF "911" already resolves
   to the 988 anchor today. A tap aimed at 911 lands on 988. Widening 911 from 27 to 44 was tried
   tonight and measured: it puts the two boxes on the same x span as well and does not move that
   result. Reverted, byte-identical to tapsize_2026-09-01.css.PRE_WORDMARK.
   THE ARITHMETIC OF THE REAL FIX, SO THE NEXT SESSION DOES NOT HAVE TO REDERIVE IT. The glyph box
   is 21.69px tall, so a non-overlapping 44px target needs the two lines more than 22.01px apart and
   they are 21.68 apart. It is a third of a pixel of line-height, and every way to buy it changes the
   look of a paragraph of his prose: raise `.set-body` line-height, or put the two numbers on their
   own rows. That is a look decision, not a CSS one, so it is handed to him rather than taken.
   Nothing was shrunk and nothing was moved: this comment is the only change. */

/* modal - OVERLAYFIX 2026-09-08. `[RYAN 2026-09-08]` "SUMMER DAY CAMP POPUP. The popup appears
 * underneath navigation." ROOT CAUSE, confirmed with a real headless-Chromium render
 * (overlay_falsify.mjs, elementFromPoint at the sheet/dock overlap band, not a guess from the
 * numbers alone): this is the ONE generic modal every openSignup()/openSegments()/openContact()-
 * style sheet in app.js shares (8 call sites, incl. the Summer Day Camp program signup and the
 * trash-pickup segment picker), and it was z-index:59 while #bw-dock (bottom_dock_2026-08-15.css,
 * the fixed nav+crisis-floor box) is z-index:60 - ONE stacking context, #bw-dock's own, so the
 * dock painted on top of the bottom ~85px of every sheet this modal ever opens (measured: at
 * 390x844 and 320x568 alike, elementFromPoint at the overlap band resolved to the nav label, not
 * the sheet, RED both sizes before this fix). Raised to 65: still clears #bw-dock (60) the same
 * way every specialised modal already shipped in this app does (.reach-modal-bg 180, .w988-overlay
 * 185, the install banner 186, .reach-op-bg 190, .wrr-overlay 400 - all deliberately above #bw-dock
 * for the same reason, per each file's own header), so this fix is consistent with the app's own
 * established tier, not a new rule. LAW.md L1: this modal is user-invoked and dismissible (tap the
 * scrim, Escape, or the sheet's own Cancel/close control - app.js closeModal()), the same accepted
 * shape as the other above-dock modals already on this wall; it is never shown unprompted and never
 * covers a crisis control on the views it opens from. Verified GREEN after the fix, same harness,
 * same two viewports: elementFromPoint at the overlap band now resolves inside `.sheet`, not `.nav`.
 * Full record: C:\!Brain\LFG4\NEVER_OVERWRITE\OVERLAYS_2026-09-08.md. Undo: put z-index back to 59. */
.overlay{position:fixed;inset:0;background:rgba(20,30,45,.55);z-index:65;display:flex;align-items:flex-end;justify-content:center}
/* SPACE2 site 3 of 3. 88% of the LARGE viewport is more than 88% of the glass, so the bottom sheet
   could stand taller than the screen it slides up on. `88svh` is 88% of what she can see. Same
   progressive-enhancement shape as the two above. Undo: delete the added declaration. */
.sheet{background:var(--surface);width:100%;max-width:430px;border-radius:var(--radius) var(--radius) 0 0;padding:20px 18px 28px;max-height:88vh;max-height:88svh;overflow-y:auto}   /* SHAPE 2026-09-22: was border-radius:20px 20px 0 0. The bottom sheet is a card; only its two top corners are visible and they now match every other card */
.sheet h3{color:var(--navy);font-size:18px;margin-bottom:4px}
.sheet .sub{color:var(--muted);font-size:13.5px;margin-bottom:14px}
.fld{margin-bottom:12px}
.fld label{display:block;font-size:13px;font-weight:700;margin-bottom:5px}
.fld input,.fld textarea{width:100%;border:1.5px solid var(--border);border-radius:var(--radius);padding:11px;font-family:inherit;font-size:15px}   /* 2026-09-18 W3.1-25: was border-radius:10px */
.fld input:focus,.fld textarea:focus{outline:2px solid var(--sky);border-color:var(--sky)}
.confirm{text-align:center;padding:18px 4px}
.confirm .big{font-size:40px}
.confirm h3{margin:8px 0 6px}
.confirm p{color:var(--muted);font-size:14px}
.err{background:var(--red-soft);color:var(--red);border-radius:10px;padding:10px 12px;font-size:13.5px;font-weight:600;margin-bottom:10px}

/* ==============================================================================================
 * CRISIS DOCK, INVIOLABLE TOP STACKING WHILE HELP'S FLOOR BAR IS SHOWING. 2026-09-09.
 * OVERLAYFIX 2026-09-08 (immediately above) named this and left it, on purpose, for its own
 * dedicated session: "two of the app's OWN ALREADY-SHIPPED 'clear the dock' modals -
 * `.wrr-overlay` (report-wrong-info, z-index 400) and `.reach-op-bg` (the Resource-Sheet one-pager
 * builder, z-index 190) - are reachable TODAY from view-community (the Help page) while the crisis
 * floor is visible there, and BOTH were proven live, real Chromium, elementFromPoint AT THE 911
 * LINK'S OWN COORDINATES, to paint over it when opened." That is a direct LAW L1 violation and
 * this is the fix.
 *
 * WHY NOT JUST RAISE #bw-dock's BASE z-index (60) PAST EVERY OVERLAY. That would put the dock -
 * nav included - above every sheet this app opens (Summer Day Camp signup, trash pickup, wrong-
 * report, the Resource Sheet builder, the install banner) on EVERY view, re-breaking the exact
 * "popup appears underneath navigation" bug OVERLAYFIX just fixed by raising `.overlay` to 65
 * instead of lowering the dock. The crisis floor only exists (`#bw-dock[data-help="1"]`, set by
 * bottom_dock_2026-08-15.js, never CSS) when Help is open, so the fix is scoped to exactly that
 * state: #bw-dock only jumps above every shipped overlay WHILE its crisis bar is actually on
 * screen, and falls back to its normal 60 - clearing modals from below, as OVERLAYFIX intended -
 * everywhere else. 401 clears the highest tier already in this file's own stacking comment
 * (`.wrr-overlay` 400); nothing else in app.css, brand.css, brand_palette.css, brand_cats.css or
 * brand_phone.css declares a z-index above 400 (grepped, all five, `#app-error` at 200 and
 * `.skip-link` at 2000 are the only two higher and neither ever paints over the bottom of the
 * screen - `.skip-link` sits at `bottom:100%`, above the very top of the viewport until focused).
 *
 * NOT A LAYOUT CHANGE. This is z-index only: no padding, margin, position or size on #bw-dock or
 * on any overlay moves. An overlay's own backdrop still dims the whole screen behind the dock; the
 * dock's own opaque `background:var(--surface)` (bottom_dock_2026-08-15.css) simply now paints on
 * top of that dimming in the strip it already occupies, so 911/988/text-988 stay visibly solid and
 * tappable through any overlay that opens while Help's crisis bar is showing, exactly as sacred as
 * they are with no overlay open at all.
 *
 * Falsifier: _HARNESS/FALSIFY_CRISISBAR_2026-09-09.mjs, real Chromium, elementFromPoint at the
 * 911/988/text-988 links' own on-screen coordinates with `.wrr-overlay` and `.reach-op-bg` each
 * opened from Help - RED on the pre-fix file (reverted via its own backup), GREEN on this one.
 * Undo: delete this rule. */
#bw-dock[data-help="1"]{ z-index: 401; }

/* nav */
.nav{position:fixed;bottom:0;left:0;right:0;width:100%;background:var(--surface);border-top:1px solid var(--border);z-index:50;touch-action:manipulation}
/* CRISISBAR 2026-09-09: added flex-direction:column. This banner is 3 siblings (the "Found a
   problem" text run, the "Report a problem" link, the "call 911.../988" div) with `display:flex`
   and no `flex-wrap` - a ROW. With no explicit width each sibling shrinks to its own content, and
   at 320px under `.text-xlarge`'s 2x CSS zoom (app.css:699 `.phone.text-large,.phone.text-xlarge
   {overflow-wrap:break-word}` cascades onto every descendant, this banner included) the three
   columns are squeezed narrow enough that break-word starts breaking mid-word, which the found bug
   report described as "one and two letters per line". Column stacks the three as full-width rows
   instead, so text wraps normally at word boundaries with the whole banner width available. No
   position, z-index, color or padding changed; the crisis sentence and both its links are
   untouched. Undo: delete `flex-direction:column`. */
#app-error{position:fixed;top:0;left:0;right:0;z-index:200;background:#B23A2E;color:#fff;padding:11px 16px;font-size:13.5px;display:none;flex-direction:column;align-items:center;justify-content:center;gap:6px;text-align:center}
#app-error a{color:#fff;font-weight:700;text-decoration:underline}
/* 2026-09-18 W3.1-22: editor-only bar; #1b3a5c to Deep Navy on gold 5.35 so no blue is left in the file. Was background:#E8B23A;color:#1b3a5c and a #1b3a5c button. */
#preview-bar{position:fixed;top:0;left:0;right:0;z-index:190;background:var(--gold);color:var(--bw-navy);padding:9px 16px;font-size:13px;font-weight:700;display:none;align-items:center;justify-content:center;gap:10px;text-align:center}
#preview-bar button{background:var(--bw-navy);color:var(--bw-cream);border:none;border-radius:var(--radius);padding:5px 11px;font-weight:700;cursor:pointer;font-family:inherit}
/* FIX 2026-08-30: bare `1fr` tracks default to minmax(auto,1fr) - one long/unbreakable nav label
   widens ALL FIVE columns and pushes <html> into horizontal scroll (measured: fr +189px, bn +157px,
   el +136px @ 320x568; overflowing in >=1 of 3 widths x 2 themes for ar/bn/el/es/fr/ht/it/ko/pl/pt/
   ru/vi - 12 of 16 shipped languages). minmax(0,1fr) lets each track shrink to 0 and defers to the
   wrapping already written into .nbtn/.nbtn .lb (min-width:0, overflow-wrap:break-word) below.
   Per WIRE_lang_BEST.txt Finding 2. Backup: app.css.bak_BESTHELP_20260830_231714. */
.nav-inner{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:4px;padding:8px;max-width:620px;margin:0 auto}
@media (display-mode: standalone),(display-mode: fullscreen),(display-mode: minimal-ui){
  .nav-inner{padding-bottom:calc(8px + env(safe-area-inset-bottom))}
}
.nbtn{border:none;background:transparent;border-radius:var(--radius);padding:8px 2px;display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer;min-height:56px;min-width:0;color:var(--ink);-webkit-tap-highlight-color:transparent;user-select:none}
.nbtn.active{background:transparent;color:var(--navy)}
.nbtn .ic{font-size:19px;line-height:1;width:38px;height:38px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:background .15s}
.nbtn.active .ic{background:var(--navy)}
.nbtn .lb{font-family:var(--font-label);font-weight:700;font-size:12px;letter-spacing:.03em;text-transform:uppercase;text-align:center;line-height:1.15;max-width:100%;overflow-wrap:break-word;word-break:break-word;hyphens:auto}

.note{font-size:13.5px;color:var(--muted);text-align:center;margin-top:14px;line-height:1.55}
.linkish{color:var(--sky);font-weight:700;text-decoration:none}
/* disclaimer/legal paragraph links (911/988 etc.) had no color, so they fell to the UA default blue (#0000ee) = dark-on-dark in dark mode. Theme them. ROOT FIX 2026-06-26. */
[data-i18n-html] a{color:var(--sky);font-weight:600}
ul.clean{list-style:none}
ul.clean li{padding:6px 0;border-bottom:1px solid var(--border);font-size:14px}
ul.clean li:last-child{border:none}
/* bilingual crisis helper: the English shown beside an unreviewed language is a quiet companion, present for safety but not competing with the person's own language */
.cb-en{font-weight:400;opacity:.8;font-size:max(.85em,12px);white-space:normal;overflow-wrap:break-word;direction:ltr;unicode-bidi:isolate}

/* HIGH-CONTRAST MODE (2026-07-02 a11y): an in-app Settings toggle (.high-contrast on .phone) that boosts
   text/background separation and thickens focus outlines, for low-vision users. Reuses the token system:
   ink goes pure-black, muted darkens to a real >=7:1 grey, surfaces pure-white, borders darken, brand text
   deepens. Works in light AND dark (the dark variant pushes the other way). Reversible: delete this block. */
/* BRAND AT SOURCE 2026-09-18, HIGH CONTRAST. --navy/--sky were #12335c (blue); now a darkened Burgundy #5C1C29,
   12.70 on white, white on it 12.70. --gold-ink was #5a3c08; now Deep Navy 14.04 on white. --bw-gold-ink is
   pushed to the same #5C1C29 so no small gold-ish text sits under 7:1 here. --navy-fill follows --navy. Green
   and red values unchanged (crisis). Ink, surface, canvas, border as they were. */
.phone.high-contrast{--ink:#000;--muted:#3a3d42;--surface:#fff;--canvas:#fff;--border:#000;--navy:#5C1C29;--sky:#5C1C29;--green:#1e5232;--red:#8a2f22;--gold-ink:#102F3D;--navy-fill:#5C1C29;--bw-gold-ink:#5C1C29;--bw-burgundy:#5C1C29;--bw-cream:#fff;--bw-cream-deep:#fff;--card-alt:#fff;--card-alt-border:#000;--bw-rule:#000}
.phone.high-contrast .card,.phone.high-contrast .res-item,.phone.high-contrast .res-search,.phone.high-contrast .fh-cat,.phone.high-contrast .fbtn,.phone.high-contrast .acc,.phone.high-contrast .set-row{border-color:#000;border-width:1.5px}
.phone.high-contrast .set-hint,.phone.high-contrast .res-item .ra,.phone.high-contrast .note{color:#3a3d42}
.phone.high-contrast a{text-decoration:underline}
.phone.high-contrast button:focus-visible,.phone.high-contrast a:focus-visible,.phone.high-contrast input:focus-visible,.phone.high-contrast textarea:focus-visible,.phone.high-contrast select:focus-visible,.phone.high-contrast [tabindex]:focus-visible{outline:4px solid #000;outline-offset:3px}
/* dark + high-contrast: push the other way - near-white ink on near-black, bright borders, brightened brand text */
/* 2026-09-18: --navy/--sky were #a9cdf5 (light blue); now light gold #E2B85A, 11.24 on black, black on it 11.24.
   --gold-ink was #ffd77a; now the same #E2B85A. Fills stay Burgundy #7A2636 with white text 9.73. Green, red unchanged. */
.phone.dark.high-contrast{--ink:#fff;--muted:#d6dae0;--surface:#000;--canvas:#000;--border:#fff;--navy:#E2B85A;--sky:#E2B85A;--green:#8fe0ab;--red:#ffab9c;--gold-ink:#E2B85A;--bw-gold-ink:#E2B85A;--bw-cream-deep:#000;--bw-rule:#fff;--card-alt:#000;--card-alt-border:#fff}
.phone.dark.high-contrast .set-hint,.phone.dark.high-contrast .res-item .ra,.phone.dark.high-contrast .note{color:#d6dae0}
.phone.dark.high-contrast button:focus-visible,.phone.dark.high-contrast a:focus-visible,.phone.dark.high-contrast input:focus-visible,.phone.dark.high-contrast textarea:focus-visible,.phone.dark.high-contrast select:focus-visible,.phone.dark.high-contrast [tabindex]:focus-visible{outline:4px solid #fff;outline-offset:3px}
/* auto-dark + high-contrast: same push when the OS is dark and the user has not chosen a theme but HAS chosen high-contrast.
   CONFIRMED DEAD, ONETHEME 2026-09-07, same reason as the block near app.css:591: index.html's `.phone`
   always carries the static `.appearance-set`, so `:not(.appearance-set)` never matches. Kept, not
   deleted; see the full note there. */
@media (prefers-color-scheme: dark){
  /* 2026-09-18: dead block (see above); blues mapped to the same values as .phone.dark.high-contrast. Was --navy:#a9cdf5;--sky:#a9cdf5;--gold-ink:#ffd77a */
  .phone:not(.appearance-set).high-contrast{--ink:#fff;--muted:#d6dae0;--surface:#000;--canvas:#000;--border:#fff;--navy:#E2B85A;--sky:#E2B85A;--green:#8fe0ab;--red:#ffab9c;--gold-ink:#E2B85A;--bw-gold-ink:#E2B85A;--bw-cream-deep:#000;--bw-rule:#fff;--card-alt:#000;--card-alt-border:#fff}
  .phone:not(.appearance-set).high-contrast .set-hint,.phone:not(.appearance-set).high-contrast .res-item .ra,.phone:not(.appearance-set).high-contrast .note{color:#d6dae0}
  .phone:not(.appearance-set).high-contrast button:focus-visible,.phone:not(.appearance-set).high-contrast a:focus-visible,.phone:not(.appearance-set).high-contrast input:focus-visible,.phone:not(.appearance-set).high-contrast textarea:focus-visible,.phone:not(.appearance-set).high-contrast select:focus-visible,.phone:not(.appearance-set).high-contrast [tabindex]:focus-visible{outline:4px solid #fff;outline-offset:3px}
}

/* ACCESSIBILITY (2026-06-23, Ada): honor a user's high-contrast / forced-colors request. Default render unchanged. */
@media (prefers-contrast: more){
  :root{ --muted:#3a3d42; }
  .note{ opacity:1 !important; }
  a{ text-decoration:underline; }
}
@media (forced-colors: active){
  .res-crisis, .crisisbar, .card{ border:1px solid; }
  a, button{ forced-color-adjust:auto; }
}

/* 034: Settings gear in the global header (replaces the redundant Find-help header button; Help is the central nav tab + the finder is on Home, so crisis access is unchanged). Labeled, >=44px tap target. Reversible: delete this block, restore the header Find-help button, and swap the Updates nav button back to Settings. */
.hdr-gear{display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:6px 12px;border-radius:var(--radius);border:1.5px solid rgba(255,255,255,.55);background:rgba(255,255,255,.12);color:#fff;font-weight:700;font-size:13px;cursor:pointer;white-space:nowrap}
.hdr-gear .ic{font-size:16px}
.hdr-gear:hover{background:rgba(255,255,255,.22)}
#home-updates a{text-decoration:none}

/* 039 fixes: Settings gear never orphaned at any width (row never wraps, title cell grows, gear pinned). System-wide, every city. */
.hdr-row{flex-wrap:nowrap}
.hdr-row>div:nth-child(2){flex:1 1 auto;min-width:0}
.hdr-right{flex:0 0 auto}
/* 042 (Ryan): the alert bar is RED, thin, sleek - the amber override is removed; red is the alert color, every city. */
.topbar.alert{background:#B23A2E}

/* 050: crisis card collapsed by default (Ryan: the "Need help right now" box takes too much space; keep it small until tapped). The fixed 911/988/211 floor bar is the always-on safety net, so collapsing the top card keeps crisis one tap away and is safe. */
.res-crisis-acc .crisis-head-btn{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;background:var(--red);color:#fff;border:none;border-radius:12px;padding:11px 15px;min-height:52px;font-family:inherit;cursor:pointer;text-align:start}
.res-crisis-acc .crisis-head-main{display:flex;flex-direction:column;min-width:0}
.res-crisis-acc #crisis-head-lbl{font-size:18px;font-weight:800;line-height:1.2}
.res-crisis-acc .crisis-head-hint{font-size:12.5px;font-weight:600;opacity:.95;margin-top:2px}
.res-crisis-acc .crisis-head-chev{font-size:14px;flex:0 0 auto;transition:transform .15s}
.res-crisis-acc.crisis-collapsed{background:transparent;border:none;padding:0;margin-bottom:14px;box-shadow:none}
.res-crisis-acc .crisis-card-body[hidden]{display:none}
.res-crisis-acc .crisis-card-body{margin-top:12px}
.phone.dark .res-crisis-acc .crisis-head-btn{background:#A34A3A}
/* slim the fixed 911/988/211 floor bar so it is present but less dominant (still >=44px tap targets) */
.res-helpbar{padding:5px 12px calc(5px + env(safe-area-inset-bottom))}
/* 2026-08-17 cardlayout: [RYAN 2026-08-17] "Bottom 988 can it say Crisis/Mental Health". The new
   label wraps to two lines below 390px in eleven of the sixteen languages. The door had 12px of
   vertical padding it never needed: its content is flex-centered and its height comes from
   min-height, so the padding was dead air that would have forced the bar 10px taller on every
   phone the moment the label wrapped. Padding goes to 0, min-height pins the door at the 47px it
   has rendered at since 2026-08-16 (was min-height:46 + font rounding), and a two-line label now
   fits INSIDE the same door. Measured at 320/360/390 in all sixteen languages: door 47.0 before
   and after, one-line languages pixel-identical. No tap target shrinks, nothing gets taller. */
.res-helpbar .rrh-crisis a{min-height:47px;padding:0 4px}
/* below 350px the French and Portuguese labels missed a two-line fit by under 2px of door width.
   The bar's 12px side padding gives each door 4px back; every door only ever gets WIDER here. */
@media (max-width:349px){.res-helpbar{padding-left:8px;padding-right:8px}}
.res-helpbar .rrh-crisis .rrh-num{font-size:16px}
.res-helpbar .rrh-crisis .rrh-cs{font-size:12px}
/* ==============================================================================================
 * SHIPPICKS 2026-09-06. HIS DRAWING, AT SOURCE, ON THE FIXED FLOOR BAR. SIZING A.
 * `[RYAN 2026-07-23]` "big 911, medium call 988 medium call 988 small 211 nonemergency"
 * `[RYAN 2026-09-06 LOOK_FOUR]` "A = A. My drawing. 911 at 34px."
 * `[RYAN 2026-09-05]` "No. Crisis buttons never get smaller."
 *
 * He picked A from a rendered picture of exactly this (LOOK_2026-09-05.md, option A repaired). The
 * flag build of A (html[data-crisis-sizes="a"], 2026-08-14) could never deliver it: crisisbar_2026-
 * 08-28.css carried `#res-helpbar-fixed .rrh-crisis .rrh-911 .rrh-num{font-size:17px}` at (1,3,1),
 * an id rule that outranked the (0,4,1) flag rule, so under every flag the 988 digits grew and 911
 * stayed 17px: the exact inverse of his sentence. Measured 2026-09-05 at 390x844: A as flagged =
 * 911 17px / 988 26px. That file is retired today (crisisbar_2026-08-28.css.RETIRED_2026-09-06) and
 * the one thing it did that his eye had accepted, 911 at flex 1.25, is folded in here. No id
 * selector touches a crisis digit anywhere in the cascade now; these are (0,4,0) and (0,4,1) and
 * beat the 16px/47px base rules above on specificity, not on order.
 *
 * WHAT SHIPS:  911 digits 34px  .  call 988 and text 988 digits 26px  .  door min-height 64px  .
 * 911 keeps flex 1.25. THE ORDER IS HIS: big 911, medium 988 call, medium 988 text. 211 is NOT on
 * this bar (LAW L2) and the 211 box below the crisis card is not touched by this block: his drawing
 * said "small 211" and 211's drawn digit is already 26px, smaller than 34; shrinking it further would
 * break "crisis buttons never get smaller", so it stays exactly as it shipped.
 *
 * NOTHING SHRINKS. Door 47 -> 64px tall; digits 17 -> 34 and 16 -> 26; no width, padding or gap
 * reduced. The 988 doors keep the flex share they shipped with. The measured cost is chrome:
 * 22.3% -> 25.4% of the screen at 390x844 and 33.2% -> 37.7% at 320x568 (his own site,
 * agingwellinc.org, is 25.3% and 41.5%). Reported to him in SHIPPICKS_2026-09-06.md.
 *
 * Falsifier: _HARNESS/_shippicks_2026-09-06/FALSIFY_SHIPPICKS_2026-09-06.mjs, RED if 911 is not
 * the largest digit on the bar, RED if any crisis control is smaller than the pre-change baseline or
 * under 44px. Wired into predeploy_gate.mjs.
 * Undo: app.css.PRE_SHIPPICKS_* in NEVER_OVERWRITE_BACKUPS/shippicks_2026-09-06/.
 * ============================================================================================== */
.res-helpbar .rrh-crisis a.rrh-911,
.res-helpbar .rrh-crisis a.rrh-988,
.res-helpbar .rrh-crisis a.rrh-988t{min-height:64px;padding:9px 4px}
.res-helpbar .rrh-crisis .rrh-911{flex:1.25 1 0}
.res-helpbar .rrh-crisis .rrh-911 .rrh-num{font-size:34px}
.res-helpbar .rrh-crisis .rrh-988 .rrh-num,
.res-helpbar .rrh-crisis .rrh-988t .rrh-num{font-size:26px}
/* ---- END SHIPPICKS SIZING A ---- */

/* ==============================================================================================
 * POLISH 2026-10-01. THE CRISIS FLOOR IS ONE OBJECT, AND "Emergency" IS NEVER BROKEN IN HALF.
 * `[RYAN 2026-10-01]` asked for "the best looking usable functional app". LOOK_2026-10-01.md named
 * this component the ugliest thing in the app: "the crisis floor at 320 with Extra Large text, where
 * "Emergency" hyphenates to "Emergen / cy" under the 911".
 *
 * NOTHING ABOVE IS DELETED. Every rule this supersedes is still on the page, directly above; these
 * four declarations are later and of equal or greater specificity, so they win the cascade. Each one
 * was proved by reading the LIVE computed value in a real headless Chromium 154.0.8037.57 render,
 * never a fetch, at 320 and 390, Normal and Extra Large, light and dark.
 *
 * WHAT WAS ACTUALLY MEASURED, because two of LOOK's three geometry claims did not survive contact:
 *   - "Three tiles at three corner radii" is NOT TRUE. All three read border-radius 13px live, from
 *     `.res-helpbar .rrh-crisis a` at line 897. There was never a radius to unify and none is
 *     changed here. Recorded so the next agent does not go looking for a bug that is not there.
 *   - "All three rects are exactly equal" IS true: top 342.5, height 129.5 at 320 Extra Large.
 *   - "The middle 988 tile visibly riding above its neighbours" is true but it is NOT a paint
 *     treatment. It is the NUMBER, not the tile. Measured tops of the three `.rrh-num` spans at 320
 *     Extra Large: 911 = 360.1, 988 = 356.0, 988t = 366.1, a 10.1px spread; at Normal, 6.7px. Cause:
 *     the tiles are centred flex columns (`justify-content:center`, line 897), so a label that wraps
 *     to more lines pushes its own number UP. "Crisis/Mental Health" wraps to four lines where
 *     "Text instead" wraps to three, and the numbers drift apart by exactly that difference. The
 *     remaining 2px is `.rrh-988t`'s 2px border eating its content box under border-box sizing.
 *   - "Emergen / cy" is NOT caused by `overflow-wrap:anywhere`. `.rrh-cs` (line 899) already computes
 *     `overflow-wrap:break-word`, and `hyphens` is already `auto`. LOOK's two proposed one-value
 *     edits (anywhere -> break-word, or hyphens:none) would both have landed and done NOTHING here.
 *     The real cause is that the word does not fit: at 320 Extra Large the 911 label box is 88.5px
 *     and "Emergency" needs about 133px at that size. `word-break:break-word` on line 899 then
 *     breaks it with no hyphen. A word that does not fit must be given room or be broken.
 *
 * SO THE ROOM IS TAKEN FROM THE BOX, NEVER FROM THE LETTERS. No font-size is touched anywhere in
 * this block; `font_floor_min_12px` stays green and `.rrh-cs` stays at 12px. At Extra Large the row
 * is allowed to wrap and 911 takes a full-width row of its own, which is also the correct hierarchy:
 * 911 is the largest and most important door and it now reads as the headline of the floor. 988 and
 * 988t share the second row at equal width. Measured after: "Emergency" is ONE line at 320 and 390,
 * Normal and Extra Large, light and dark, and the number spread inside every row is 0.0px.
 *
 * NOT APPLIED WHILE THE KEYBOARD IS OPEN. `html[data-crisis-slim="1"]` is Ryan's own 2026-09-09
 * ruling ("the 911 988 988 is so big and there is my keyboard and i cant even see what comes up"),
 * which collapses the three doors to one compact row while typing. Letting this wrap fire in slim
 * mode would make the bar two rows tall at exactly the moment he ruled it must get shorter, so the
 * wrap is gated behind `html:not([data-crisis-slim="1"])`. Verified live with the attribute set.
 *
 * Every sacred href, and their order, is untouched: this block declares no content, no href and no
 * display:none. The floor is tel:911, tel:988, sms:988, in that order, before and after (LAW L2).
 * Undo: NEVER_OVERWRITE_BACKUPS/polish_2026-10-01/app.css.PRE_POLISH
 * ============================================================================================== */
.res-helpbar .rrh-crisis a{justify-content:flex-start}   /* was justify-content:center (line 897), which let a longer label push its own number up. Top-aligning the column pins all three numbers to the same padding edge. Measured spread 10.1px -> 0.0px. */
.res-helpbar .rrh-crisis a.rrh-988t{padding-top:7px}   /* was 9px (line 2116). .rrh-988t is the only one of the three with a border (2px, line 892 region); under border-box that 2px pushed its content down. 7+2 = the 9 the other two have. Measured residual 2.0px -> 0.0px. Outer size, min-height and radius all unchanged. */
html:not([data-crisis-slim="1"]) .phone.text-xlarge .res-helpbar .rrh-crisis{flex-wrap:wrap}
html:not([data-crisis-slim="1"]) .phone.text-xlarge .res-helpbar .rrh-crisis .rrh-911{flex:1 1 100%}   /* was flex:1.25 1 0 (line 2117), which is kept and still governs Normal and Large. At Extra Large only, 911 takes its own full-width row so "Emergency" fits whole. */
/* ---- END POLISH CRISIS FLOOR 2026-10-01 ---- */

/* ==============================================================================================
 * CRISISBAR SLIM MODE, 2026-09-09. `[RYAN 2026-09-09]` "or like i search poll but the 911 988 988
 * is so big and there is my keyboard and i cant even see what comes up." HIS RULING, on a card,
 * that night: "While typing only, the three doors collapse into one compact row about 44px tall,
 * still tappable, still all three numbers. Returns to full size the moment you stop typing or tap
 * a result." He chose this over hiding the bar and over leaving it full size.
 *
 * DRIVEN ENTIRELY BY html[data-crisis-slim="1"], set ONLY by bottom_dock_2026-08-15.js's
 * applySlim() - true only while a real DOM focus sits on one of the four search inputs (#res-q,
 * #csv-input, #ev-q, #news-q) AND the SAME visualViewport keyboard-open test --bw-kbdlift already
 * uses (keyboardHiddenPx() >= MIN_LIFT) is true. Never a screen-height guess, never a second
 * detector - read bottom_dock_2026-08-15.js's own header next to that block first.
 *
 * LAW L1, CHECKED IN EVERY RULE BELOW: every door keeps min-height:44px, no door is hidden, no
 * digit reordered, no number renamed. This block only ever shrinks padding, gap and font-size, and
 * only while html[data-crisis-slim="1"] is present - the instant it is not (not typing, keyboard
 * closed, or a result was just tapped), every rule here stops matching and the bar/dropdown are
 * governed by SHIPPICKS SIZING A above, byte-identical to before this block existed.
 *
 * SPECIFICITY. Every selector below carries `html[data-crisis-slim="1"]` (an attribute selector,
 * class-weight, plus the `html` type) ahead of the SAME class chain SHIPPICKS SIZING A and the base
 * `.res-suggest li.crisis-sug` rules use, so each one outranks its unslimmed counterpart on
 * specificity alone - no !important, same technique SHIPPICKS and the SIZING A block above already
 * use for the identical reason (see their own comments on why bare `html` is there).
 *
 * SURFACE 1 - THE HELP FIXED FLOOR BAR (`.res-helpbar` inside #bw-dock, Help/#res-q only, always on
 * screen whenever Help is open so it is the one that never gets a 150ms self-close the way the
 * suggestion dropdown below sometimes does). Collapses the two-line "911 / Emergency" stacked door
 * to one line: the descriptive word (`.rrh-cs`) shrinks rather than disappears, so a voice-control
 * or screen-reader user reading the DOM still gets the full label; only the drawn size changes.
 * ============================================================================================== */
html[data-crisis-slim="1"] .res-helpbar{
  padding: 3px 8px calc(3px + env(safe-area-inset-bottom, 0px));
}
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis{ gap: 6px; }
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis a.rrh-911,
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis a.rrh-988,
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis a.rrh-988t{
  min-height: 44px;
  padding: 2px 4px;
  flex-direction: row;
  gap: 5px;
}
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis .rrh-911{ flex: 1.1 1 0; }
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis .rrh-911 .rrh-num{ font-size: 16px; }
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis .rrh-988 .rrh-num,
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis .rrh-988t .rrh-num{ font-size: 14px; }
/* SPACE 2026-09-28. 8.5px -> 12px. THE CAPTION UNDER 911, 988 AND TEXT-988 ON THE CRISIS FLOOR BAR.
 * `[RYAN 2026-09-28]` "and then everything else above that needs fixing you have all day do it right"
 *
 * WHAT WAS HERE: a declared size of 8.5px, shipped 2026-09-09 with slim mode. 3.5px under this app's own
 * 12px floor, on the crisis bar, on an app built for older adults and people with low vision, while
 * the numbers beside it are 16, 14 and 14px. It was the LAST offender against `font_floor_min_12px`
 * in `decision_invariants.mjs`, the invariant that exists to protect exactly this caption.
 *
 * WHY IT SAT AT 8.5 FOR NINETEEN DAYS, honestly: `NEVER_OVERWRITE\GATEFULL_2026-09-09.md` found it
 * the night it shipped and wrote "this needs a human decision ... my call is do not waive it
 * silently"; `NEVER_OVERWRITE\A1_TOKENS_2026-09-18.md` left it with the reason "both crisis-slim
 * rules under Ryan's own ruling: card"; FONTCSS measured it safe on 2026-09-27 and still left it,
 * saying the same thing. THE CARD WAS NEVER PUT IN FRONT OF HIM. Three passes deferred one number
 * to a card nobody wrote. He has now said to fix everything that needs fixing, so it is fixed and
 * the after picture goes to him rather than the question.
 *
 * MEASURED AGAIN HERE, NOT TRUSTED FROM THE PRIOR REPORT. Real headless Chromium, the app's own
 * gear control driving text size and the mode verified engaged before every measurement, an honest
 * `visualViewport` shim (width always === innerWidth, height = innerHeight - keyboard, so the app's
 * own `keyboardHiddenPx()` sees a real keyboard), 19 language codes x {320x568, 390x844} x
 * {Normal, Large, Extra Large} x {slim on, slim off} = 228 configurations, rendered ink measured as
 * computed font-size x the painted/layout ratio, because CSS `zoom` is invisible to
 * `getComputedStyle`.
 *
 * THE FINDING THAT CHANGES HOW THIS NUMBER READS, AND NOBODY HAD STATED IT: the 8.5 only ever
 * painted as 8.5 at NORMAL text size. At Large and Extra Large `.phone` carries `zoom:1.5`/`zoom:2`
 * (app.css:1412-1413), so 8.5px was already painting 12.6 to 13.0 visual px in all nineteen
 * languages. The harm was real and it was confined to Normal, which is the default and therefore
 * the size almost every resident sees. At 12px the caption paints 12.0 at Normal and about 18 at
 * Large and Extra Large.
 *
 * COST, MEASURED BOTH WAYS, before and after, same harness, same run: recorded in
 * `outputs\SPACE_2026-09-28.md`. Nothing clips (`scrollWidth` never exceeds `clientWidth` on any
 * caption in any configuration), nothing overflows sideways (sub-pixel max painted right edge vs
 * `innerWidth`), every door stays at or above 44px, the href list and its order are byte-identical,
 * and slim mode still does its job: the bar stays far under the height it occupies with slim off.
 *
 * CASCADE, PROVED FROM LIVE COMPUTED VALUES AND NOT FROM SPECIFICITY ARITHMETIC, per
 * `NEVER_OVERWRITE\OVERRIDE_TRAP_2026-09-18.md`: `app.css` is the only file on the wall that
 * declares a font-size on `.rrh-cs` at all, and this selector already won at 8.5px, so it keeps the
 * exact cascade position it had. Verified by reading `getComputedStyle(.rrh-cs).fontSize` live in
 * every one of the 228 configurations after the change.
 *
 * NOTHING ELSE IN THIS RULE MOVED. `line-height` and `max-width` are byte-identical. No door is
 * hidden, no digit reordered, no number renamed, no `tel:` or `sms:` href touched: every href in
 * every check was read as a string and nothing was ever dialled.
 * Undo: put 8.5px back on the one declaration below. The value is written here as prose and never as
 * a declaration, because `font_floor_min_12px` in `decision_invariants.mjs` is a raw regex over this
 * whole file and does not know a comment from a rule: writing the old value in declaration form
 * inside a comment kept the gate RED with nothing wrong on screen. Verified by running the gate.
 * The CHECK WAS NOT WIDENED and its regex was not touched; the sentence was. */
html[data-crisis-slim="1"] .res-helpbar .rrh-crisis .rrh-cs{
  font-size: 12px;
  line-height: 1.05;
  max-width: 100%;
}

/* SURFACE 2 - THE CRISIS-SUGGESTION ROWS every one of the four search dropdowns can show
 * (`.res-suggest li.crisis-sug`, built by app.js resSugOpen for #res-q and by
 * search_unify_2026-08-23.js's shared Combo() for #ev-q/#news-q/#csv-input - one class, one fix,
 * four boxes). `display:inline-flex` on each `.crisis-sug` <li> lets 2-3 adjacent items (they are
 * always generated first, with no other markup between them - grep app.js resSugOpen and
 * search_unify_2026-08-23.js Combo()'s crisis branch) sit side by side in the SAME row that normal
 * `<li>` block-level flow would have stacked vertically; the first non-crisis result after them is
 * still `display:flex` (its own rule, untouched) so it drops to its own new line automatically,
 * exactly the "one compact row, then the real results" shape his ruling describes. Icon and the
 * descriptive line are hidden here (not on the floor bar above) because at 320px three side-by-side
 * pills have no room for either without wrapping to more than one line; the full label is still in
 * the DOM and still read by a screen reader (aria stays on the <li>, nothing here touches it). */
html[data-crisis-slim="1"] .res-suggest li.crisis-sug{
  display: inline-flex;
  vertical-align: top;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: calc(33.333% - 5px);
  min-width: 0;
  min-height: 44px;
  margin: 0 4px 4px 0;
  padding: 3px 3px;
  gap: 1px;
  border-left: none;
  border-bottom: 3px solid var(--red, #a32525);
  border-radius: 8px;
  text-align: center;
}
html[data-crisis-slim="1"] .res-suggest li.crisis-sug .ic{ display: none; }
html[data-crisis-slim="1"] .res-suggest li.crisis-sug .cs-main{
  flex-direction: column;
  align-items: center;
  gap: 0;
}
/* FONTCSS 2026-09-27. 11.5px -> 12px. This is the label on a CRISIS SUGGESTION, on an app built for
 * older adults and people with low vision, in the slim mode that exists for the smallest screens. It
 * is ONE OF TWO offenders against `font_floor_min_12px` in `decision_invariants.mjs`, and that
 * invariant's own record shows this caption falling under the floor THREE times before 076. The
 * check was not widened to 11px and the rule was not deleted to buy a green. The number is fixed.
 *
 * SUPERSEDED 2026-09-28 BY SPACE: the paragraph below was true when FONTCSS wrote it and is not
 * true now. The 8.5px value it describes was raised to 12px above on 2026-09-28 and
 * `font_floor_min_12px` no longer has an offender in this file. FONTCSS's words are kept whole
 * because its measurement is the record of how the number was checked before it was changed.
 * THE OTHER OFFENDER IS STILL THERE, ON PURPOSE, AND IT IS WORSE. The 8.5px declaration on
 * `html[data-crisis-slim="1"] .res-helpbar .rrh-crisis .rrh-cs` (about forty lines above this one) is
 * the descriptive word under 911 / 988 / 988-text on the fixed Help floor bar. It is 3.5px under the
 * floor, and it is the very caption `font_floor_min_12px`'s own description NAMES. It was NOT
 * changed here. It is not an oversight: `NEVER_OVERWRITE\GATEFULL_2026-09-09.md` found it the night
 * CRISISBAR shipped and wrote "this needs a human decision ... my call is do not waive it silently",
 * and `NEVER_OVERWRITE\A1_TOKENS_2026-09-18.md` left both values with the reason "both crisis-slim
 * rules under Ryan's own ruling: card". That card was never put in front of him. Measured tonight
 * the same way as the label below, 8.5 -> 12px is SAFE on every metric (nothing clips, no horizontal
 * overflow, doors stay on one line, every door stays >=44px and still hit-tests to itself, at 320 and
 * 390, Normal and Extra Large, in every language) and costs door height: at 320/Normal English,
 * Korean, Arabic and Greek do not move at all, Polish 44 -> 54, Russian 44 -> 58; at 320/Extra Large
 * English and Polish 75 -> 119 and Russian 78 -> 100, still well under the 129-170 the same bar
 * occupies with slim mode OFF, so slim mode still does its job at 12px. It is left at 8.5px because
 * raising it visibly changes how the crisis floor bar looks at Extra Large and HIS EYE DECIDES WHAT
 * CORRECT IS. `font_floor_min_12px` therefore stays RED on this one value until he rules.
 * So: `decision_invariants.mjs` still reports 2 RED, not 1. The claim that this surface had "exactly
 * one offender" was wrong; `BUILD_BATON.md` has recorded the correct count, two, for weeks.
 *
 * WHY 11.5 WAS THERE, AND WHAT IT WAS ACTUALLY BUYING. Presumed: something did not fit at 12px at
 * 320 wide in a long language. MEASURED, and it does not. In the live app, Help open, #res-q focused
 * with the crisis rows painted and data-crisis-slim="1" forced on, at 320 wide, in all seventeen
 * languages, at 11.5 / 12 / 12.5px: NOTHING is clipped (`scrollWidth` never exceeds `clientWidth` on
 * any label, in any language, at any of the three sizes), the dropdown NEVER overflows horizontally,
 * the pills NEVER break out of their single row, and `min-height:44px` holds on every door every
 * time. Repeated at 320 wide with .text-xlarge (CSS zoom 2): same verdict, and the dropdown is
 * already scrolling at 11.5px there, so 12px introduces no scroll that was not already present.
 *
 * THE ONLY COST IS A FEW PIXELS OF PILL HEIGHT, because the label wraps rather than clipping (no
 * white-space:nowrap here, and none wanted). Pill height 11.5px -> 12px at 320/normal: English,
 * Khmer, Korean, Chinese, Chinese-traditional 44 -> 44 (no change at all, the 44px floor absorbs
 * it); French, Bengali, Vietnamese 47 -> 49; Spanish, Portuguese, Polish, Greek 60 -> 62; Italian
 * and Arabic 47/60 -> 62/75, the worst case at +15px; Lao 44 -> 49; Russian 97 -> 101. Russian is
 * the longest string in the app on this surface, `cb988` at 56 characters, and it wraps to seven
 * lines at BOTH sizes: half a pixel was never what made Russian fit.
 *
 * So there was no real constraint to solve: no letter-spacing, padding, line-height or wrapping
 * change is needed, and inventing one would be changing how a crisis suggestion looks for nothing.
 * The half pixel was buying a few pixels of vertical compactness inside a dropdown that scrolls.
 *
 * CASCADE. Changed IN PLACE, in the file that owns the thing, per LAW L6 and the 2026-09-02 ruling,
 * NOT as a new sheet on top: `app.css` is the ONLY file on the wall that declares a font-size on
 * `.cs-lbl` at all (grepped every `.css`, plus inline styles in `index.html` and every `.js`), so
 * this declaration keeps the exact cascade position that was already winning at 11.5px and there is
 * no override race to lose. See NEVER_OVERWRITE\OVERRIDE_TRAP_2026-09-18.md.
 * line-height and max-width below are UNCHANGED. */
html[data-crisis-slim="1"] .res-suggest li.crisis-sug .cs-lbl{
  font-size: 12px;
  line-height: 1.1;
  max-width: 100%;
}
html[data-crisis-slim="1"] .res-suggest li.crisis-sug .cs-desc{ display: none; }

/* ============================================================================================== *
 * SURFACE 3 - THE SCREEN SPACE ITSELF, WHILE THE KEYBOARD IS UP. SPACE 2026-09-28.
 * `[RYAN 2026-09-28]` "be mindful of screen space everywhere especially when the presearfhc items
 * load with the keyboard open maybe only do 3 when that is the case ... and then everything else
 * above that needs fixing you have all day do it right"
 * `[RYAN 2026-09-24]` "SEEMS like too much wasted space on the nav buttons row screen space is gold"
 *
 * WHAT A RESIDENT ACTUALLY HAD, MEASURED BEFORE ANY OF THIS, real headless Chromium, 320x568, Help
 * open, #res-q focused, an honest visualViewport shim (keyboard 298px, so the app`s own
 * keyboardHiddenPx() sees a real keyboard and sets data-crisis-slim itself):
 *   visible viewport 270px. Masthead 0-56. Search box 64.0-112.8. Result list 118.8-215.8, so
 *   NINETY-SEVEN PIXELS OF RESULT LIST, holding 8 suggestions whose own scrollHeight is 427px.
 *   TWO of the eight rows were fully on screen. The crisis bar 225-270.
 * At 390x844 with a 444px keyboard: 215px of list, five of eight rows fully on screen.
 *
 * WHERE THE WASTED SPACE WAS, AND THIS IS THE WHOLE OF IT. The height of the list is not a
 * stylesheet number: the cap is computed from the visible viewport minus where the list STARTS
 * minus the dock. So every pixel above the list is a pixel of list. Above it sat `.view`s 16px top
 * padding (app.css:165), `.res-search`s 2px top margin (app.css:829, NOT reclaimable, see the
 * withdrawn line below) and the list`s own 6px top
 * margin and 6px inner padding (app.css:1003). Thirty pixels of box, on a 270px screen, holding
 * nothing. Inside the list, every row carried 12px of padding above and below its text
 * (app.css:1147) and every sub-row 9px (app.css:1178).
 *
 * TAKEN FROM THE BOXES, NEVER FROM THE LETTERS. NOT ONE font-size, line-height, min-height or tap
 * target is touched here. `.res-suggest li` keeps `min-height:48px` and `li.sug-sub` keeps
 * `min-height:44px`, so a one-line row does not move at all and stays a full target; only rows that
 * wrap to two or more lines get shorter, and only by the padding. This app exists for people who
 * cannot read small text and the dock`s own defensive-shrink fix had to be undone for exactly that
 * reason. Shrinking text to buy room is not available here and was not used.
 *
 * GATED ON html[data-crisis-slim="1"] AND NOTHING ELSE, so every one of these rules stops matching
 * the instant she stops typing or the keyboard closes, and the app with the keyboard closed is
 * byte-identical to before this block existed. Same gate, same technique and same reason as SURFACE
 * 1 and SURFACE 2 above. Every selector repeats the class chain its base rule uses and adds the
 * attribute selector plus the `html` type, so it outranks its own base on specificity alone, no
 * !important. Cascade NOT reasoned about: the live computed value of every property below is read
 * back in the browser in all 228 configurations, per NEVER_OVERWRITE\OVERRIDE_TRAP_2026-09-18.md.
 *
 * WHAT IT IS NOT. It is not a cap on how many suggestions app.js builds: that is SEARCHUX`s half of
 * his sentence and lives in app.js. It does not touch #bw-dock, the nav row or its height: another
 * pass reclaimed 1.219px there and this builds on that rather than fighting it. It does not touch
 * the crisis bar`s doors, hrefs, order or 44px floor.
 * Undo: delete this block. Nothing else refers to it. */
html[data-crisis-slim="1"] #view-community.view{ padding-top: 4px; }              /* was 16px, app.css:165 */
/* WITHDRAWN 2026-09-28, AND KEPT HERE AS THE RECORD OF AN OVERRIDE TRAP THAT WAS ACTUALLY HIT.
 * The line that was here read `html[data-crisis-slim="1"] #view-community > .res-search{margin-top:0}`
 * and it LANDED AND DID NOTHING. Not reasoned about: `getComputedStyle` was read live in the browser
 * with slim mode engaged and returned `2px`, so the rule lost. The winner, found by enumerating every
 * matching rule on the live element rather than by counting specificity, is this file's own
 * `@media (max-height:568px),(max-width:900px){ #view-community > .res-search,#news-search-wrap
 * { margin:2px 0 16px !important } }` near app.css:3804. `!important` beats a normal declaration
 * whatever its specificity, which is exactly the mechanism NEVER_OVERWRITE\OVERRIDE_TRAP_2026-09-18.md
 * row 6 records. The rule is WITHDRAWN rather than escalated: the prize is TWO PIXELS, and answering
 * an `!important` with another `!important` on a rule whose own block says the 2px is load-bearing
 * arithmetic (app.css:4757, "16 + 2 + 89 + 16 = 123") would be buying two pixels with a fight over a
 * number somebody else proved. Nothing is deleted from that media rule. Two pixels stay spent. */
html[data-crisis-slim="1"] .res-suggest{ margin-top: 3px; padding-top: 3px; padding-bottom: 3px; }   /* was 6px / 6px / 6px, app.css:1003 */
html[data-crisis-slim="1"] .res-suggest li{ padding-top: 8px; padding-bottom: 8px; }                 /* was 12px, app.css:1147. min-height:48px UNTOUCHED */
html[data-crisis-slim="1"] .res-suggest li.sug-sub{ padding-top: 7px; padding-bottom: 7px; }         /* was 9px, app.css:1178. min-height:44px UNTOUCHED */
/* ---- END CRISISBAR SLIM MODE ---- */

/* ============ 050b HELP LAYOUT REFINEMENT (HELP_LAYOUT_LITREVIEW_2026-07-19_050) ============ */
/* crisis card: compact bar with inline horizontal Call 911 / Call 988 + More; rest one tap away */
.res-crisis.res-crisis-acc{padding:12px;background:var(--red-soft);border:1.5px solid var(--red);border-radius:14px;margin-bottom:14px}
.res-crisis-acc.crisis-collapsed{padding:12px;background:var(--red-soft);border:1.5px solid var(--red)}
.res-crisis-acc .crisis-bar{display:flex;flex-direction:column;gap:9px}
.res-crisis-acc .crisis-bar-lbl{font-size:16.5px;font-weight:800;color:var(--red);display:flex;align-items:center;gap:6px;line-height:1.15}
.res-crisis-acc .crisis-bar-row{display:flex;gap:8px}
.res-crisis-acc .cq{flex:1 1 0;min-width:0;display:inline-flex;align-items:center;justify-content:center;gap:5px;min-height:48px;border-radius:11px;font-family:inherit;font-weight:800;font-size:16px;text-decoration:none;cursor:pointer;padding:8px 6px;text-align:center;border:1.5px solid var(--red);box-sizing:border-box}
.res-crisis-acc .cq.call{background:var(--red);color:#fff}
.res-crisis-acc .cq.more{background:#fff;color:var(--red);flex:0 0 auto;padding:8px 15px}
.res-crisis-acc .cq .cq-chev{font-size:12px;transition:transform .15s}
.res-crisis-acc .cbtn-text{margin-bottom:10px}
.phone.dark .res-crisis-acc .cq.call{background:#A34A3A;border-color:#A34A3A}
.phone.dark .res-crisis-acc .cq.more{color:#A34A3A;border-color:#A34A3A}
/* 211: more compact vertically */
.res-211{padding:12px 14px;gap:13px;margin-bottom:14px}
.res-211 .big{font-size:33px;line-height:1}
.res-211>div>div:first-child{font-size:15px}
.res-211>div>div:nth-child(2){margin-top:2px}
.res-211 a{margin-top:7px;min-height:44px;padding:8px 14px}
/* need chips: grid so rows fill evenly, no orphan */
.help-cats{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:7px;margin-bottom:12px}
.help-cats .help-cat{width:100%;text-align:center;white-space:normal}
/* responsive shell: refined bounded width on tablet/desktop instead of one stretched column */
@media (min-width:760px){
  #view-community{max-width:1140px;margin-left:auto;margin-right:auto;padding-left:22px;padding-right:22px}
  .help-cats{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
  .res-crisis-acc .crisis-bar{flex-direction:row;align-items:center;justify-content:space-between}
  .res-crisis-acc .crisis-bar-lbl{flex:0 0 auto}
  .res-crisis-acc .crisis-bar-row{flex:1 1 auto;max-width:520px}
  /* OBS-2 (081): masonry columns so uneven-height category cards fill both columns; the count header spans all so it stops eating column 1 and stranding the left column. Phone (<760) stays single-column block, unchanged. */
  #res-list{display:block;column-width:320px;column-gap:14px}
  #res-list .res-resulthead{column-span:all}
  #res-list .res-cat{break-inside:avoid;margin:0 0 14px}
  /* 2026-08-17 cardlayout: the crisis card is a CRISIS SURFACE and must read as ONE card. OBS-2's
     masonry only protected .res-cat, so any .res-crisis sitting directly in #res-list (the app's own
     crisisHit card at app.js:1292, safe_911_first's lead card, and the crisis-foot) was FRAGMENTED
     across the columns: measured 2026-08-17 at 1300x850 on the "safe" browse, its boxes sat at three
     x positions (87/477/867) with buttons sliced mid-pixel. crisiscard_BROKEN_wide.png is the proof.
     column-span:all lifts the card out of the column flow so it renders whole, full width, above the
     columns, at every width. Phone (<760) never enters this block and is unchanged. */
  #res-list>.res-crisis{column-span:all;break-inside:avoid}
}
@media (min-width:1100px){
  #view-community{max-width:1200px}
}
/* phone only (2-col chips): if the last chip is alone on its row, span full width so it is intentional, not orphaned */
@media (max-width:759px){
  .help-cats .help-cat:last-child:nth-child(odd){grid-column:auto}
}

/* 050d: Updates page done + refined; bound all content views on desktop (Ryan: all pages max-width like a website) */
#view-updates .card{margin-bottom:0}
@media (min-width:760px){
  #view-home,#view-events,#view-updates,#view-beloved,#view-settings,#view-help,#view-findhelp,#view-trash,#view-youth{max-width:1140px;margin-left:auto;margin-right:auto;padding-left:22px;padding-right:22px;box-sizing:border-box}
  #updates-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:14px;align-items:start}
  /* website width: header + announcement bar align to the SAME content column as the views (was 820, floated narrow) */
  .hdr-row,.topbar-inner{max-width:1140px;padding-left:6px;padding-right:6px}
}
@media (min-width:1100px){
  #view-home,#view-events,#view-updates,#view-beloved,#view-settings,#view-community{max-width:1200px}
  .hdr-row,.topbar-inner{max-width:1200px}
}
/* 050e: actionable update buttons (directions / call / source link) */
.upd-acts{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.upd-act{display:inline-flex;align-items:center;gap:4px;background:var(--surface);border:1.5px solid var(--navy);color:var(--navy);border-radius:var(--radius);padding:8px 13px;min-height:40px;font-size:14px;font-weight:700;text-decoration:none;font-family:inherit}   /* 2026-09-18 W3.1-25: was border-radius:9px */
/* ==============================================================================================
   BW-11, 2026-09-24. THE NEWS BUTTONS OVERFLOWED THEIR OWN BOXES IN EVERY LANGUAGE TESTED.
   ----------------------------------------------------------------------------------------------
   Measured: div.upd-acts 108 in 100 and button.upd-act 106 in 97 at English 320 Extra Large;
   Russian "Посмотреть подробности" 137 in 133 at 390 Extra Large; Khmer 105 in 98; Spanish "Para
   esto, llame Woonsocket Board of Canvassers" 106 in 98, and in Spanish the whole alert row went
   139 in 128 with it.
   THE CAUSE IS ONE LINE: `display:inline-flex` on a button whose label is a SENTENCE. An
   inline-flex box sizes to its content on one unbreakable line and then refuses to give any of it
   back, so the label decides the width of the row instead of the row deciding the width of the
   label. Every one of the strings above is a sentence, not a verb.
   THE FIX: let the label wrap inside the button, and let the button shrink to the row. `max-width`
   stops it ever being wider than the card it sits in. `text-align:start` keeps a wrapped second
   line reading from the same edge as the first in both LTR and RTL. `min-height` goes 40 to 44 in
   the same rule, which is the BW-15 floor this button was 4px under anyway.
   No label is shortened, no font-size moves, no border or colour changes.
   ============================================================================================== */
.upd-act{flex:0 1 auto;min-width:0;max-width:100%;white-space:normal;overflow-wrap:anywhere;text-align:start;min-height:44px;flex-wrap:wrap}   /* `anywhere`, not `break-word`: only `anywhere` takes part in intrinsic sizing, so only `anywhere` lets the flex row offer this button the width it has. See the block at line 1316. */
.upd-acts{min-width:0}

/* ==================================================================================================
 * SIDEWAYS 2026-09-28 (DOCK2). `[RYAN 2026-09-24]` "the bottom nav seems to float sometimes", and
 * his job #1, "the nav bar in the middle of the page". THE CAUSE IS A PHONE NUMBER, AND IT IS HERE.
 * --------------------------------------------------------------------------------------------------
 * REPRODUCED AND TRACED 2026-09-28 in real Chromium, News view, 390x844, text-large, English, NO
 * keyboard and NO shim, every figure read off the live document:
 *   window.innerWidth  390 -> 425      window.innerHeight 844 -> 920
 *   at Extra Large     390 -> 469                         844 -> 1015
 *   Russian at Large   390 -> 438                         844 -> 948
 *   English 320 Large  320 -> 373                         568 -> 663
 * The page is wider than the device, so Chromium widens the LAYOUT viewport to fit it. #bw-dock is
 * `position: fixed`, which is laid out against the layout viewport, so it pins itself to the bottom
 * of a 920px page on an 844px screen and ends up 76px below the fold, with the page still visible
 * underneath it. That is the nav bar in the middle of the page, and no amount of pinning fixes it,
 * because the dock is doing exactly what it was told. THE CURE IS THE OVERFLOW.
 *
 * WHAT WAS TOO WIDE. Not a box: no element in the whole document had a right edge past 390.
 * `.phone` had clientWidth 260 and scrollWidth 284, 24 local px of overflow, and 24 x 1.5 = 36,
 * which is the 35 the layout viewport grew by. It is overflowing TEXT, which does not grow the box
 * that holds it, which is why every box-rect sweep before tonight found nothing.
 * MEASURED with a Range over every text node: `span.tel-num` holding `(401) 767-9223`, inside
 * `a.upd-act`, a News action button. The span is a flex item with `min-width: 0`, so flex shrank it
 * to 55.8 rendered px; `.tel-num` carries `white-space: nowrap`, so its 154.6 px of digits could not
 * wrap and spilled 98.8 px out of the button, out of the card, out of the view and off the screen to
 * x = 425.3. `[RYAN]` a single number wider than the screen, literally.
 *
 * WHY IT APPEARED TONIGHT AND WHY NEITHER HALF IS WRONG. RTLFIX added `.tel-num{white-space:nowrap}`
 * TODAY, and it is RIGHT: it measured 15 phone numbers breaking across two lines in Arabic and 20 in
 * English, and a resident who cannot read a number in one piece cannot dial it. The `min-width: 0`
 * on `.upd-act` above is RIGHT TOO, and its own note says why: `overflow-wrap: anywhere` is what
 * lets that button take the width the flex row can offer it. The two are only wrong together,
 * because `nowrap` takes the number out of the wrapping contract the rest of the button lives by.
 *
 * SO BOTH ARE KEPT AND NEITHER IS WEAKENED. `flex-wrap: wrap` on `.upd-act` above lets the number
 * move to its own line INSIDE the button instead of being crushed, and `min-width: min-content`
 * below stops flex shrinking a `nowrap` span below the width of its own unbreakable content, which
 * is the container-independent half: it holds in any flex or grid parent, not just this one.
 * NOT `white-space: normal`, which would put the hyphen break back. NOT U+2011 - RTLFIX and RTLSCOUT
 * both record in blood that an ASCII hyphen is a European Separator that JOINS two digit groups
 * under bidi rule W4, and that is why `401-767-4124` reads as one ascending block in Arabic.
 * NOT `overflow: hidden`, which would have clipped the last four digits of a phone number.
 * MEASURED AFTER, same cells: innerWidth 390 stays 390 and innerHeight 844 stays 844.
 *
 * `.tel-num`'s own declaration now lives here, which is where RTLFIX asked for it: it is parked as a
 * self-described LODGER in bar_variants_2026-08-28.css:285 only because app.css was claimed for
 * write all of 2026-09-28, and that block says to delete it the moment app.css carries this. THAT
 * DELETION IS NOT MINE - bar_variants_2026-08-28.css was not mine to open tonight - AND IT IS HANDED
 * OVER: bar_variants_2026-08-28.css, the RTLFIX F1 block at lines 259-285, retire the whole block.
 * Until then the two declarations are identical and the later file simply re-states them; the
 * `min-width` here is untouched by it and wins on its own.
 *
 * Undo: delete this block and the ` ;flex-wrap:wrap` on `.upd-act` above.
 * ============================================================================================== */
.tel-num{ unicode-bidi:isolate; white-space:nowrap; min-width:min-content; }

/* The same pair, in the other place it bites. MEASURED 2026-09-28 on Help: `a.cl-callbtn`, the
   "Call the replacement" button, is `display:inline-flex` in helptail_consolidated_2026-08-30.css:49
   with no `flex-wrap` and no `max-width`, so it takes its content's full width and runs off the
   right edge in EVERY language: English 35.7 px past 320 at Large and 139.2 px past at Extra Large,
   Russian 33.6 and 136.4, Greek 6.1 px past 320 at NORMAL text. In Russian at 320 Extra Large it
   takes the layout viewport with it, 320 -> 351 and 568 -> 624, which is the same dock fault as
   News. `.phone .cl-callbtn` is (0,2,0) against that file's (0,1,0), so it wins the cascade on
   specificity alone and needs no `!important` - PROVED from the live computed value, not reasoned
   about from load order. THIS IS AN OVERRIDE AND IT IS NOT WHERE IT BELONGS: the two declarations
   belong on the rule in helptail_consolidated_2026-08-30.css:49, and HANDED OVER to that file's
   owner to move them there and delete this. It is here because that file was not mine tonight and
   the button is off the screen today. Undo: delete these two lines. */
.phone .cl-callbtn{ flex-wrap:wrap; max-width:100%; }

/* The third container, found by re-measuring after the first two rather than by reading the list
   again: `a.res-call.oc-primary`, the resource card's call button on Help. Same shape exactly -
   `display:flex`, no `flex-wrap`, a `.tel-num` child squeezed to 19.2 rendered px by the app-wide
   reset while `1-800-422-4453` needs about 97. MEASURED 2026-09-28 at 320 Extra Large: Russian
   77.6 px of digits past the right edge and the layout viewport taken 320 -> 351 with it, Greek
   75.7 px and 320 -> 364, Polish 320 -> 351. `flex-wrap: wrap` puts the number on its own line
   inside the button, whole.

   WHY THE CONTAINER AND NOT `min-width` ON `.tel-num`. I WROTE THE `min-width: min-content` ABOVE
   AND THEN PROVED IT DEAD, from the live computed value and not from reading the files: at 320
   Extra Large `getComputedStyle(.tel-num).minWidth` reads `0px`, not `min-content`. It loses to
   `app.css:1469` `.phone.text-large *,.phone.text-xlarge *{min-width:0}`, which is (0,2,0) against
   its (0,1,0) and outranks it whatever the order, and to `org_card_2026-08-23.css:144`
   `.res-item.oc-card .res-actions > * > *{min-width:0}` at (0,3,0) as well. Winning that fight would
   have needed a four-class selector or an `!important`, and BOTH of those would have been me putting
   a phone number back at its intrinsic width in the one place the app-wide reflow guard exists to
   stop exactly that. THE RESET IS RIGHT AND I LEFT IT ALONE. `min-content` is KEPT rather than
   deleted because it is correct and it does apply at Normal text, where no reset outranks it - but
   it is the containers below and above that carry the fix at Large and Extra Large, and this note is
   here so nobody spends another night wondering why the `min-width` looks like it should be enough.
   DOCK2 ADDED NO `!important` ANYWHERE.
   Undo: delete this line. */
.phone .res-call{ flex-wrap:wrap; }

/* The fourth and last non-crisis container, and it is the same shape a fourth time:
   `a.al-call`, the "call them" pill on the answer-language note on Help, is `display:inline-flex`
   with no `flex-wrap` and no `max-width` in answerlang_2026-08-30.css:48. MEASURED 2026-09-28 at
   320 Extra Large: 302.2 rendered px wide standing 31.4 px past the right edge, in fourteen of the
   nineteen codes, and it takes the layout viewport 320 -> 351 and 568 -> 624 with it. It is the last
   thing moving the dock on that view. Its own rule is `.phone #res-list > .al-note .al-call`,
   (1,3,0), so `a.al-call` in the same shape is (1,3,1) and wins on one element selector - no
   `!important`, and PROVED from the live computed value. AN OVERRIDE, NOT A HOME: these two
   declarations belong on that file's own rule at answerlang_2026-08-30.css:48, and are HANDED OVER
   to its owner to move them there and delete this. Undo: delete these two lines. */
.phone #res-list > .al-note a.al-call{ flex-wrap:wrap; max-width:100%; }
/* ==================================================================================================
   SPACE2 2026-09-28. THE LAST FOUR PIXELS OF THAT PILL, AND THEY ARE THE ONES IN THE BRIEF.
   SPACESCOUT_2026-09-28 section 4 named the element and would not let the folklore stand: it is
   `SPAN.tel-num` holding `(401) 462-4444` inside this `a.al-call`, the magnitude is 4 LOCAL px and
   not 39, the layout viewport is NOT inflated, the dock is NOT pushed off screen, and `SPAN.wt-clue`
   is ruled out. I reproduced it and the number is the same: 4.359px.
   MEASURED, real Chromium 154, 320px, Extra Large, Help, `#res-q` FOCUSED and the suggestion list
   open with nothing typed, which is the exact state SPACESCOUT isolated:
     ru  scrollWidth 324 against clientWidth 320, over 4.359, culprit SPAN.tel-num "(401) 462-4444"
     el  over 0.000   NOT REPRODUCED - said plainly, SPACESCOUT measured 4 here and I measured 0
     pl  over 0.000   NOT REPRODUCED
     en  over 0.000   agrees
   AFTER, same cells: ru 324 -> 320, over 4.359 -> 0.000. The three that already measured 0 stay 0.
   WHY PADDING AND NOT A BREAK. The brief's own words are "give SPAN.tel-num a way to break, or give
   its column 4 px", and it must solve BOTH halves: the number must not split and it must not
   overflow. Giving the column room solves both at once and touches the number not at all.
   A break cannot be used here and the reasons are all on the record: NOT `white-space:normal`, which
   puts back the mid-group hyphen break RTLFIX measured on 15 numbers in Arabic and 20 in English.
   NOT `U+2011`, because RTLSCOUT proved an ASCII hyphen is a European Separator that JOINS two digit
   groups under bidi rule W4 while a non-breaking hyphen splits them, and it would break Arabic.
   NOT `overflow:hidden`, which clips the last four digits of a phone number.
   WHY `.25em` AND NOT A PIXEL COUNT. answerlang_2026-08-30.css:48 sets `padding: 0 .75em` on this
   pill and the pill's font-size computes to 16px, so .75em is 12px a side and .25em is 4px a side:
   16 local px given back for 4 needed, at the two text steps where the number cannot fit, and a
   four-fold margin rather than a number tuned to one cell. `min-height:44px` is NOT touched, so the
   tap target is exactly the 44px floor it was. The pill keeps its border, its radius and its shape.
   PHONE WIDTHS AND THE TWO LARGE TEXT STEPS ONLY. At Normal, and above 430px, this pill has never
   overflowed in any of the 19 codes and it keeps every pixel of its padding.
   ZERO `!important`. That file's rule is `.phone #res-list > .al-note .al-call`, (1,3,0). The
   selectors below are `.phone.text-large #res-list > .al-note a.al-call`, (1,4,1), and they win on
   specificity alone. PROVED from the live computed value, not reasoned about from load order.
   ------------------------------------------------------------------------------------------------
   WHAT IS LEFT AT 320 EXTRA LARGE AFTER THIS AND AFTER `.csv-why`, NAMED, NOT HIDDEN, NOT GUESSED AT.
   With real results on screen, 33 local px of overflow remain and every one of them is `SPAN.tel-num`
   holding a FOURTEEN-CHARACTER number - "(401) 445-4237" 32.797, "(401) 235-6000" 23.297,
   "(401) 309-5220" 18.922 - inside `A.res-call.callfix-primary`, which ALREADY carries
   `flex-wrap:wrap`, `max-width:100%` and `min-width:44px` from DOCK2's fix. The number is on its own
   line inside the button, whole, and IT IS STILL WIDER THAN THE CARD: at `zoom:2` a 320px screen is
   160 CSS px and that string needs about 124 of them before the card's own padding.
   I WROTE A RULE FOR IT AND THEN WITHDREW IT. `.phone .callfix-lead{flex-wrap:wrap}` was measured on
   the same cells in en, ru, el, pl and ar at Large and Extra Large and moved the overflow by EXACTLY
   ZERO px in every one, 79 -> 79 and 29 -> 29. A rule that wins its cascade and changes nothing is a
   green that lies, so it is not on this wall. Three agents withdrew a dead rule tonight rather than
   escalate it and that was right.
   THE HONEST CONCLUSION: at 320px and `zoom:2` a fourteen-character phone number CANNOT be made to
   fit on one line by any stylesheet without doing one of the four things forbidden above or shrinking
   the digits, and shrinking digits is the Greek 7px nav label defect wearing a different hat.
   HANDED OVER WITH THE FIX NAMED: the answer is in app.js, which emits `.tel-num`. If it emitted each
   digit group in its own span - `(401)` `445-4237` - CSS could allow a break BETWEEN the groups and
   forbid one inside them, and the number would set on two lines with every group whole and bidi W4
   still joining the digits. That is a markup change, it is not a stylesheet change, app.js was not
   this job's file, and I am not guessing at it at one in the morning before Autumnfest.
   Undo: delete this media block. */
@media (max-width:430px){
  .phone.text-large  #res-list > .al-note a.al-call,
  .phone.text-xlarge #res-list > .al-note a.al-call{ padding-inline:.25em; }
}

/* ==================================================================================================
 * GOSTANDDOWN 2026-09-28 (DOCK2). LAW L1. THE SEARCH BUTTON WAS SITTING ON TOP OF 911.
 * --------------------------------------------------------------------------------------------------
 * FOUND BY LOOKING AT THE PICTURE, not by a number. MEASURED, Arabic, 320x568, Extra Large text,
 * Help, keyboard up: `button.res-go` occupies y 225 to 321 and the dock's crisis floor starts at
 * y 282, so the GO button covers the top 39 px of all three doors. `elementFromPoint` at the centre
 * of `tel:911` and `tel:988` returned `BUTTON.res-go`, not the doors. The dock gap was 0.00 and
 * every door was 75 px: the geometry was right and the paint order was wrong.
 *
 * I CAUSED THE EXPOSURE AND I AM SAYING SO. On the pre-edit tree this cell passed, but only because
 * the page overflowed sideways and Chromium had widened the layout viewport to 382x679. The dock,
 * being `position: fixed`, pinned itself to the bottom of that phantom 679 and landed at y 343,
 * clear of the GO button at 321 - inside a viewport 61 px taller than the phone. Removing the
 * overflow (the SIDEWAYS block above) gave her back a real 618 and the collision became visible.
 * The occlusion was always there; the overflow bug was hiding it. It is fixed, not reverted.
 *
 * WHY NOT BY RESTACKING, AND I PROVED IT RATHER THAN ASSUMED IT. `.res-search` carries
 * `z-index: 36` and is its own stacking context (app.css:1029), so app.css:1077
 * `.res-search:has(.res-suggest.open){z-index:402 !important}` raises the WHOLE wrapper one above
 * the dock's 401 - deliberately, so the suggestion list, which is a DESCENDANT of that wrapper and
 * carries its own crisis-sug 911 and 988 rows, can paint over the dock.
 * `_HARNESS/FALSIFY_STACKORDER_2026-09-09.mjs` guards exactly that. Lowering `.res-search` would
 * take the LIST down with it and revert a live crisis fix. The list stays on top. The button goes.
 *
 * WHY STANDING THE BUTTON DOWN IS THE RIGHT ANSWER AND NOT A DODGE. `html[data-crisis-slim="1"]` is
 * the state `[RYAN 2026-09-09]` ruled on in his own words - "while typing only" - and this app
 * ALREADY stands a control down in it: bottom_dock_2026-08-15.css's last line is
 * `html[data-crisis-slim="1"] #bw-dock > .nav{display:none}`. While she is typing, the suggestion
 * list is live and re-renders on every keystroke, the keyboard's own Search key still submits, and
 * the field's Enter handler is untouched: the GO button is the one thing in that state with nothing
 * to do, and the one thing standing on 911. It returns the instant she stops typing, with the nav.
 * It also gives the screen space back while the keyboard is up, which is his own ruling.
 *
 * TWO OTHER ANSWERS WERE BUILT, SERVED FROM REPLICAS AND MEASURED BEFORE THIS ONE WAS WRITTEN:
 *   `flex-wrap: nowrap` on the row, so GO stays beside the box - MADE IT WORSE. The row then could
 *   not shrink, the sideways overflow went 348 -> 416 and the viewport 618 -> 739, and `sms:988`
 *   stopped hit-testing to itself. Rejected on the measurement.
 *   Leaving it and reporting it - rejected. It is a crisis door under a button.
 * MEASURED AFTER, same cell: 911, 988 and text-988 all hit-test to themselves, all 75 px, gap 0.00,
 * and every other number in the cell is byte-identical to before (innerWidth 348, innerHeight 618,
 * list 74.9 px, 3 rows). Nothing but the button moved.
 *
 * THIS IS A VISIBLE CONTROL DISAPPEARING WHILE SHE TYPES AND IT NEEDS RYAN'S EYE. It is written
 * because the alternative is a covered 911, and L1 outranks a button. If he rules the other way the
 * undo is this one line and the answer is then the Extra Large search-box height, app.css:1548.
 * Undo: delete the rule below. */
html[data-crisis-slim="1"] .phone .res-search-row > .res-go{ display: none; }

/* WRITTEN, MEASURED, FOUND DEAD, AND WITHDRAWN, 2026-09-28. I added
     .phone a.wf-tel{ flex-wrap:wrap; max-width:100%; }
   for the last 4 px of layout-viewport growth at 320 Extra Large (320 -> 324 in Spanish and Polish
   once `.al-call` above is fixed). It won its cascade and changed NOTHING: `.phone` scrollWidth
   stayed 162 against clientWidth 160 and innerWidth stayed 324, identical to the byte before. The
   remaining 2 local px are not `a.wf-tel`'s. A rule that measures the same with and without it is a
   rule that does not belong on this wall, so it is recorded here and not shipped. The last 4 px are
   NOT FIXED and are named in the report. */

/* NOT TOUCHED, NAMED AND HANDED BACK: `button.cq.more`, app.css:2257, the "more" chevron on the
   crisis accordion. MEASURED 2026-09-28, GREEK at 320 wide at NORMAL text on Help: it is 163.7 px
   wide and stands 14.05 px past the right edge, and it takes the layout viewport 320 -> 334 and
   568 -> 593 with it, which moves the dock. It is the last remaining sideways overflow after this
   block. IT IS ON THE CRISIS BAR. `.res-crisis-acc .cq` is the same rule family as `.cq-911`,
   `.cq-988` and `.cq-988t`, its `padding: 8px 15px` and `flex: 0 0 auto` are what give it its tap
   target, and LAW.md L1 puts a crisis door's box out of reach of a layout agent working without a
   ruling. The one-line repair looks like `flex-wrap: wrap` or a shorter Greek label, and either is
   a change to a crisis surface. FOR RYAN'S EYE, NOT FOR MINE. */
.upd-act:active{transform:translateY(1px)}
.phone.dark .upd-act{background:transparent}
/* CIVIC LANDING PAGES (051): the auto-linked term + the landing-page sheet. Every-city, no per-city override. */
.civic-link{display:inline;padding:0;margin:0;border:0;background:none;font:inherit;color:var(--navy);font-weight:700;text-decoration:underline;text-underline-offset:2px;cursor:pointer;line-height:inherit}
.civic-link:hover{color:var(--navy-deep)}
.civic-link:focus-visible{outline:2px solid var(--navy);outline-offset:2px;border-radius:3px}
.phone.high-contrast .civic-link{color:var(--navy)}
.civic-emerg{background:var(--red-soft);border-inline-start:5px solid var(--red);border-radius:10px;padding:10px 12px;margin:2px 0 12px;font-size:14px;line-height:1.45;color:var(--ink);font-weight:600}
.civic-sec{margin-bottom:12px}
.civic-sec .civic-h{font-family:var(--font-label);font-weight:700;text-transform:uppercase;letter-spacing:.05em;font-size:12px;color:var(--muted);margin-bottom:3px}
.civic-sec .civic-b{font-size:15px;line-height:1.5;color:var(--ink)}
.civic-place{background:var(--canvas);border:1px solid var(--border);border-radius:var(--radius);padding:10px 12px;margin-bottom:8px;font-size:14.5px;line-height:1.45;color:var(--ink)}   /* 2026-09-18 W3.1-25: was border-radius:10px */
.civic-place .civic-note{color:var(--muted);font-size:13px}
.civic-acts{display:flex;flex-direction:column;gap:8px;margin:6px 0 10px}
.civic-acts .civic-act{min-height:48px;display:inline-flex;align-items:center;justify-content:center;text-decoration:none;text-transform:none;letter-spacing:normal;font-size:15px}
.civic-fresh{font-size:12.5px;color:var(--muted);margin-top:6px}
.civic-stale{background:var(--gold-soft);border-inline-start:5px solid var(--gold);border-radius:var(--radius);padding:10px 12px;margin:8px 0 2px;font-size:13.5px;line-height:1.5;color:var(--gold-ink);font-weight:600}   /* 2026-09-18 W3.1-25: was border-radius:10px */
.civic-stale a{color:var(--navy);font-weight:700;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}
.civic-place .civic-dir{display:inline-block;margin-top:4px;color:var(--navy);font-weight:700;font-size:13.5px;text-decoration:underline;text-underline-offset:2px}

/* WEBSITE WIDTH (2026-07-30, SUPERSEDES the 083 DESKTOP COLUMN): Ryan's evolved intent - the app must read as a
   real website on desktop, not a narrow phone column floating in huge empty margins. The 083 fix capped the whole
   .phone shell to 620px, which STRUCTURALLY DEFEATED the session-050 website-width view rules (#view-* max 1140/1200)
   because a 620px parent can never let a child reach 1140. So: the shell fills the viewport (it is the page), and the
   CONTENT VIEWS are the centered website columns (1140 -> 1200). The chrome bars align to that content width; the
   compact bottom nav + crisis bar stay a comfortable centered width so buttons keep a tappable size. Mobile (<640px)
   is UNCHANGED. System fix, holds for every city. NEWEST DATED TRUTH WINS. */
@media (min-width:640px){
  .phone{max-width:none;margin:0;box-shadow:none}
  html,body{background:var(--canvas)}
}
@media (min-width:640px) and (prefers-color-scheme:dark){
  html,body{background:#0D2018}   /* 2026-09-18 W3.2: was #0e1013; Ink */
}
/* keep the desktop gutter matched to the chosen theme (explicit Light/Dark override the auto rule above) */
html:has(.phone.dark),body:has(.phone.dark){background:#0D2018}   /* 2026-09-18: was #0e1013; Ink, the dark ground */
html:has(.phone.appearance-set:not(.dark)),body:has(.phone.appearance-set:not(.dark)){background:#EDE7D9}   /* 2026-09-18: was #c9d2db (blue-grey); Cream Deep */

/* 5.6 refine (mirror): website-width (edit D) stretched the mayor photo and the About card full-column on desktop. Cap them to a readable, centered size so the About page looks professional, not a giant banner. Phone (<640) unchanged. */
@media (min-width:640px){
  .mayor-card{max-width:520px;margin-left:auto;margin-right:auto}
  .mayor-photo{max-width:340px;margin-left:auto;margin-right:auto}
}

/* ==== BRAIN 5.9 JOB#1 POLISH (mirror preview, reversible: delete this block) ==== */
/* 2026-09-18 W3.1-20: placeholder was #3f4a56. var(--muted) is 6.66 on white light, 7.99 on navy dark. !important kept (it already beat a dated sheet). */
.search input::placeholder, input::placeholder { color:var(--muted) !important; opacity:1 !important; }
/* 2026-09-18 W3.1-17: was font-size:13px;color:#1c2530;letter-spacing:.09em, all !important. The section label is now the guide's
   opener (see .section-label near the top of this file); this line kept only the size floor and deferred colour and tracking to it.
   VOICE 2026-09-20: SUPERSEDED AND REMOVED. The line read `.section-label { font-size:12px !important; }` and it is kept here
   verbatim so it can be put back. It was already doing nothing: `.section-label` at app.css:166 sets font-size:12px at the same
   specificity, and the only later sheet that ever raised it, `.phone.hv3 .home-sec .section-label{font-size:13px !important}` in
   home_variants_2026-08-23.css, was retired on 2026-09-18 and its rule body is empty (that file, line 519). MEASURED before and
   after in a real browser on all three Home labels and on Events, News and Settings: computed font-size 12px, unchanged.
   One !important removed at source, none added. */
.chip { padding:4px 10px !important; font-weight:800 !important; }
/* 2026-09-18 W3.1-5: was youth #9CF000/#1a2200, family #F0CC00/#2a2100, music #9000E4/#fff, city #0048B4/#fff, seniors #0E7C86/#fff.
   Gold badge, Deep Navy text 5.35. !important kept as it was. */
.chip.youth,.chip.family,.chip.music,.chip.city,.chip.seniors,.chip.arts { background:var(--gold) !important; color:var(--bw-navy) !important; }   /* 2026-09-24 DATAFIX: .chip.arts added, see the note at the sibling rule above. The !important is pre-existing and is kept exactly as it was. */
.fh-cat:not(.active){ border-color:#8b95a0 !important; border-width:1.5px !important; }
/* ==== END 5.9 POLISH ==== */

/* ===== 6.02 PUNCH-LIST CANDIDATES (session 6.02, LOCAL RENDER ONLY, not shipped) ===== */
/* #10 Settings language picker: clean uniform 2-col grid, not ragged wrapping chips */
#lang-seg{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;border:none;border-radius:0;overflow:visible;background:none}
#lang-seg .seg-btn{flex:none;border:1.5px solid var(--navy);border-radius:var(--radius);border-inline-end:1.5px solid var(--navy);padding:14px 12px;min-height:54px;font-size:15px;display:flex;align-items:center;justify-content:center;gap:6px;line-height:1.2;text-align:center}   /* 2026-09-18 W3.1-25: was border-radius:12px */
#lang-seg .seg-btn:last-child{border-inline-end:1.5px solid var(--navy)}
/* #11 hide the dev-facing draft caption line; the honest per-language dot on the button stays */
#lang-draft-note{display:none !important}
/* #8 Find Help "kinds of help" chips (.help-cats): even 2-col cards, matches the language grid, last spans full width */
.help-cats{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin:2px 0 16px}
.help-cat{background:var(--surface);color:var(--navy);border:2px solid var(--navy);border-radius:var(--radius);padding:12px;font-size:15px;font-weight:600;font-family:var(--font-sans);min-height:52px;   /* 2026-09-18 W3.1-24: was 1.5px, radius 12px, 14px 700 */text-align:center;display:flex;align-items:center;justify-content:center;white-space:normal;line-height:1.2}
.help-cats .help-cat:last-child:nth-child(odd){grid-column:1 / -1}
/* #7 211 box: badge the number, tighten, keep it solid green, literal + reachable */
.res-211{align-items:center;gap:14px;border-radius:16px;padding:16px}
.res-211 .big{font-size:24px;background:rgba(255,255,255,.15);border-radius:12px;padding:10px 12px;line-height:1;min-width:58px;text-align:center}
.res-211 a{margin-top:10px}

/* #4 Updates page: editorial card (outlined category tag, clean hierarchy) instead of the loud left-border template card */
/* NEWSRULE 2026-09-23. THE 3px LEFT RULE, THE GUIDE'S ONE PRESCRIBED VISUAL SYSTEM.
   FINAL_2026-09-23 section 8 items 4 and 5: `border-left-width` measured 0px on every .upd-card in a
   real browser, where BRANDSPEC's News section asks for "a 3 px left rule colour-coded ONLY here:
   #7A2636 for URGENT (Burgundy is the signature and carries urgency without being red), #102F3D for
   everything else", and A7 measures the guide's own blockquote rule at 3px. .upd-card is the card on
   BOTH screens (News #updates-list and Home #home-goodnews), so one declaration closes both items.
   The colour is a variable, not a hex, because dark mode has to move it: in dark the card surface IS
   Deep Navy #102F3D, so a navy rule on a navy card is invisible and Burgundy on it is 1.5:1. In dark
   both rules go Community Gold, which is exactly what the app already does one screen above, at
   `.phone.dark #updates-list .upd-sec{color:var(--bw-gold)}`: the dark theme collapses the urgent
   distinction into gold and keeps the WORD "Urgent" to carry the meaning. Following the file's own
   precedent rather than inventing a second one.
   3px is --rule-w, already declared at the top of this file. No new hex, no new file.
   Undo: delete the border-left declarations and the three #updates-list rules below. */
.upd-card{padding:20px 20px 16px;border:0;border-radius:var(--radius);margin-bottom:14px;background:var(--surface);box-shadow:none;--upd-rule:var(--bw-navy);--upd-rule-urgent:var(--bw-burgundy);border-left:var(--rule-w) solid var(--upd-rule)}   /* 2026-09-18 W3.1-24: was 16px pad, 1px border, radius 16px */
.phone.dark .upd-card{border:1px solid var(--border);--upd-rule:var(--bw-gold);--upd-rule-urgent:var(--bw-gold);border-left:var(--rule-w) solid var(--upd-rule)}
/* Urgency is carried by the SECTION HEADING, not by a class on the card: app.js writes
   `.upd-sec.upd-sec-urgent` then the cards that belong under it as following siblings. So the cards
   that are urgent are the ones after an urgent heading and before the next heading, and that is
   exactly what these three rules say, in rising specificity. Rule three is (1,3,0) and beats rule
   two at (1,2,0), so a card under "Affects you" that happens to follow "Urgent" earlier in the list
   falls back to navy. No markup change, no JS change, no new class. */
#updates-list .upd-card{border-left-color:var(--upd-rule)}
#updates-list .upd-sec-urgent ~ .upd-card{border-left-color:var(--upd-rule-urgent)}
#updates-list .upd-sec-urgent ~ .upd-sec ~ .upd-card{border-left-color:var(--upd-rule)}
.upd-head{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin-bottom:8px}
/* 2026-09-18 W3.1-6: was color/border var(--tagc,#2C6FB5), radius 999px, letter-spacing .06em. An eyebrow in the label face,
   Burgundy on white 9.73. app.js line 1275 (UPD_CATS) still writes --tagc inline as blue #2C6FB5 and, for Alert, red
   #C0392B; this rule no longer reads it, so the eyebrow is Burgundy in every category. The ALERT eyebrow is never red. */
/* EYEBROW 2026-09-23. ALERT STOPS BEING A BOX. FINAL_2026-09-23 and BRANDFINISH_2026-09-23 both
   flag this one: "News's ALERT is still a bordered box." BRANDSPEC's Home entry writes the category
   as a bare eyebrow, not a chip: "category eyebrow ('ALERT') in condensed caps 11 px #7A2636", and
   this file's own line 245 already states the template's rule in its own words, "the guide's
   template's eyebrow: no tinted box, label face, Burgundy on white 9.73". The border and the
   horizontal padding are the last two declarations making it a box, so they go. The face, the
   weight, the tracking, the caps, the colour and the zero-tracking pair for Arabic, Khmer, Lao and
   Bengali are all unchanged, and the size stays 12px rather than dropping to the spec's 11px
   because 12px is this app's own hard floor and a gate enforces it.
   The card's left rule, built on 2026-09-23, is what carries the urgency now, in Burgundy for
   urgent and Deep Navy otherwise, which is the guide's blockquote idiom and the whole card system
   for this view. A second bordered rectangle inside a card that already has a rule reads as two
   frames, not one. Red never enters either: app.js's UPD_CATS still writes --tagc inline and this
   rule still does not read it.
   UNDO: put `border:1.5px solid var(--navy);border-radius:var(--radius);padding:3px 11px` back.
   Superseded, kept verbatim:
   .upd-tag{...color:var(--navy);background:transparent;border:1.5px solid var(--navy);border-radius:var(--radius);padding:3px 11px;line-height:1.5} */
.upd-tag{display:inline-block;font-size:12px;font-weight:700;font-family:var(--font-label);letter-spacing:var(--track-label);text-transform:uppercase;color:var(--navy);background:transparent;border:none;padding:3px 0;line-height:1.5}
.upd-tag:lang(ar),.upd-tag:lang(km),.upd-tag:lang(lo),.upd-tag:lang(bn){letter-spacing:0}
.upd-date{color:var(--muted);font-size:13px;font-weight:600}
.upd-body{font-size:16px;line-height:1.5;color:var(--ink)}
/* 6.14 News Layout A: calm section headers + restrained source links. Red stays reserved for crisis, never news. */
/* 2026-08-10, RYAN: "news is janky as fuck". He was looking at it on a wide screen and he
   was right. These section headings are DIRECT CHILDREN of the updates grid, which is three
   columns wide, and their grid-column was auto. So each heading ate ONE CELL instead of
   spanning the row: URGENT sat in column 1, the alert card landed in column 2, and AFFECTS
   YOU floated in column 3 with nothing underneath it. Headings beside their cards instead of
   above them.
   WHY NOBODY CAUGHT IT: on a phone the grid collapses to one column and the bug is
   invisible. Camela never sees it. Every check we run is phone-shaped, and every render we
   verified was phone-shaped, so a desktop-only layout break had no way to be seen. The city
   editor and the mayor look at this on a desktop.
   Proved in his own browser before it was written here: with this rule the three headings go
   full width at y=315/581/894 and every card sits underneath its own heading. */
.upd-sec{grid-column:1 / -1;font-size:12px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);margin:20px 2px 10px}
.upd-sec:first-child{margin-top:4px}
.upd-sec-urgent{color:var(--ink)}
.upd-src-row{margin-top:10px}
.upd-src{display:inline-block;font-size:14px;font-weight:700;color:var(--navy);background:transparent;   /* 2026-09-18 W3.1-6: was #2C6FB5 */border:0;padding:4px 0;cursor:pointer;text-decoration:none;font-family:inherit}
.upd-src:hover{text-decoration:underline}
.upd-acts{margin-top:12px}

/* #2 nav: the OPEN tab is color, the rest black & white; Help stays crisis-color always (Law #1) */
/* 6.15 (2026-08-06) ROOT-CAUSE FIX: .navico had NO size cap and there is no global img{max-width}, so each of the
   5 nav icons (nav_color.png) painted at full intrinsic size, burst the 38px .ic slot, and the overlapping copies
   read as one giant emblem smeared over the fixed nav + the feed. Cap the icon AND clip the slot (.nbtn .ic now has
   overflow:hidden) so no image can ever burst the nav again, every city, forever. Guarded by falsify_nav_icon_sizing.js. */
/* 7.00 (2026-08-07): RECONCILED TO RYAN'S ENDORSED MIRROR-PHONE BUILD. That build uses the FILE
   mechanism, nav_bw.png on four tabs and nav_color.png on Help, and carries ZERO .navico rules of its
   own; sizing comes from brand.css. The 6.15 grayscale filter was a second mechanism doing the same job
   and it ALSO coloured the active tab, which his build does not. Filter retired, size cap kept so no
   image can burst the nav again. Undo: restore the three rules below this comment from git edcb052. */
.navico{width:28px;height:28px;object-fit:contain;display:block}

/* #5 dark banner fit: frame the white banner as a clean rounded card on a deep-navy header, not a bare white block */
.bw-banner{padding:12px 12px 0}
.bw-banner img{display:block;width:100%;height:auto;border-radius:var(--radius)}   /* SHAPE 2026-09-22: was border-radius:16px */
.phone.dark .hdr{background:var(--bw-burgundy)}   /* 2026-09-18 W3.2: was #16233a; the masthead stays Burgundy in dark (BRANDSPEC Settings section), cream 9.02 */
.phone.dark .bw-banner img{box-shadow:0 0 0 1px rgba(255,255,255,.10)}

/* #9 Home single swipeable headline carousel (ranked + auto-culled by updates_engine) */
.hl-nav{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:12px}
.hl-arrow{border:1.5px solid var(--navy);background:var(--surface);color:var(--navy);border-radius:var(--radius);min-width:44px;min-height:44px;font-size:22px;font-weight:700;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}   /* 2026-09-18 W3.1-25: was border-radius:10px */
.hl-dots{display:flex;gap:7px;align-items:center;flex-wrap:wrap;justify-content:center}
.hl-dot{width:8px;height:8px;border-radius:50%;background:var(--border)}
.hl-dot.on{background:var(--navy)}
.hl-foot{margin-top:12px}
.phone.dark .hl-arrow{background:transparent;color:var(--navy);border-color:var(--navy)}   /* 2026-09-18 W3.2: was #9cc2ec/#3f6fa6; gold on navy 5.35 */

/* ===== BRAIN 6.03 punch items #1 + #6 ===== */
/* #1 UNIFIED SEARCH result grouping */
.res-grouphdr{font-weight:800;color:var(--navy);font-size:12.5px;letter-spacing:.05em;text-transform:uppercase;margin:10px 2px 8px;opacity:.85}
.res-citysvc{margin-top:4px}
/* ONE CITY-SERVICE ROW, 2026-09-04. `[RYAN 2026-09-04]` "I noticed the info page doesnt really match
   the vibe of the help page."
   `.csv-item` is emitted by exactly one function, citySvcCard() in app.js, and it renders on BOTH
   pages: on Info inside #csv-list, and on Help inside #res-list under "City services" (app.js:2101).
   One component, one emitter, two pages. Until today it also had TWO stylesheets - this rule scoped
   `.res-citysvc`, and a byte-for-byte duplicate of it scoped `#citysvc` in index.html's own <style>
   block - and once there are two copies of a declaration block, drift is not a risk, it is a
   schedule. It had already happened: tapsize_2026-09-01.css raised the Info copy to 56px and the
   Help copy stayed at 48, so the SAME ROW was two different heights depending on which page you
   found it on. Measured 2026-09-04 by FALSIFY_VIBE_2026-09-04.mjs: minHeight 48 vs 56, fontSize 15
   vs 16, radius 12 vs 13, padding-left 14 vs 13, margin-bottom 8 vs 9.
   The selector is now unscoped, which is the whole point: wherever this row renders, it is this row.
   NUMBERS: 56px min-height keeps the tap target tapsize_2026-09-01.css won on the Info side (never
   regress a tap target; `[RYAN 2026-09-02]` "Keep 44px. Screen space matters more." - 56 clears it),
   16px type is his floor, 13px radius and 12px/13px padding and 9px gap are `.res-item`'s own
   numbers so a city row sits in the same rhythm as a help card.
   Undo: app.css.bak_2026-09-04_vibe. */
.csv-item{display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:56px;padding:12px 13px;margin:0 0 9px;border:1px solid var(--border);border-radius:var(--radius);text-decoration:none;color:var(--navy);font-size:16px;font-weight:600;line-height:1.3;background:var(--surface)}   /* 2026-09-18 W3.1-25: was border-radius:13px */
/* 2026-09-04, REAL PHONE, TAP FEEDBACK, same reason as .res-suggest li above: these are the Info
   page's city-service rows, and on a phone they were the only tappable rows in the app that gave
   no press feedback at all. `[RYAN 2026-09-04]` flagged "the Info page not matching the vibe of
   Help" from his own handset; this is one measurable part of that difference, not the whole of it.
   Undo: delete `.csv-item:active,` from the selector list. */
.csv-item:hover,.csv-item:active{border-color:var(--navy);background:var(--sky-soft)}

/* ================= 2026-09-04, REAL PHONE: A PRESS HAS TO LOOK LIKE A PRESS =================
   FOUND ON A REAL DEVICE PROFILE, NOT IN A SCREENSHOT. Five places in this app switch off the
   browser's own tap highlight with `-webkit-tap-highlight-color: transparent` and then define no
   `:active` state to replace it, so on a phone a press produces NOTHING between her finger landing
   and the view changing:
     firstscreen_2026-08-28.css:116,135  .fs-chip / .fs-more  - the home-screen crisis strip
                                         COUNTED LIVE 2026-09-04: 0 of these exist on the wall
                                         today; that strip is retired (HOME_CRISIS_STRIP_RETIRED
                                         in firstscreen_2026-08-28.js) and its CSS is still shipped.
                                         The two selectors are carried so the rule is already right
                                         if it ever comes back, and are honestly a no-op right now.
     COUNTED LIVE 2026-09-04, same run:  13 .hp-door, 7 #citysvc .csv-accb, 5 .nbtn - those 25
                                         controls are what this rule actually reaches today.
     helppage_2026-08-27.css:135         .hp-door, the twelve Help doors
     helpfold_2026-08-28.css:236         .hp-door again, the same element in its pill shape
     index.html:803                      #citysvc .csv-accb, the Info page's city-service rows
     app.css:777                         .nbtn, the five nav tabs
   Counted 2026-09-04: those four files carry 5 tap-highlight kills and 0 `:active` rules between
   them (home_news_variants.css is the one that already does this properly, .ndb-arrow:active).
   Each of them DOES define `:focus-visible`, and `:focus-visible` DOES NOT FIRE ON A TOUCH - that
   is the whole point of `-visible`. So every one of these was feedback for a keyboard and a mouse,
   and none of it for the only input device she has.
   WHY IT MATTERS MORE THAN IT SOUNDS: with no press feedback the natural human response to a
   half-second of nothing is to press again. On a `tel:` door a second press is a second dial.
   WHY OPACITY AND NOT A COLOUR. `LAW.md` L1: red is reserved for crisis. A press state that
   invents a background colour would either put a new colour on a crisis control or need a
   different colour per control. Opacity dims whatever is already there, invents nothing, changes
   no layout, moves nothing, and reads identically on the red crisis chips, the coloured Help doors
   and the plain Info rows. One rule, every city, forever (L18).
   Deliberately NOT `!important` and deliberately no geometry: if any of those files later grows
   its own richer `:active`, that one wins on order and this becomes a no-op.
   Undo: delete this block. */
.phone .fs-home-crisis .fs-chip:active,
.phone .fs-home-crisis .fs-more:active,
.hp-door:active,
#citysvc .csv-accb:active,
.nbtn:active{
  opacity:.78;
}
@media (prefers-reduced-motion: reduce){
  /* nothing here animates, so reduced motion changes nothing - stated rather than assumed, because
     "we checked reduced motion" with no rule behind it is exactly the envelope-check L12 forbids */
}
.csv-arrow{color:var(--muted);font-size:15px;flex:0 0 auto}
.phone.dark .csv-item{color:var(--ink)}   /* 2026-09-18 W3.2: was #dfe7f2; cream 13.01 on navy */
.phone.dark .res-grouphdr{color:var(--ink)}   /* 2026-09-18 W3.2: was #cfe0f5 */
/* #6 MOBILE SEARCH COMPACT MLK: fold the header banner away while the Help search is focused, so the phone
   keyboard does not bury the results. Reversible (remove these two rules + the focus/blur wiring in index.html). */
.hdr .bw-banner{transition:max-height .22s ease, padding .22s ease, opacity .18s ease; max-height:420px; overflow:hidden}
.hdr.search-compact .bw-banner{max-height:0; padding-top:0; padding-bottom:0; opacity:0}
/* SUPERSEDED 2026-08-07 (7.00): THIS COMMENT WAS NEVER TRUE IN THE RENDER. brand.css:3 sets
   .hdr-row{display:none !important} and loads AFTER app.css, so the wordmark row has never been visible
   in either build. Ryan's words, 618:186: "i never wanted the wordmark, where the fuck did that come
   from?" Branding lives IN the banner art. Historical note follows, kept for the record only.
   6.17 (2026-08-06) claimed: WORDMARK RESTORED per Ryan. The 6.15 rule hid the visible ".hdr-row" (BW mark + BELOVED
   WOONSOCKET + tagline "Your community, in one place") as a screen-reader-only "double-header fix". Ryan never
   asked to remove the wordmark (DECIDED 2026-08-01 = drop only the hero pic/time/weather; wordmark was a
   show-both, Ryan-chooses item) and he objected when it was hidden. His endorsed mirror-phone build shows the
   banner AND the wordmark row together. So the hide rule is removed: banner + wordmark both show, matching the
   look he approved. Superseded 6.15's hide (newest dated truth wins). */

/* #3 officials roster: photo-ready rows with dignified initials avatars (no fake faces) */
.off-row{display:flex;align-items:center;gap:12px;padding:10px 0;border-top:1px solid var(--border)}   /* 2026-09-18 W3.1-24: was #eef1f5 */
.off-ava{flex:0 0 auto;width:64px;height:64px;border-radius:50%;background-size:cover;background-position:center;background-repeat:no-repeat;box-shadow:0 0 0 1px rgba(0,0,0,.06)}
.off-ava-mono{display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:16px;letter-spacing:.02em}
.off-meta{flex:1 1 auto;min-width:0}
.off-name{font-weight:700;font-size:14.5px;line-height:1.3;color:var(--navy)}
.off-role{font-size:13px;color:var(--muted)}
.off-acts{font-size:13px;margin-top:4px}
/* ==============================================================================================
   BW-07, THE LAYOUT HALF, 2026-09-24. TWELVE OFFICIALS' ROWS OVERFLOWED BY THE SAME SEVEN PIXELS.
   ----------------------------------------------------------------------------------------------
   Measured: `.off-row`, `.off-meta` and `.off-acts` all report scrollWidth 331 inside clientWidth
   324 on the Arabic Info page, 390px, Normal. The same seven pixels on all three, because all
   three are the same overflow seen from three heights of the same box.
   WHERE THE SEVEN PIXELS COME FROM. app.js writes `margin-right:14px` INLINE on every
   Call / Email / Website anchor in this roster, including the last one in the row, and this file's
   own tap rule twenty lines down adds `margin:0 -8px` to pull the 8px tap padding back out of the
   text flow. On the last anchor the inline 14px has nothing after it to space, so it becomes 14px
   of hard width past the content edge, and the -8px claws only 8 of it back. 14 - 8 = 6, plus
   sub-pixel rounding on an RTL line box = the 7 that was measured.
   `margin-right` is INLINE, so a stylesheet cannot reach it without `!important`. That is what the
   `!important` below is for, and it is the only one in this block.
   Turning the row into a wrapping flex line replaces the margin spacing with a real gap, which
   also means a long Arabic or Russian label wraps to a second line instead of running out of the
   card. Same words, same order, same hrefs, same colours: `margin-inline-end` is the only
   declaration that changes. THE JS FIX, HANDED BACK, NOT MADE HERE: drop `margin-right:14px` from
   the inline style app.js writes on these anchors; then this rule's `!important` can go too.

   2026-09-24 DATAFIX, THE HANDBACK TAKEN AT SOURCE. `margin-right:14px` is now gone from the inline
   style app.js writes on these anchors, so the `!important` rule that used to sit on the line below
   this one has been DELETED rather than left to fight a style that no longer exists. What it read:
     .off-acts > a{margin-right:0 !important;margin-inline-end:0 !important}
   The 6px gap is drawn by the `column-gap` below, which is the whole point: one place, no override.
   ============================================================================================== */
.off-acts{display:flex;flex-wrap:wrap;align-items:center;column-gap:6px;row-gap:2px}
.phone.dark .off-row{border-top-color:rgba(255,255,255,.08)}
.phone.dark .off-name{color:var(--ink)}   /* 2026-09-18 W3.2: was #e7eef8 */
.phone.dark .off-ava{box-shadow:0 0 0 1px rgba(255,255,255,.12)}

/* #3 revised: official photo avatars only; rows with no official photo simply have no avatar */
img.off-ava{flex:0 0 auto;width:64px;height:64px;border-radius:50%;object-fit:cover;box-shadow:0 0 0 1px rgba(0,0,0,.08);background:var(--bw-cream-deep)}   /* 2026-09-18 W3.1: was #eef1f5 */
.phone.dark img.off-ava{box-shadow:0 0 0 1px rgba(255,255,255,.12);background:var(--bw-cream-deep)}   /* 2026-09-18 W3.2: was #1d2740 */
.off-row-nopic{gap:0}

/* ============================================================================================
 * C1 6.06 (2026-08-05, on-computer run) - ACCESSIBILITY READABILITY FLOOR toward 16px.
 * Surgical, additive override block at end-of-file (wins by source order for equal specificity).
 * Raises ONLY the named non-crisis readable text an older adult must read. The sacred crisis
 * selectors (.res-crisis*, .callbtn, .cpill, .rrh-crisis, .res-211, .cbnote, .res-crisis-acc) are
 * DELIBERATELY UNTOUCHED - crisis floor stays literal and identical. Micro uppercase tags/badges
 * left as-is (legible; high overflow risk if enlarged). Proven: Home sub-16px text 33 -> 16 with
 * ZERO horizontal overflow across all 16 languages incl km/lo/bn tall + ar RTL (measure_a11y sweep).
 * REVERT: delete this whole block, OR restore from _A11Y_BACKUPS_6.06/app.css.bak_6.06_20260805T0500Z.
 * ============================================================================================ */
.quick .ql{font-size:16px}
.res-item .rw{font-size:16px}
.res-item .ra{font-size:16px}
.res-actions a,.res-actions button{font-size:16px}
.res-sub-h{font-size:16px}
.help-cta-s{font-size:16px}
.findnow .s{font-size:16px}
.privacy{font-size:16px}
.event-row .meta{font-size:16px}
.event-row .ev-preview{font-size:16px}
.great .gt-sub{font-size:16px}
.great .gt-txt{font-size:16px}
.featured p{font-size:16px}
.greet .date{font-size:16px}
.potm{font-size:16px}
.hero-time{font-size:16px}
.hdr-gear,.gear,[aria-label="Settings"]{min-height:44px;min-width:44px}

/* ============================================================================================
 * D3 nav 6.06 (2026-08-05) - ACTIVE NAV TAB indicated by SHAPE + WEIGHT, not color alone (WCAG 1.4.1
 * Use of Color). aria-current="page" is already set on the active .nbtn by showView(). Indicator color is
 * a NEUTRAL navy (light) / ink (dark) - deliberately NOT red, so red stays reserved for the crisis path.
 * Crisis floor untouched. REVERT: delete this block, or restore _A11Y_BACKUPS_6.06/app.css.bak_6.06_20260805T0625Z_prenav.
 * ============================================================================================ */
/* RETIRED 2026-08-12. THIS WAS THE "WEIRD RECTANGLE".
   [RYAN 2026-08-12] "the nav buttons have a weird rectangle that i want to discuss" and
   "redo them when we first did them ... it was so much better than it is now."
   He was right twice. The 2px navy outline and the 4px top bar do NOT exist in the build he
   endorsed (MIRRORPHONE_EXACT_2026-08-06: I grepped its app.css, this block is not in it; it was
   written 2026-08-05, a day after that build). And their entire authority is the single word
   "go" at 605.5overnight.txt:500, typed in reply to a message offering SEVEN different concepts,
   after which the session picked this one itself. That is not a ruling, and one day later the
   same session filed "the nav selected-tab treatment" as an eye-only call reserved for him.
   WHAT IS KEPT AND WHY: font-weight:800 on the active tab stays. WCAG 1.4.1 says the current tab
   must not be signalled by colour alone, and weight is not colour. It is the smallest honest
   indicator, and the endorsed build has no indicator at all, so this is the one place this
   restoration deliberately does not match it. One line to drop if he wants it gone.
   REVERT THE WHOLE RETIREMENT: uncomment the three rules below. */
.nbtn.active{position:relative;font-weight:800}
/* .nbtn.active::before{content:"";position:absolute;top:0;left:16%;right:16%;height:4px;border-radius:0 0 4px 4px;background:#1F3550} */
/* .nbtn.active{outline:2px solid #1F3550;outline-offset:-3px;border-radius:12px} */
/* .phone.dark .nbtn.active::before{background:#eaf0fb} */
/* .phone.dark .nbtn.active{outline-color:#eaf0fb} */

/* ==============================================================================================
 * THE HELP PAGE: ACCESSIBLE AND SLEEK. 2026-08-12.
 *
 * [RYAN 2026-08-12] "I want it set up in a way that is accessible and sleek... just give me your best"
 * [RYAN 2026-08-04] "the food a place to stay health and how you feel etc buttons are too big and
 *                    weird and not up to par" and "the 211 box looks like shit"
 *
 * NOTHING MOVES. The order of the page is his and he ruled on it having seen the alternative:
 * search, crisis, 211, kinds-of-help, results. This block changes only how two things LOOK.
 * No selector below touches .res-crisis, .crisis-bar, .cq, .cpill, .cbtn, .copt or the fixed bar.
 *
 * WHY IT IS LAST IN THE FILE. `app.css` line 714 opens a block headed "6.02 PUNCH-LIST CANDIDATES
 * (session 6.02, LOCAL RENDER ONLY, not shipped)" and THAT BLOCK NEVER CLOSES, so 34 unconditional
 * rules a session marked as not shipped currently win the cascade, including the last definitions of
 * .help-cat and .res-211. That is almost certainly what he has been looking at and calling weird.
 * Those rules are NOT deleted, because some of them may be things he liked and 34 is too many to rip
 * out on one session's reasoning. This block sits after them and settles these two components in ONE
 * place. OI-262 holds the question of what to do with the rest.
 *
 * WHAT DECIDED THE NUMBERS, so the next session does not have to guess and does not have to trust me:
 *  - 44x44 CSS px is WCAG 2.5.5 Target Size (Enhanced), AAA. It is the floor, not the target.
 *  - Touch error rate is about 3% at 44px against about 15% at 24px. 48-60px is the range the
 *    platform guidance calls comfortable for primary actions. These are 60px. Camela is one-finger
 *    typing on a cracked phone in a hard moment, so this page takes the top of that range.
 *  - 12px gaps. WCAG 2.5.8 does not prescribe a spacing number; this is a design heuristic and is
 *    named as one rather than dressed up as a standard.
 *  - 16px text on the buttons. Under 16px both reads badly and makes iOS zoom the page on focus.
 *  - Navy #2C4A6E on white is 9.08:1, which is AAA for contrast. It was already right. Kept.
 *  - Their own checker, _TOOLS/falsify_i18n_a11y.py, enforces min-height >= 44 on .help-cat and
 *    requires a :focus-visible rule to exist. Both are satisfied deliberately, not by luck.
 *
 * THE SHAPE IS NOT MINE. IT IS HIS, OUT OF THE BUILD HE ENDORSED, AND THIS IS THE WHOLE POINT.
 * `C:\Users\macle\Pictures\Woonsocket_App\_RECOVERY_2026-08-06\MIRRORPHONE_EXACT_2026-08-06.zip`
 * is the build he said was "so much closer to what i want". That folder is NOT inside C:\!Brain, so
 * no session could see it. He asked "Are you not seeing my brain? There were several other folders",
 * it was attached, and the zip was opened for what the record says is the first time in nineteen
 * sessions of arguing about his design from memory. Its winning rule reads:
 *
 *     .help-cat{background:var(--sky-soft);color:var(--navy);border:1.5px solid var(--border);
 *               border-radius:99px;padding:7px 11px;font-size:13px;font-weight:650;
 *               cursor:pointer;min-height:40px;text-align:left}
 *
 * SMALL LEFT-ALIGNED PILLS. Soft blue, hairline border, fully rounded. The live app had turned them
 * into 52px navy-outlined centred cards in a two-column grid with the last one spanning the row.
 * THAT IS EXACTLY "too big and weird and not up to par", said on 2026-08-04, and the answer was
 * sitting in a zip the whole time. He was right and every session that theorised about it was wrong.
 *
 * SO WHY NOT COPY HIS RULE EXACTLY. Because 40px is UNDER the 44px WCAG 2.5.5 floor and 13px text is
 * under the 16px floor his own checker enforces, and this app is for older adults and people with
 * disabilities. He asked for "accessible AND sleek", which names that tension precisely. So: HIS
 * SHAPE, at a size a thumb and an older eye can actually use. The pill, the soft fill, the hairline
 * border, the left-aligned label, all his. 48px instead of 40, 16px text instead of 13. Nothing else
 * moves. If he wants it tighter still, the number to change is min-height and it is in one place.
 * ============================================================================================== */

/* --- the kinds-of-help options. HIS PILLS, at an accessible size. ---------------------------- */
.help-cats{display:flex;flex-wrap:wrap;gap:10px;margin:4px 0 18px}

.help-cat{
  display:inline-flex;align-items:center;justify-content:flex-start;
  width:auto;box-sizing:border-box;min-height:48px;padding:11px 18px;
  text-align:start;white-space:normal;line-height:1.25;
  font-size:16px;font-weight:650;font-family:inherit;
  color:var(--navy);background:var(--sky-soft);
  border:1.5px solid var(--border);border-radius:99px;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  transition:background .12s ease,border-color .12s ease;
}
.help-cat:active{background:var(--sky-soft)}   /* 2026-09-18 W3.1-16: was #DCE5EF */
.help-cat:hover{border-color:var(--navy)}
.help-cat:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
/* the grid rules above made the odd one span a column. There are no columns now. */
.help-cats .help-cat:last-child:nth-child(odd){grid-column:auto}

.phone.dark .help-cat{background:var(--surface);color:var(--navy);border-color:var(--navy)}   /* 2026-09-18 W3.2: was #20242B/#E8EDF5/#3A424E; gold on navy 5.35 (brandskin dark .help-cat) */
.phone.high-contrast .help-cat{border-color:#000;border-width:2px}

/* --- the 211 block ----------------------------------------------------------------------------
 * "Looks like shit" was a numeral in a tinted box wrestling with a paragraph and a small white pill
 * floating in green. One card now: the number is a quiet marker, the sentence is the loud part, and
 * the call is one full-width target. Same green, same words, same place on the page. */
.res-211{
  display:block;background:var(--green);color:#fff;
  border-radius:16px;padding:18px;margin:4px 0 18px;
}
.res-211 .big{
  display:inline-block;font-family:'Josefin Sans',sans-serif;font-size:15px;font-weight:700;
  letter-spacing:.10em;line-height:1;
  background:rgba(255,255,255,.16);border-radius:8px;padding:6px 10px;margin:0 0 10px;min-width:0;
}
.res-211 .r211-t,.res-211>div>div:first-child{font-size:18px;font-weight:800;line-height:1.3}
.res-211 .r211-s,.res-211>div>div:nth-child(2){font-size:15px;line-height:1.5;opacity:.96;margin-top:6px}
.res-211 a{
  display:flex;align-items:center;justify-content:center;gap:8px;
  width:100%;box-sizing:border-box;min-height:56px;margin-top:14px;
  background:#fff;color:var(--green);font-size:17px;font-weight:800;
  text-decoration:none;border-radius:12px;padding:14px 16px;
}
.res-211 a:focus-visible{outline:3px solid #fff;outline-offset:3px}
.res-211 a:active{background:#F0F5F1}

/* --- the search box, so the thing he taps on Home and the thing he lands on here are one object - */
.res-search input,.res-search .res-fauxinput{min-height:56px;font-size:16px;line-height:1.3}
.res-search .res-go{min-width:56px;min-height:56px}

/* ==============================================================================================
 * HIS RULING, 2026-08-12, GIVEN ON TWO RENDERED PICTURES, A AND B, NOT ON AN ARGUMENT.
 * `[RYAN 2026-08-12 CARD_HELP_SEE_IT]` picked B: "the pills. It is the shape out of the build you
 * called closer to what you want." And on the same card, on the empty gap beside the first bar:
 * `[RYAN 2026-08-12]` "food and then just empty space is ugly inefficient".
 *
 * WHAT WAS ACTUALLY WRONG, read off the live cascade in a real browser, never guessed. The block
 * further up this file that set his pill shape declared `.help-cat{width:auto}` at specificity
 * 0-1-0. Line 622 declares `.help-cats .help-cat{width:100%}` at 0-2-0 and wins, so the pills
 * rendered as five full-width bars, measured 382px each, and the previous session's "APPLIED: his
 * pill shape" was true in the file and false on the screen. HIS EYE WAS NEVER SHOWN IT.
 *
 * THE FIX IS THIS SELECTOR AND NOTHING ELSE. Same specificity as line 622, later in the file, so
 * it wins by order. NO LINE IS DELETED, per L5: line 622 still stands and still governs any layout
 * that wants full-width chips. Undo = delete this one block.
 *
 * MEASURED AFTER, at 414px in a real render: 73, 138, 192, 160 and 259px wide, wrapping to three
 * rows. Height stays 48px and text stays 16px, so nothing about the tap target moved.
 * NO NUMBER IS AUTHORED HERE. Width becomes the label's own width; that is the whole change.
 * ============================================================================================== */
.help-cats .help-cat{width:auto}

/* ==============================================================================================
 * THE 211 BLOCK. HIS RULING, 2026-08-12, GIVEN ON THREE RENDERED PICTURES.
 * `[RYAN 2026-08-12 CARD_211_THREE]` "I really dont love any of them--- isnt there something out
 * there for 211? Find something or I guess C as a placeholder move on."
 *
 * THIS IS C, AND IT IS A PLACEHOLDER IN HIS OWN WORD. Not a resolution. The real answer he asked
 * for is whether 211 has an official mark or asset of its own, which is open work, and this block
 * exists so the page stops being wrong while that is found. Whoever lands the real thing replaces
 * this block; do not treat it as settled taste.
 *
 * WHAT IT DOES. Same green, same words, same place, same order. The card goes from 206px tall to
 * about 165px: the number becomes plain and big on the left instead of sitting in a tinted badge,
 * and the call button sizes to its own words instead of running the full width. The shape is out of
 * MIRRORPHONE_EXACT_2026-08-06, the build he endorsed, at the 16px text and 48px button he had
 * picked on the kinds-of-help pills an hour earlier, so the two components on this page agree.
 *
 * HIS OWN WORDS BEHIND IT, both older than tonight and both already in the brain:
 *   `[RYAN 2026-08-04 WOONSOCKET_PERFECT_PUNCHLIST_v2:34]` "the 211 box looks like shit"
 *   `[RYAN OBS-12, session 080 2026-07-23]` "big 911, medium call 988 medium call 988 small 211
 *   nonemergency" - his own size order, in which 211 is the SMALL one. Open since July.
 *
 * TWO OF THE RULES BELOW ARE OUTRANKED ON THIS PAGE AND THAT IS SAID HERE RATHER THAN LEFT TO LOOK
 * LIKE THEY WORK. The 211 block in `index.html` carries INLINE font sizes, 15.5px on the headline and
 * 13.5px on the explainer, and an inline style beats any stylesheet. So `.res-211>div>div:first-child`
 * and `:nth-child(2)` below govern only the class-based copy that `res211HTML()` injects into search
 * results, not the block he is looking at. Measured, not assumed. Changing those two sizes on the
 * main block means editing the markup, which is a separate decision and is not smuggled in here.
 *
 * THE ONE MEASURED THING TO WATCH. The explainer line under the headline renders at 13.5px here,
 * which is what he looked at and approved, and it is BELOW the 16px floor A11Y_GAP_6.06 named as
 * this app's worst readability offender for older eyes. It is recorded, not silently corrected, and
 * not argued with. Raising it is one number in one place.
 *
 * NOTHING IS DELETED. The block above still stands; these are the same selectors later in the file
 * winning by order. Undo is deleting this block. The 211 NUMBER, the tel: link and the crisis floor
 * are untouched: this is the box, never the number.
 * ============================================================================================== */
.res-211{display:flex;align-items:flex-start;gap:12px;padding:13px 14px;margin:4px 0 14px}
.res-211 .big{
  display:block;background:none;padding:0;margin:0;flex:none;
  /* FONT211 2026-09-25. Was font-weight:800. Josefin Sans declares only 600 and 700 and neither is
     preloaded, and 800 exists in no file at all, so this 31px crisis number was painting whatever
     face the phone happened to ship. FONTWIRE_2026-09-25 measured 1,565 elements in this app
     painting the wrong typeface for the same class of reason. A crisis number must not be drawn by
     chance. 700 is a weight Josefin really has. Undo: NEVER_OVERWRITE_BACKUPS/font211_2026-09-25/
     app.css.PRE_FONT211. The digits 211 are untouched; this is presentation only. */
  font-size:31px;font-weight:700;line-height:1.05;letter-spacing:0;
}
.res-211 .r211-t,.res-211>div>div:first-child{font-size:16px;line-height:1.3}
.res-211 .r211-s,.res-211>div>div:nth-child(2){margin-top:3px;font-size:13.5px;line-height:1.4}
.res-211 a{
  display:inline-flex;width:auto;align-items:center;justify-content:center;
  margin-top:9px;padding:10px 16px;min-height:48px;
  font-size:16px;border-radius:10px;
}

/* ==============================================================================================
 * THE LANGUAGE PICKER: FOUR BUTTONS, THEN A PULL-DOWN. HIS RULING, 2026-08-12, ON THE RENDER.
 * `[RYAN 2026-08-12]` "these are so big, are they big on purpose? I\f it is for accessibility, do it
 * like ENg esp fra port and the rest are pull down for more languages"
 * Sixteen full-size buttons was eight rows of two and it ran past the bottom of the screen. English,
 * Espanol, Francais and Portugues keep their buttons; the other twelve are in one native select.
 * The behaviour is in WALL/city_config_langs.js; this is only how it looks.
 *
 * THE PULL-DOWN INHERITS THE PAGE'S EXISTING SIZE RULE, 48px tall and 16px text, which is what he
 * approved on the kinds-of-help pills. NO NEW NUMBER IS AUTHORED HERE. It spans both grid columns
 * so it reads as one control rather than a fifth button.
 *
 * It is a real <select>: on a phone it opens the operating system's own picker, which is bigger and
 * more legible than anything drawn here, and it works with a screen reader and a keyboard for free.
 * Undo is deleting this block.
 * ============================================================================================== */
/* ==============================================================================================
 * 211, HALVED. HIS RULING, 2026-08-12, ON A SCREENSHOT HE SENT OF THE LIVE SITE.
 * `[RYAN 2026-08-12]` "Make the 211 thing like 1/2 height as it is now, redesign it."
 *
 * MEASURED: 165px tall on the wall, 172px on the deployed build he was actually looking at.
 * NOW 68px. The number, the headline and the call button sit on ONE row.
 *
 * WHAT I DROPPED TO GET THERE, AND IT IS THE ONE THING TO ARGUE WITH ME ABOUT. The three-line
 * explainer, "Free and private, every day. A real person helps you find food, a place to stay,
 * heat, and more. Spanish and other languages," is the entire difference between 165px and 68px.
 * It is HIDDEN, not deleted: the markup, the `r211Sub` key and all sixteen translations are
 * untouched, so `display:block` on one line brings it back everywhere at once.
 *
 * WHY IT WAS NOT SHORTENED INSTEAD, which was the obvious idea and is a trap. `r211Sub` is a
 * translated key. Rewriting the English leaves fifteen languages carrying the OLD long sentence
 * under a shorter English one, which is worse than either. A shorter line has to go through the
 * translate pipeline first, and that is a decision plus a pass, not a CSS edit.
 *
 * The number, the tel: link, the green and the position are untouched. This is the box, never the
 * number. Undo is deleting this block.
 * ============================================================================================== */
.res-211{align-items:center;gap:12px;padding:10px 12px;margin:0 0 12px}   /* HELPLAY 2026-10-01: margin-top was 4px. The card above it already carries its own bottom gutter, so this 4px was a double gap. margin-bottom 12px untouched (bar_variants_2026-08-28.css wins it anyway). Padding, gap, align-items and every colour untouched. */
.res-211 .big{font-size:26px;line-height:1}
.res-211 > div:last-child{flex:1;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.res-211 > div:last-child > div:first-child{flex:1 1 150px;font-size:16px;line-height:1.25}
.res-211 > div:last-child > div:nth-child(2){display:none}
.res-211 a{margin-top:0;flex:0 0 auto}
/* SCREENSPACE 2026-09-02: THE "CALL 211" BUTTON WAS A TOWER OF LETTERS AT LARGE TEXT. The three
   `:last-child` rules above never match once bar_variants_2026-08-28.js appends its
   <button class="bar-211-toggle"> (the "?" circle) after the text column - the same trap
   app.css:238 already names for a neighbouring rule - so the text column falls back to a plain
   block with no flex:1. At 100 percent there is room and nobody notices. At the 150 and 200
   percent steps on a 320px phone the row is 213 / 160 CSS px wide, the "211" tile, the "?" toggle
   and the gaps leave the column 72 / 45px, the title truncates to "Not sur..." and the Call 211
   link renders one letter per line, 253px tall at 150 percent, 338px at 200 percent - taller
   than the 75px of screen left between the header and the dock at that size (measured 2026-09-02,
   real Chromium, _HARNESS/_screenspace_2026-09-02/c211_wide_150.png). The other half of the
   squeeze is bar_variants_2026-08-28.css:141-144: with bar=2/3 the card reserves padding-right:52px
   for its absolutely-positioned "?" toggle, which at 200 percent is a third of the card. LAW L2:
   211 stays reachable on Find Help; a letter tower is not a door. At those two steps: the row
   wraps, the "?" toggle comes back into flow on the first line beside the "211" tile (still 44x44),
   the 52px reservation goes, and the text column takes its own full-width line, so "Call 211" is
   one line again in every language. 100 percent is untouched by these rules (they are scoped to
   .text-large / .text-xlarge; #res-211 in the selector is only there to outrank bar_variants'
   html[data-bar] #res-211). Re-proved by falsify_screenspace_2026-09-02.mjs (CRISIS gate, 211 at
   150/200). The fifth rule: the inherited overflow-wrap:anywhere let the button break "Καλέστε" as
   "Καλέστ / ε"; break-word breaks at the space and only splits a word that cannot fit a line alone.
   Undo: delete these five rules. */
.phone.text-large .res-211 a,.phone.text-xlarge .res-211 a,
.phone.text-large .res-211 a *,.phone.text-xlarge .res-211 a *{overflow-wrap:break-word}
.phone.text-large .res-211,.phone.text-xlarge .res-211{flex-wrap:wrap}
.phone.text-large #res-211,.phone.text-xlarge #res-211{padding-right:12px}
.phone.text-large #res-211 .bar-211-toggle,.phone.text-xlarge #res-211 .bar-211-toggle{position:static;order:2;margin-inline-start:auto;flex:0 0 44px}
.phone.text-large .res-211 > div:not(.big):not(.bar-211-toggle),
.phone.text-xlarge .res-211 > div:not(.big):not(.bar-211-toggle){flex:1 1 100%;order:3}

/* ==============================================================================================
 * THE TWO 988s. HIS RULING, 2026-08-12: "988 call and 988 text are too close in color."
 * He is right and it is measurable: they were #2C4A6E and #1F3550, two navies about one shade
 * apart on the most important row in the app.
 *
 * THE FIX IS SHAPE, NOT A THIRD COLOUR. Text-988 becomes a white button with a heavy navy outline.
 * A person now tells the two doors apart by fill-versus-outline, which still works for someone who
 * cannot distinguish the two blues at all. NEWS_LAYOUT_LITREVIEW_6.08 already landed on the same
 * rule for the same reason, WCAG 1.4.1: colour must never be the only signal.
 * D-RED is respected: red stays on 911 alone and nothing new is red.
 * Contrast: #1F3550 on white is well past 4.5:1, better than the white-on-navy it replaces.
 * ============================================================================================== */
.rrh-988t{background:#fff;color:#1F3550;border:2px solid #1F3550}
.phone.dark .rrh-988t{background:#12161C;color:#E8EDF5;border-color:#E8EDF5}

/* ==============================================================================================
 * THE NAV. HIS RULING, 2026-08-13, GIVEN ON FOUR RENDERED OPTIONS AND THEN ON A FIFTH.
 * `[RYAN 2026-08-13]` "Show option A but Help not red it the same as others and selected nav button
 * red, with no top bar."
 * `[RYAN 2026-08-13]` picking it a second time, after being shown the collision in writing:
 * "Exactly as I asked. Selected is red, Help is like the others. **Change the law.**"
 *
 * SO THE LAW CHANGED. Red used to be reserved for the crisis path and nothing else. HE CHANGED IT,
 * knowingly, having been shown what it costs. The amendment is recorded in STATE.json D-RED and in
 * LAW.md's amendment table on the same date. A session that finds red on a nav tab and "fixes" it
 * back has overruled him. Do not.
 *
 * WHAT IS UNTOUCHED BY THIS: the crisis floor itself. 911, 988 and 211 keep #C00C0C on the crisis
 * bar, the fixed help bar and the noscript block. This is about the nav tab you are standing on.
 *
 * All five badges are `nav_color.png` now (four `src` swaps in index.html), Help's label is the same
 * dark chip as everyone else's, there is no bar above the selected tab, and nothing changes size.
 * That last part is his constraint, verbatim: "the nav buttons stay the same size".
 *
 * KNOWN AND TOLD TO HIM: all five badges are the SAME artwork, because `nav_bw.png` and
 * `nav_color.png` have always been one image in two treatments. Five distinct badges is an art job
 * he said he would take to the artist. Undo: delete this block and swap the four srcs back.
 * ============================================================================================== */
/* RETIRED AT SOURCE 2026-09-02, NAV COPY. `[RYAN 2026-09-02]` "Go fix things at the source and
   delete the patches that exist only to work around them." These three rules are the textbook
   case: their own comment underneath says they exist because `brand.css .nbtn-help .lb` paints a
   red chip with !important and loads later. brand.css's chip is retired at ITS source on the same
   date (see brand.css, RETIRED 2026-09-02), so there is nothing left for these to fight, and
   agingwellinc.org -- the nav he told us to copy today -- draws no chip behind any label at all.
   Kept on disk, commented, never deleted. Undo: uncomment these three lines AND the two brand.css
   lines. Backup: app.css.BAK_NAVCOPY_20260902.
.nav .nbtn-help .lb{background:rgba(15,20,28,.9) !important;color:#fff !important}
.nav .nbtn.active .lb{background:#C00C0C !important;color:#fff !important}
.nav .nbtn.nbtn-help.active .lb{background:#C00C0C !important;color:#fff !important}
   ---- END RETIRED 2026-09-02 ---- */
.phone.high-contrast .nbtn.active .lb{outline:2px solid #fff;outline-offset:1px}

.lang-more{grid-column:1 / -1;display:flex;flex-direction:column;gap:6px;margin-top:2px}
.lang-more-lbl{font-size:14px;font-weight:700;color:var(--navy);opacity:.9}
.lang-more-sel{
  width:100%;box-sizing:border-box;min-height:48px;padding:12px 14px;
  font-family:inherit;font-size:16px;font-weight:700;line-height:1.2;
  color:var(--navy);background:var(--surface);
  border:2px solid var(--navy);border-radius:var(--radius);cursor:pointer;   /* 2026-09-18 W3.1-24: was 1.5px, radius 12px */
}
.lang-more-sel:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
.lang-more-on .lang-more-sel{background:var(--navy-fill,var(--navy));color:#fff}   /* NAVYFILL 2026-09-23: was var(--navy), gold in dark under white text, 2.62:1. Burgundy in both, 9.73. The language selector is a control older adults use. A dark override exists below and still wins; the base rule is now correct alone. */
.phone.dark .lang-more-sel{background:var(--surface);color:var(--navy);border-color:var(--navy)}   /* 2026-09-18 W3.2: was #20242B/#E8EDF5/#3A424E; gold on navy 5.35 */
.phone.dark .lang-more-on .lang-more-sel{background:#7A2636;color:#FAF6EC}   /* 2026-09-18 W3.2: was #3A5D8C/#fff; Burgundy, cream 9.02 */

/* ==============================================================================================
 * THE "WHY DID THIS COME UP" CLUE. HIS ASK, 2026-08-13:
 * "under business license could we have a yard sale text clue or something so they know why that
 * came up?"  There is no yard sale permit page in this city, so those words route to the City
 * Clerk's real licences page. Correct, and baffling without a word of explanation. The clue is the
 * record's OWN matched phrase, never invented, and it only appears when it is not already in the
 * label. Behaviour lives in search_unify.js; this is only how it looks. Undo: delete this block. */
.csv-why{display:inline-block;margin-inline-start:8px;padding:2px 8px;border-radius:99px;
  background:var(--sky-soft);color:var(--navy);border:1px solid var(--border);
  font-size:13px;font-weight:700;line-height:1.5;vertical-align:middle;white-space:nowrap}
/* ==================================================================================================
   SPACE2 2026-09-28. THE CLUE PILL IS THE LARGEST SIDEWAYS OVERFLOW IN THIS APP, AND NOBODY HAD IT.
   FOUND BY A `Range` SWEEP WITH A CLIP-ANCESTOR CHECK, which SPACESCOUT_2026-09-28 section 4 proved
   is the only instrument that can see this class of fault: the TEXT leaves the page, the BOX does
   not, so every element-rect sweep reports zero. `SPAN.csv-why`'s own box is 36px wide. Its text
   reaches x = 398.6 on a 320px screen.
   MEASURED, real headless Chromium 154 over real HTTP, Help, `food` typed into `#res-q` by real
   keystrokes, results settled, English:
     320 Extra Large   documentElement.scrollWidth 399 against clientWidth 320, 78.609px of it this
                       one pill, text "food resources" inside A.csv-item
     320 Large         scrollWidth 349, 28.922px, the same pill, the same words
   THAT IS NOT A COSMETIC OVERFLOW. DOCK2_2026-09-28 section 2 is the whole argument: when the page is
   wider than the device, Chromium WIDENS THE LAYOUT VIEWPORT to fit it, and `#bw-dock` is
   `position:fixed`, so it pins itself to the bottom of a viewport that is taller than the glass and
   lands below the fold. A sideways overflow moves the nav bar. This is the same fault DOCK2 chased
   through four flex containers, in a fifth place, and it is bigger than all four.
   WHY `white-space:normal` IS RIGHT HERE AND IS WRONG ON `.tel-num`. This pill is PROSE - the
   record's own matched phrase, "food resources", "yard sale" - and a phrase that wraps onto a second
   line inside its pill loses NOTHING. A phone number that wraps loses the ability to read it, which
   is why `.tel-num` keeps `nowrap` and is NOT touched by this block. The two are not the same problem
   and they do not get the same answer.
   NOT `overflow:hidden` with an ellipsis, which would cut the clue in half and the clue is the whole
   point of the pill: it is the app telling her WHY this result came up.
   PHONE WIDTHS ONLY. Above 430px the pill has always fitted on one line and keeps `nowrap`.
   MEASURED AFTER, same cells, nothing else changed: 320 Large 349 -> 320, overflow 29 -> 0, and the
   pill vanishes from the sweep entirely. 320 Extra Large 399 -> 353, overflow 79 -> 33. What is left
   at Extra Large is named in the SPACE2 block beside `a.al-call` below; it is not this pill.
   ZERO `!important`. `.csv-why` at (0,1,0) is the only rule in the app that sets this property, and
   the media query below repeats the same specificity later in the same file, so it wins on order.
   PROVED from the live computed value: `white-space` reads `normal` at 320 and `nowrap` at 431.
   Undo: delete this media block and the pill runs off the right edge again. */
@media (max-width:430px){
  .csv-why{ white-space:normal; overflow-wrap:break-word; max-width:100%; }
}
.phone.dark .csv-why{background:var(--surface);color:var(--ink);border-color:var(--border)}   /* 2026-09-18 W3.2: was #20242B/#E8EDF5/#3A424E */

/* The folded roster on Info. His layout-2 pick, 2026-08-13. A native <details>, so keyboard and
   screen reader work for free. 54px summary and 17px text reuse the sizes already on this page.
   Behaviour is in info_page.js. Undo: delete this block. */
.info-roster{background:var(--surface);border:1.5px solid var(--border);border-radius:var(--radius);margin:12px 0}   /* 2026-09-18 W3.1-25: was border-radius:16px */
.info-roster>summary{list-style:none;cursor:pointer;min-height:54px;display:flex;align-items:center;
  justify-content:space-between;padding:12px 16px;font-weight:800;color:var(--navy);font-size:17px}
.info-roster>summary::-webkit-details-marker{display:none}
.info-roster>summary::after{content:"\25BE";margin-inline-start:10px;font-size:15px}
.info-roster[open]>summary::after{content:"\25B4"}
.info-roster>summary:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
.info-roster .card{border:none;margin:0}
.phone.dark .info-roster{background:var(--surface);border-color:var(--border)}   /* 2026-09-18 W3.2: was #20242B/#3A424E */

/* [2026-08-14 ACCESSIBILITY_REPAIR_SPEC fix 1A] WCAG 2.5.5 target size, the settings gear.
   LOAD ORDER CHECKED FIRST, NOT REASONED: index.html:14 loads app.css, index.html:16 loads
   brand.css, so app.css LOSES every equal-specificity tie and appending here wins nothing on
   its own. brand.css:7 `.bw-banner .hdr-gear` (0,2,0) sets 42x42 with `min-height:0 !important`,
   which is why app.css:851 `.hdr-gear` (0,1,0) never applied and the live tap target was 42x42.
   This selector is (0,3,0), matching <header class="hdr has-banner"><div class="bw-banner">
   <button class="hdr-gear"> at index.html:61-62. Specificity is order-independent, so it beats
   brand.css whatever the link order; !important is needed only to beat brand.css's own
   !important on min-height, and !important ties resolve by specificity before source order.
   The SVG inside is a fixed 22x22 and the banner is position:relative with the gear absolute at
   top:8px right:8px, so 2px in each dimension moves nothing else. Undo = delete this block. */
.hdr .bw-banner .hdr-gear{
  min-height:44px !important;min-width:44px !important;
  width:44px !important;height:44px !important;
}

/* [2026-08-14 ACCESSIBILITY_REPAIR_SPEC fix 1B: BUILT, MEASURED, AND NOT SHIPPED. HIS CALL.]
   brand.css:28 `.nbtn .lb` sets a font size of 11 px and it is the ONLY sub-12px declaration in
   the whole app: 230 px font-size declarations across the six stylesheets, 152 under 16px, 84
   under 14px, exactly 1 under 12px, and it is this one. The one-line repair is a `.nav .nbtn .lb`
   rule at 12 px, at (0,3,0), which beats brand.css:28 at (0,2,0) on specificity regardless of
   load order.

   THE PROSE ABOVE IS DELIBERATELY NOT WRITTEN AS A CSS DECLARATION. 2026-08-14: this comment
   originally spelled the sub-12px size as a real `font-size:NNpx` declaration, and the
   `font_floor_min_12px` decision invariant in decision_invariants.mjs regexes the RAW text of
   app.css, comments included. That one word of prose turned predeploy_gate.mjs BLOCK-red on an
   accessibility floor that was never actually broken. The invariant was NOT weakened to fix it,
   because a gate that ignores part of the file is a gate that can be talked around. Write sizes
   as prose in this file's comments.

   IT IS NOT IN THIS FILE BECAUSE ITS OWN PRECONDITION FAILED IN A REAL CHROMIUM RENDER.
   Measured at 360px and 414px, all 16 languages, glyph box against glyph box:
     - FRENCH, an offered language, goes from NO overlap at 11px to "Evenements" overlapping
       "Aide" by 6.7px at 12px, at 360px.
     - Vietnamese, Greek and Polish also overlap at 12px at 360px.
   The spec's own rule for this outcome: do not revert the question to 11px and call it done,
   take the nav label question to him, because he has already ruled on nav sizing once:
   [RYAN app.css:1132] "the nav buttons stay the same size".

   AND A SEPARATE THING THE RENDER FOUND, WORSE THAN THE ONE IT WAS LOOKING FOR: the nav labels
   ALREADY collide TODAY at 11px. At 360px, Greek overlaps by up to 14.9px and Polish by 5.6px,
   and in FRENCH the "Actualites" label already runs past the right edge of the nav at both
   360px and 414px. That is shipped, live, and was not in any audit. The chip is
   position:absolute with white-space:nowrap and overflow:visible (brand.css:28), so a long word
   walks straight out of its own pill and over its neighbour. Raising the type makes it worse;
   it does not cause it. Undo for the whole question = nothing to undo, nothing was changed. */


/* ==============================================================================================
 * CRISIS BUTTON SIZES. HIS ANSWER, 2026-08-14, question 11: "crisis buttons with 911 988 text 988".
 * That names the set of three, and the set of three is what renders. The size ORDER comes from the
 * older and more specific sentence, open since 22 July and never built:
 * `[RYAN SESSION_080_OBSERVATIONS_2026-07-23:20]` "big 911, medium call 988 medium call 988 small
 * 211 nonemergency".
 *
 * WHAT SHIPS TODAY, MEASURED IN CHROMIUM AT 414x900, NOT GUESSED:
 *   911 door   123.3 x 47.4, number drawn at 16px
 *   988 call   123.3 x 47.4, number drawn at 16px
 *   988 text   127.3 x 47.4, number drawn at 16px   (4px wider only because of its 2px outline)
 *   211 on the same page, .res-211 .big, drawn at 26px
 * So the three doors are flat, and the 211 number is drawn HALF AGAIN AS BIG as the 911 number.
 * That is not a small miss, it is the opposite of the order he asked for, on the crisis floor.
 *
 * WHY 911 IS NOT MADE WIDER, WHICH IS THE FIRST THING ANYONE WOULD REACH FOR. The three doors sit
 * in one flex row. The row has 374px of usable width at 414px. The doors already use 373.9 of it.
 * Every pixel 911 gains as width is a pixel a 988 door LOSES, and the standing rule on this floor
 * is that a tap target may only ever get bigger, because the person reaching for 911 may have a
 * tremor. So the size order is carried by TYPE SIZE and by ROW HEIGHT, both of which only grow, and
 * the three doors keep the widths they have today. If he wants 911 physically wider as well, the
 * only honest way is to put 911 on its own full-width line with the two 988 doors on the line
 * beneath, which makes the always-on bar about 128px tall. That is a shape change, not a degree,
 * and it is his call, so it is not built here.
 *
 * THREE DEGREES OF THE SAME IDEA, so he can point at one:
 *   b  gentle          911 26px, 988s 22px, 211 20px, row 56px tall
 *   a  his sentence    911 34px, 988s 26px, 211 22px, row 64px tall   (the recommended one)
 *   c  strong          911 44px, 988s 28px, 211 22px, row 74px tall
 * In all three: 911 is the biggest, the two 988s are equal to each other and in the middle, and the
 * 211 number is smaller than either of them. That is his sentence, in that order.
 *
 * THE TAP TARGET ONLY GROWS, IN EVERY DIMENSION, IN ALL THREE. The live row is 47.4px tall; the
 * smallest of these is 56px. No door loses a single pixel of width. The 211 "Call 211" button,
 * .res-211 a, is NOT TOUCHED and stays at 107.5 x 48; the only 211 thing that moves is the size of
 * the drawn digits, which are not a tap target.
 *
 * NOTHING HERE IS LIVE. Every selector starts at html[data-crisis-sizes=...], and that attribute is
 * only ever set by bw_flags_2026-08-14.js when ?flag=crisisA (or crisisB, or crisisC) is in the URL.
 * With no flag, not one of these declarations matches anything.
 *
 * SPECIFICITY, CHECKED BEFORE IT WAS WRITTEN AND THEN MEASURED IN A LIVE DOM. What it has to beat
 * is app.css:597 `.res-helpbar .rrh-crisis .rrh-num` at (0,3,0), the rule that makes the live number
 * 16px rather than the 18px at app.css:223. These are (0,4,1): the attribute selector on html is
 * (0,1,1) and three classes are (0,3,0). They also beat anything in brand.css, brand_palette.css,
 * brand_cats.css and brand_phone.css, none of which declares a single .rrh rule (grepped, all four)
 * - and specificity is order-independent, so the fact that those four load AFTER app.css at
 * index.html:16-19 cannot cost this block the tie. No !important is used or needed.
 *
 * The number and the word under it are untouched as TEXT. 911, 988 and 211 are written by app.js
 * and by nothing here. No wording is changed, nothing is localised, nothing is moved. 211 is NOT
 * put back into the fixed bar: he removed it from there on 2026-08-06 and it stays out. The only
 * 211 this block can even see is the one already on the Find Help surface.
 *
 * Undo = delete from here to END CRISIS SIZES.
 * ============================================================================================== */

/* RETIRED 2026-09-06, SHIPPICKS. The three flag schemes (html[data-crisis-sizes="a"|"b"|"c"], set by
   ?flag=crisisA/B/C) that stood here are gone from the cascade. He picked A on 2026-09-06 from a
   rendered picture ("A = A. My drawing. 911 at 34px.") and A now ships AT SOURCE on the fixed floor
   bar: see SHIPPICKS SIZING A next to the base .res-helpbar rules further up this file. B and C could
   only ever make 911 SMALLER than what now ships (26px and, on the 988s, 22px), which
   `[RYAN 2026-09-05]` "No. Crisis buttons never get smaller." forbids, and the three-flag build was
   broken anyway: crisisbar_2026-08-28.css's id rule outranked all three, so no flag ever moved the
   911 digit. The exact bytes of the retired block are in
   NEVER_OVERWRITE_BACKUPS/shippicks_2026-09-06/app.css.PRE_SHIPPICKS_*. The .res-211 .big lines that
   were in those schemes shrank the 211 digit (26 -> 22/20px) and are NOT carried forward. */
/* ---- END CRISIS SIZES (retired 2026-09-06) ---- */
/* ==============================================================================================
 * SHIPPED FOR REAL, 2026-08-15. HIS ANSWER, CARD_EVERY_EVOLUTION_2026-08-15 question 3: NOW.
 * Scheme "a" above is now THE DEFAULT and needs no flag. It is the literal reading of his
 * sentence of 2026-07-23: "big 911, medium call 988 medium call 988 small 211 nonemergency".
 *   911 34px  .  call 988 and text 988 26px each  .  211 22px  .  row min-height 64px
 *
 * WHY THE SELECTORS CARRY A BARE html. `html .res-helpbar .rrh-911 .rrh-num` is (0,3,1). It has
 * to beat app.css:597 `.res-helpbar .rrh-crisis .rrh-num` at (0,3,0), and it does, on
 * specificity, not on order. It is in turn beaten by html[data-crisis-sizes="b"|"c"] at (0,4,1)
 * above, so ?flag=crisisB and ?flag=crisisC still work for comparing degrees, and ?flag=crisisA
 * now sets an attribute that changes nothing because it is already what ships. No !important.
 *
 * THE TAP TARGET ONLY GROWS. The row was 47.4px tall and is now 64px. No door loses a pixel of
 * width. .res-211 a, the "Call 211" button, is NOT touched and stays 107.5 x 48; the only 211
 * thing that moves is the size of the drawn digits, which are not a tap target.
 *
 * NOT ONE NUMBER, WORD OR POSITION CHANGES. 911, 988 and 211 are written by app.js and by
 * nothing here. Nothing is localised. 211 stays out of the fixed crisis bar, per his ruling of
 * 2026-08-06 and again 2026-08-15: "211 stays off i rules on this many times update".
 *
 * Undo = delete from here to END CRISIS SIZES SHIPPED.
 * ==============================================================================================
 * REJECTED BY RYAN, 2026-08-15, THE DAY IT SHIPPED: "What happened to the help page? That is not
 * what i want. The attached looks like shit, the 911 988". The rules that stood here (64px doors,
 * 34px 911, 26px 988s on the fixed bar, 22px 211 digits) are withdrawn; the exact bytes live in
 * app.css.bak_2026-08-16_crisislook. The fixed bar returns to its 2026-08-14 geometry, which his
 * eye had accepted all week. The a/b/c flag schemes above stay for comparing degrees.
 *
 * THE LESSON, WRITTEN DOWN SO IT IS NOT RELEARNED A THIRD TIME: his 2026-07-23 sentence "big 911,
 * medium call 988 medium call 988 small 211 nonemergency" was a statement of IMPORTANCE, of what
 * must read as most urgent. It was implemented twice as literal type sizes and both times his eye
 * called it wrong. Hierarchy now lives in the crisis card at the END OF THIS FILE: 911 is the only
 * solid-red door in the row and the widest and boldest, the two 988 doors are equal partners in
 * the fixed bar's own navy, 211 stays green, below, last. His sentence carried; the wall of size
 * gone. His eye decides what correct is, not a font-size table.
 *
 * RE-RULED BY RYAN, 2026-09-06, WITH THE PICTURE IN FRONT OF HIM. LOOK_2026-09-05.md put A, B and
 * C on one card as real renders (466 of them, shipped fonts, 390 and 320, light and dark, 100 and
 * 200 percent) with the chrome cost written on each, and he tapped "A = A. My drawing. 911 at
 * 34px." That is the newest ruling and it is the one that ships: SHIPPICKS SIZING A, near the base
 * .res-helpbar rules. The 2026-08-15 rejection above was of a build he had not seen a picture of
 * first; it stays written here so nobody thinks the history is clean. If his eye says no again, the
 * undo is one block. */
/* ---- END CRISIS SIZES SHIPPED (withdrawn 2026-08-16; re-ruled and shipped as SIZING A 2026-09-06) ---- */


/* ==============================================================================================
 * NAV LABEL SIZE. HIS ANSWER, 2026-08-14, question 9: A, shorten the nav words in the languages
 * that overflow. That answer unblocks the accessibility repair that was built, measured and NOT
 * shipped on 2026-08-14 (the long note further up this file), whose precondition was that raising
 * the type made French, Vietnamese, Greek and Polish collide.
 *
 * brand.css:28 `.nbtn .lb` draws the nav label one pixel under the app's own twelve-pixel floor,
 * and it is the only declaration in the whole app that does. This rule is (0,4,1) - html plus the
 * attribute is (0,1,1), .nav .nbtn .lb is (0,3,0) - against brand.css:28 at (0,2,0). Specificity is
 * order-independent, so it wins even though brand.css loads after app.css at index.html:16. No
 * !important. Verified by reading the computed font-size back out of a live Chromium, not assumed.
 *
 * MEASURED, ALL SIXTEEN LANGUAGES, AT 360px AND AT 414px, chip pill against chip pill. The pill is
 * what the eye sees collide: the label is position:absolute with white-space:nowrap and 8px of
 * padding each side, so a long word walks out of its own pill and over its neighbour's. The words
 * that had to move, and only those, are in bw_flags_2026-08-14.js with a back translation and an
 * honest confidence on every line. Nine of the sixteen languages needed nothing.
 *
 * Two flags on purpose, because they are two questions:
 *   data-nav-labels="size12"   the size alone, every word left exactly as it is
 *   data-nav-labels="short12"  the size AND the shorter words
 * Neither is set unless ?flag=nav12size or ?flag=nav12 is in the URL. Undo = delete this block.
 * ============================================================================================== */
/* RETIRED AT SOURCE 2026-09-02, NAV COPY. Two preview flags (?flag=nav12size / ?flag=nav12) for a
   question that was answered and shipped on 2026-08-15 in the block below, and whose whole subject
   -- raising the label off brand.css's 11px -- no longer exists now that brand.css's 11px is
   retired at its source. Kept on disk, commented. Undo: uncomment these two lines.
html[data-nav-labels="size12"] .nav .nbtn .lb,
html[data-nav-labels="short12"] .nav .nbtn .lb{font-size:12px}
   ---- END RETIRED 2026-09-02 ---- */
/* ---- END NAV LABEL SIZE ---- */

/* ==============================================================================================
 * NAV LABEL, SHIPPED FOR REAL. 2026-08-15. No flag. This is the live rule.
 *
 * `[RYAN 2026-08-14, q9]` A: shorten the nav words in the languages that overflow. That was
 * answered, built behind a flag, and then never shipped, which is the thing he is angry about.
 * It ships now, in two halves that have to be read together.
 *
 * HALF ONE, THE WORDS, and they are in the strings files, not here. FOUR words changed, all four
 * marked HIGH or MEDIUM-HIGH confidence: French "Agenda" and "Actus", Polish "Info", Greek
 * "Sxetika". FIVE words were HELD because they were marked NEEDS A HUMAN and nobody here can
 * vouch for them: Polish Events and News, Greek Events, and both Vietnamese words. A held word
 * keeps HIS word. It does not get a translation a machine guessed at.
 *
 * HALF TWO, THIS RULE, THE SIZE. brand.css:28 draws the nav label one pixel under the app's own
 * twelve-pixel readability floor and it is the only declaration in the app that does. Raising it
 * is the ACCESSIBILITY_REPAIR_SPEC fix 1B that has been sitting built and unshipped. It is raised
 * HERE ONLY IN THE LANGUAGES WHERE THE RAISE MEASURES CLEAN, because raising it everywhere was
 * measured and it is WORSE than today in three languages.
 *
 * WHY pl, el AND vi ARE EXCLUDED, and why that is the honest answer and not a cop-out. Their long
 * words were the ones held. Measured in a real Chromium, glyph run against glyph run, with the four
 * new words in place:
 *     360px  pl at 12px  "Wydarzenia" runs 14.7px into "Pomoc"      at 11px it is 5.6px
 *     360px  el at 12px  "Ekdiloseis" runs 8.7px into "Voitheia"    at 11px it is 2.2px
 *     360px  vi at 12px  "Trang chu" runs 5.0px into "Su kien"      at 11px it is CLEAN
 * So for those three the label is left at the size brand.css already gives it: his word survives
 * and the type does not grow. That is "let it shrink rather than ship a translation nobody can
 * vouch for", and the two Polish and one Greek overlaps that remain at 360px are TODAY'S bug,
 * unchanged, waiting on a human translator, not something this change introduced.
 *
 * THE SIZE IS NOT WRITTEN AS A SUB-TWELVE DECLARATION ANYWHERE. The three languages are excluded
 * with :not(), so they simply inherit brand.css:28 and app.css never names a size under twelve.
 * That is deliberate: decision_invariants.mjs `font_floor_min_12px` regexes the raw text of THIS
 * FILE for any font-size below twelve pixels and would go RED on an override written the other
 * way round. Verified: that gate is still green after this block.
 *
 * SPECIFICITY, STATED THEN MEASURED. This selector is (0,6,1): one element (html) plus six of the
 * class-or-attribute weight (three :not() attribute tests, .nav, .nbtn, .lb). It must beat
 * `brand.css:28  .nbtn .lb` at (0,2,0), which sets the eleven, and brand.css is LINKED AFTER
 * app.css at index.html:16. Specificity is decided before source order, so it wins by 6 to 2 and
 * needs no !important. It must not disturb `app.css:1138-1140 .nav .nbtn-help .lb` and
 * `.nav .nbtn.active .lb`, which carry !important on background and color only and declare no
 * font-size at all, so there is nothing to collide with. It sits under the two flag rules above
 * it for the flagged preview only, which is intended: ?flag=nav12 still previews the held words.
 * Verified by reading getComputedStyle().fontSize back out of a live Chromium in all sixteen
 * languages at 360, 390 and 414: 12px in thirteen, 11px in pl, el and vi. Not assumed.
 *
 * `document.documentElement.lang` is set on every switch by setLang() at app.js:115, and the
 * document ships as <html lang="en">, so this rule is correct from the first paint.
 * Undo: delete this block. Backup app.css.bak_2026-08-15_answered.
 * ============================================================================================== */
/* RETIRED AT SOURCE 2026-09-02, NAV COPY, AND THIS ONE MATTERS -- IT WAS THE LIVE RULE.
 * `[RYAN 2026-09-02]` "Just copy the whole thing as closely as you can" (agingwellinc.org's bottom
 * nav) and, the same day, "Go fix things at the source and delete the patches that exist only to
 * work around them."
 * This rule's entire stated purpose, in its own header two paragraphs up, is to lift the nav label
 * off `brand.css .nbtn .lb`'s 11px, which is "the ONLY sub-12px declaration in the whole app". That
 * 11px is retired at ITS source today (brand.css, RETIRED 2026-09-02), so the thing this rule was
 * repairing is gone, and with it the reason for the pl/el/vi :not() carve-out.
 * IT ALSO HAD TO GO FOR THE COPY TO BE A COPY. At (0,6,1) it outranked every nav rule written
 * since -- including nav_rebuild_2026-08-30.css's own `font-size:12.5px` at (0,4,1), which is why
 * the label measured 12px and not 12.5px in a live Chromium on 2026-09-02, four days after that
 * file supposedly set it. agingwellinc.org's label is `.85rem`. As long as this line stood, no
 * later file could set the size at all without an !important it should not need.
 * THE SIZE NOW LIVES IN EXACTLY ONE PLACE: navcopy_2026-09-02.css, `.85rem` with a measured
 * per-language ladder -- the same mechanism their own stylesheet uses. Nothing is written below
 * 12px in THIS file, so decision_invariants.mjs `font_floor_min_12px`, which regexes app.css's raw
 * text, stays green for the same reason it was green before.
 * Kept on disk, commented, never deleted. Undo: uncomment this one line (and re-check the ladder).
 * Backup: app.css.BAK_NAVCOPY_20260902.
html:not([lang="pl"]):not([lang="el"]):not([lang="vi"]) .nav .nbtn .lb{font-size:12px}
 * ---- END RETIRED 2026-09-02 ---- */
/* ---- END NAV LABEL SHIPPED 2026-08-15 ---- */

/* ==============================================================================================
 * HIS RULING, 2026-08-15. THE SEARCH BAR IS ONE OBJECT AND IT IS ONE HEIGHT.
 *
 * `[RYAN 2026-08-15 08:10]` "I did rule on this, find it. Leads with a search bar that is the
 * exact height as the home page."
 *
 * Said in answer to a card that asked him whether Home should lead with search or with buttons.
 * He had ruled it before: `[RYAN 2026-08-12]` "I want to go back to the help page and make sure
 * that search bar that is tied to the search bar on the home page". The card should never have
 * been put to him.
 *
 * MEASURED FIRST, IN A REAL CHROMIUM, BEFORE ANY CSS WAS WRITTEN. Home and Help were ALREADY
 * exactly equal and no change was needed there: the card 119.39px, the typing box 56px, the
 * magnifier 56px, at 360x640, 390x844 and 414x896, in en/es/pt/km/ko, in dark, high-contrast,
 * text-xlarge and bigtext, in browse and in mid-search. They share app.css:268-276 and 981-982.
 *
 * THE ONE SEARCH BAR IN THE APP THAT DID NOT MATCH was the Info page's, which leads that page
 * under his R-010: `#csv-input` rendered 48px against Home's 56px. `.lk-input` at app.css:462
 * carries padding 13px, font-size 16px and a 1.5px border and NO min-height, so it computed to
 * 48. The 56px floor added for the Home/Help pair at app.css:981 never reached it.
 *
 * SPECIFICITY, STATED AND THEN VERIFIED BY MEASURING, NEVER BY REASONING. `#csv-input` is
 * (1,0,0). What it must beat: `.lk-input` app.css:462 (0,1,0), and the inline <style> block in
 * index.html:443-455 which is later in the document but whose strongest selector on this element
 * is (0,2,0). brand.css, brand_palette.css, brand_cats.css, brand_phone.css,
 * home_news_variants.css and nav_anchor_2026-08-14.css contain no rule matching `.lk-input` or
 * `#csv-input` at all, so brand.css loading AFTER app.css changes nothing here. The global
 * `*{box-sizing:border-box}` at app.css:9 is what makes min-height:56px render as exactly 56.
 *
 * THIS DOES NOT MERGE THE BOXES. R-010 stands: the Info search is his own, separate from the
 * Help search, with its own index. Only the geometry is made equal. #lk-input, the street-name
 * field, is deliberately NOT touched; it is not a search bar.
 *
 * Asserted by _TOOLS/falsify_search_bar_height.mjs at three viewports.
 * Undo: delete this block. Backup app.css.bak_2026-08-15_searchbar.
 * ==============================================================================================
 * RETIRED 2026-09-08 (SEARCHBARS session). This ID-specific patch was written when `#csv-input`
 * carried `class="lk-input"` (0,1,0) and needed a (1,0,0) rule to beat it - true on 2026-08-15,
 * VERIFIED FALSE TODAY: SEARCHPERFECT's 2026-09-02 markup unification removed the class entirely
 * (index.html:1146 `#csv-input` now carries no class attribute at all) and nested the input inside
 * `.res-search > .res-search-row > .res-combo`, so `.res-search input,.res-search .res-fauxinput
 * {min-height:56px}` at app.css:1518 already reaches it through the ancestor chain - the exact
 * "fix at the source" `[RYAN 2026-09-02 CARD_DECLAN]` asked for, and the exact kind of leftover
 * patch REMNANTS (2026-09-04) was hunting. VERIFIED by real Chromium render (not by reading code,
 * per his rule): with this line commented out, `#csv-input` still measures 56.0px (100%) / 112.0px
 * (200% zoom) at 390x844, 320x568 and 1280x800 - byte-identical to res-q/ev-q/news-q at every one -
 * see `_HARNESS/_searchbars_2026-09-08/results.json` and `SEARCHBARS_2026-09-08.md`. Kept here,
 * commented, never deleted, per LAW L3. Undo: uncomment the line below. */
/* #csv-input{min-height:56px}  RETIRED 2026-09-08, dead since SEARCHPERFECT 2026-09-02 removed .lk-input from this element; superseded by app.css:1518 .res-search input min-height:56px, reached via .res-search > .res-search-row > .res-combo > input ancestry. See note above. */
/* ---- END SEARCH BAR HEIGHT 2026-08-15 ---- */

/* ==============================================================================================
 * THE THIRD DOOR. 2026-08-15. THE CRISIS ROW WRAPS INSTEAD OF SHRINKING.
 *
 * `[RYAN 2026-07-23]` "big 911, medium call 988 medium call 988 small 211 nonemergency"
 * `[RYAN 2026-08-06]` 211 came out of the row and Text 988 went in
 * `[RYAN 2026-08-14]` "crisis buttons with 911 988 text 988"
 * `[RYAN 2026-08-15]` "211 stays off i rules on this many times update"
 *
 * WHAT WAS ACTUALLY ON THE SCREEN until today, measured in a real Chromium against the wall and
 * against BelovedWoonsocket_2026-08-15_b.zip, at 360x640, 390x844 and 414x896:
 *     Call 911  100.8x48    Call 988  100.8x48    More  84.5x48     (widths at 360)
 * and Text 988 at 0x0, inside #cmore-body, which ships with the `hidden` attribute set. The third
 * door he named three separate times was behind an extra tap, and the person it exists for is the
 * one who cannot speak. index.html now puts it in the row. This block is what stops that costing
 * anybody a tap target.
 *
 * THE RULE HE GAVE FOR THE TRADE: no tap target may get smaller, and if three doors plus More will
 * not fit on one line, MORE is what gives way, not a door. Three equal doors across the row would
 * have shrunk 911 from 100.8px to 95.3px at 360px. An older adult with a tremor has to hit 911, so
 * that is not a trade that was available. The row wraps instead:
 *     line 1   911            flex-basis 100%
 *     line 2   Call 988 | Text 988   flex-basis calc(50% - 5px), grow fills the line
 *     line 3   More           natural width, unchanged
 * MEASURED AFTER, same three viewports, English and Greek (the longest crisis labels of the
 * sixteen): every door is WIDER than it was, none is shorter than 48px, and More is the same
 * 84.5x48 it always was. Nothing shrank.
 *
 * calc(50% - 5px) and not calc(50% - 4px): 8px is the gap, so exactly-half leaves zero slack and a
 * sub-pixel rounding in either direction drops the second door onto its own line. 5px leaves 2px of
 * slack, flex-grow:1 takes it straight back, and three doors still cannot fit one line (146*3 + 16
 * = 454 against 302 available at 360px), which is what forces More down and keeps the doors big.
 *
 * SPECIFICITY, STATED AND THEN MEASURED, NEVER REASONED. What these must beat:
 *     app.css:606  .res-crisis-acc .crisis-bar-row   (0,2,0)   sets display:flex;gap:8px
 *     app.css:607  .res-crisis-acc .cq               (0,2,0)   sets flex:1 1 0
 *     app.css:608  .res-crisis-acc .cq.call          (0,3,0)
 *     app.css:609  .res-crisis-acc .cq.more          (0,3,0)   sets flex:0 0 auto
 * The row rule below is (0,2,0), a tie with 606, and it is LATER in the same file, so it wins on
 * source order. The door rules are (0,3,0), a tie with 608 and later, so they win the same way. The
 * More rule is (0,4,0) and beats 609 outright. No !important anywhere. brand.css, brand_palette.css,
 * brand_cats.css, brand_phone.css, home_news_variants.css and nav_anchor_2026-08-14.css contain no
 * selector matching .crisis-bar-row, .cq, .cq-911, .cq-988 or .cq-988t at all, so brand.css loading
 * after app.css at index.html:16 changes nothing here. Verified by reading getBoundingClientRect
 * and getComputedStyle back out of a live Chromium, not assumed.
 *
 * WHAT THIS DOES NOT DO, ON PURPOSE. It changes no font-size, no colour, no phone number and no
 * word. "big 911, medium call 988 medium call 988" is a SIZE sentence and it is still HIS to make.
 * The three size schemes at app.css:1271-1326 are html[data-crisis-sizes="a"|"b"|"c"], nothing sets
 * that attribute, and this block does not set it either. Layout only.
 *
 * 211 is not here and does not go here. It stays in the res-211 block above the kinds-of-help
 * buttons, in the noscript floor, in resCard's no-contact fallback and in CIVIC_211, all untouched.
 *
 * The >=760px block at app.css:627-629 puts the label beside the row and caps the row at 520px.
 * Wrapping is unchanged there: 911 takes the 520, the two 988 doors take 257.5 each, More drops
 * below. Measured at 800x900. Nothing overlaps.
 *
 * Undo: delete this block and restore index.html.bak_2026-08-15_threedoors.
 * Asserted by falsify_crisis_three_doors.mjs, five pages x three sizes x two languages.
 * ============================================================================================== */
/* CORRECTED 2026-08-16_crisislook. The wrap rules that stood here made Call 911 a full-width slab
 * on its own row, the 988 pair a second row, and More an orphan on a third: 216.6px of card where
 * 2026-08-14 had 104.6. Ryan rejected that look on sight, 2026-08-15: "That is not what i want.
 * The attached looks like shit". The three doors now share ONE row, as the row always had until
 * today, and the third door STAYS IN THE ROW: the no-extra-tap fix survives, only the geometry
 * changed. More moved up beside the header label (index.html, same button, same toggle, still a
 * 48px target), so no row dangles. 911 keeps primacy by being the only solid-red door, the widest
 * and the boldest; the 988 doors are equal partners in the fixed bar's own navy, so the card and
 * the bar finally speak one colour language: solid red = emergency call, solid navy = 988 call,
 * navy outline = 988 text. Exact bytes that were here: app.css.bak_2026-08-16_crisislook. */
.res-crisis-acc .crisis-bar-top{display:flex;align-items:center;justify-content:space-between;gap:8px}
.res-crisis-acc .crisis-bar-top .crisis-bar-lbl{flex:1 1 auto}
.res-crisis-acc .crisis-bar-top .cq.more{flex:0 0 auto;min-height:48px;padding:8px 15px}
.res-crisis-acc .crisis-bar-row{flex-wrap:nowrap}
.res-crisis-acc .crisis-bar-row .cq-911{flex:1.25 1 0;font-size:17px}
/* The !important below is not vanity: brand.css:35 hardcodes every .cq.call to #C00C0C with its
 * own !important and loads AFTER this file (index.html:222), so an ordinary rule here loses. The
 * doubled classes lift specificity to (0,5,0) so these win inside the row and nowhere else. The
 * 911 door is deliberately NOT here: it keeps the brand.css deep red and stays the only red door.
 * Navy #2C4A6E and outline #1F3550 are the fixed bar's own 988 colours (app.css .rrh-988,
 * .rrh-988t), so the card and the bar say the same thing: solid red = 911, navy = call 988,
 * outline = text 988. White on #2C4A6E is 8.6:1, past AAA, both modes. */
.res-crisis-acc .crisis-bar .crisis-bar-row .cq.call.cq-988{background:#2C4A6E !important;border-color:#2C4A6E !important;color:#fff !important}
.res-crisis-acc .crisis-bar .crisis-bar-row .cq.call.cq-988t{background:var(--surface) !important;color:#1F3550 !important;border:2px solid #1F3550 !important}
.phone.dark .res-crisis-acc .crisis-bar .crisis-bar-row .cq.call.cq-988t{background:#12161C !important;color:#E8EDF5 !important;border-color:#E8EDF5 !important}
/* ^ the extra .crisis-bar in each selector is load-bearing: brand.css also carries
 * .phone:not(.appearance-set) .res-crisis-acc .cq.call at (0,5,0) important, which ties a
 * five-class selector and wins on file order. Six classes beats it in every mode. Measured in a
 * live Chromium with .dark forced and .appearance-set absent, not assumed. */
/* Greek at 360 broke this card once (Stiilte minima sto 988 is the longest crisis label of the
 * sixteen). Under 375px the door TYPE steps down one notch so the words fit in fewer lines; the
 * doors themselves never drop under 48px tall and never leave the row. Font is not a tap target. */
@media (max-width:374px){
  .res-crisis-acc .crisis-bar-row .cq{font-size:14px}
  .res-crisis-acc .crisis-bar-row .cq-911{font-size:15.5px}
}
/* TABLET AND DESKTOP, >=760px VIEWPORT. app.css:627-629 stands the crisis label BESIDE the doors
 * there. The media query asks the VIEWPORT, but the shell it lands in is capped at 430px wide, so
 * the card is 386px, the bar 360px, and the label eats 189 of it. MEASURED ON THE UNCHANGED WALL,
 * 800x900, BEFORE ANY OF THIS: Call 911 was FORTY POINT NINE PIXELS WIDE in English, 39.3 in
 * Vietnamese, 49.4 in Bengali. That is a pre-existing defect and it is 911. It is not something
 * this block introduced, and it is not something this block is willing to leave alone either.
 * A flex-basis on its own does not fix it: flex-shrink pulls the row straight back down to its
 * min-content. The bar has to be ALLOWED TO WRAP, so that when the label and the doors cannot both
 * fit the LABEL is what moves and the doors keep the full width, which is the same rule as above,
 * one level up. Measured after, 800x900 English: 911 360x48, the two 988 doors 176x48 each. */
@media (min-width:760px){
  .res-crisis-acc .crisis-bar{flex-wrap:wrap}
  .res-crisis-acc .crisis-bar-row{flex:1 1 320px}
}
/* ---- END THE THIRD DOOR 2026-08-15 ---- */

/* ==============================================================================================
   EASIER TO TAP, 2026-08-16 (the selection pass).
   `[RYAN 2026-08-15]` "Investigate user interface everywhere i think there can be easier ways to
   select in places instead of clicking a hyerlink."
   Measured in a real Chromium at 390x844 before this block existed (sel_officials_BEFORE.png):
   the Info page held 16 links 17.0px tall (8 Email, 3 Website on the officials roster, 4 "See the
   official page", 1 "Learn more about the Situation Table") plus the 24.8px "Your local
   government" pulldown toggle. The smallest target anywhere else in the app is 40px.
   Two moves, and neither one makes a default-visible page taller:
   1. Inline links KEEP their link look. He asked for hyperlinks, not more rectangles
      (`[RYAN 2026-07-23]` "way more chances for hyperlinks or easier access for camella rather
      than finding that block", SESSION_080 OBS-14). Their tap area grows to ~45px through
      vertical padding, which on an inline element paints nothing and moves no layout.
   2. A single link standing on its own line becomes a full-width tappable row, the same 44px
      block-row grammar the "Your local government" link list on this same page has used since
      index.html:499. The words do not change, the whole row catches the finger.
   No target got smaller. No new color. Nothing explains itself.
   ============================================================================================== */
/* Officials roster: Call / Email / Website. 17px tall before; ~45px of finger room after, the
   words sit exactly where they sat. The 13px font goes to 14, still under the 14.5px name. */
.off-acts{font-size:14px}
/* WORDMARK 2026-09-24: 8px became 10px, and the negative margin went with it. This rule won the
   HEIGHT on 2026-09-24 (17px to 45px) and never looked at the width. Tonight's all-views,
   all-sixteen-languages sweep measured the shortest word in this row, "Site", at 40.00 x 49.69 in
   every language: four pixels short, on 29 anchors across the Officials roster. 4 chars of 14px
   Barlow plus 2 x 10px of padding is 44.00 exactly, measured, and the equal negative margin means
   the words sit on the same pixels they sat on tonight. "Call" and "Email" were already past 44 and
   only get wider. The horizontal overlap with the chip beside it goes from 10px to 14px, and the
   CENTRES stay 56px apart, so elementFromPoint at each chip's own centre still resolves to that
   chip: measured, all three, not assumed. */
.off-acts a{padding:14px 10px;margin:0 -10px}
/* "See the official page" under each group, and the Situation Table learn-more: whole row taps.
   The font-size !important is needed only because app.js writes 13px inline on these two. */
.off-inner > div > div > a[rel="noopener"],
.st-section > div > a[rel="noopener"]{display:block;box-sizing:border-box;min-height:44px;padding:13px 0;font-size:14px !important}
/* ==============================================================================================
   BW-15, 2026-09-24. THE RULE ABOVE HAS NEVER MATCHED A SINGLE ELEMENT ON THIS PAGE.
   ----------------------------------------------------------------------------------------------
   It asks for `a[rel="noopener"]`. Measured 2026-09-24 in a real render, the anchors it is aimed
   at carry NO `rel` attribute at all: app.js writes them with an inline style
   (`font-size:13px;color:var(--sky);font-weight:700`) and nothing else. So the selector matched
   nothing, the rule painted nothing, and both links have been 17px tall for as long as the rule
   has existed. The comment above says "17px tall before; ~45px of finger room after". The "after"
   never happened, on these two. This is exactly the failure LAW L12 names: a fix whose green was
   never pointed at the thing it guards.
   The attribute requirement is dropped. Everything else is carried across unchanged from the rule
   above, including the `font-size:14px !important`, which is still needed for the same stated
   reason: app.js writes the size inline and an inline style beats a stylesheet.
   Proved by hit test at each element's own centre after scrolling it into view, not by reading
   this declaration back: 17px to 44px, elementFromPoint returns the anchor itself.
   ============================================================================================== */
/* 2026-09-24 DATAFIX, LAYOUTFIX HANDBACK 4 TAKEN. `font-size:14px` NO LONGER NEEDS `!important`.
   It needed it because app.js wrote `font-size:13px` inline on the "See the official page" anchor
   and `font-size:13.5px` inline on the Situation Table learn-more anchor, and an inline style beats
   a stylesheet. Both inline sizes are gone from app.js as of tonight, so this is a plain 14px and
   the override is deleted. Measured the same on both sides: 44px tall, 14px type. */
.off-inner > div > div > a,
.st-section > div > a{display:block;box-sizing:border-box;min-height:44px;padding:13px 0;font-size:14px}
/* The "Your local government" pulldown toggle was the only sub-44px toggle in the app. */
#csv-gov-sum{padding:10px 0}
/* In-sentence civic words (his cooling-centers ask) and the source note in a resource sheet:
   same invisible growth. Kept small horizontally so neighbouring words stay themselves. */
.note a{padding:12px 4px;margin:0 -4px}
/* ==============================================================================================
   BW-15, THE THREE REMAINING UNDER-44px CONTROLS OUTSIDE HELP, 2026-09-24.
   ----------------------------------------------------------------------------------------------
   Measured in a real render at 390px Normal, before these rules:
     button.ev-title-btn   26.8px tall  (the event TITLE on Home: "Autumnfest", "Election Day")
     h3.nl-citybtn         24.8px tall  ("City services & local info", a heading that is a control)
   Both grow by the same idiom this file already uses on `.note a` one line above and on
   `.off-acts a`: vertical padding for the finger, an equal negative vertical margin so the words
   stay on the pixels they were already on. Nothing moves, nothing is re-worded, nothing is
   re-linked, and no font-size changes. The event title keeps `padding-inline:0` so the title still
   starts flush with the card's own text column.
   `.nl-citybtn` carries an inline `margin:18px 0 9px` from app.js, so its negative margin is
   written as `margin-block` with `!important`; that is the only `!important` here and it exists
   solely because an inline style beats a stylesheet. HANDED BACK: move that inline margin into a
   class in app.js and the `!important` can go.
   ============================================================================================== */
.event-row .ev-main h3 > .ev-title-btn{box-sizing:content-box;min-height:26px;padding-block:9px;margin-block:-9px}
/* 2026-09-24 DATAFIX, LAYOUTFIX HANDBACK 3 TAKEN. The heading's colour, size and margin used to be
   written inline on the <h3> in index.html, which is why the line below needed `!important` on
   `margin-block` and nothing else in this file did. They are a class now, in the file that owns
   every other appearance of this heading, so the `!important` is gone. Identical pixels: 18px above
   and 9px below, minus the 10px of invisible finger padding on each side, is the same 8px / -1px
   the !important rule was already computing. */
.nl-cityhead{color:var(--navy);font-size:16px;margin:18px 0 9px}
.nl-citybtn{box-sizing:content-box;min-height:26px;padding-block:10px;margin-block:8px -1px}
/* In-sentence civic words (his cooling-centers ask, 2026-07-11) are buttons, and a button will not
   stay inline when padded, so the finger room comes from an invisible reach layer instead:
   zero layout, zero paint, the tap box goes from 24px to 48px tall. */
.civic-link{position:relative}
.civic-link::after{content:"";position:absolute;left:-6px;right:-6px;top:-12px;bottom:-12px}
/* 2026-09-24 BW sweep. This button sits INSIDE a sentence ("call your nearest cooling centers"),
   so it inherits the sentence's line box but not the sentence's ability to break: 36 flagged boxes
   across the 1,728-cell sweep, all of them one long word in a narrow zoomed line. `anywhere` lets
   it break where the sentence around it already breaks. The invisible reach layer above is
   untouched, so the tap box stays 24px taller and 12px wider than the words, which is what makes
   this a 44px+ target without padding a word inside a sentence. */
.civic-link{overflow-wrap:anywhere;max-width:100%}

/* 2026-08-21 HOME CATEGORY CHIPS, REBUILT, OFF BY DEFAULT.
   The 047 note at line 327 above records the old .hh-chips rules removed as dead because
   renderHomeHelp discarded the block it computed. Ryan 2026-08-04 had asked for the old
   buttons FIXED ("too big and weird and not up to par"), not deleted. These are the fix:
   one compact scrollable row, pill buttons in the app's own arrow style (.hl-arrow at
   line 806: navy border, surface fill, navy text, so dark and high contrast follow the
   tokens for free). Inert until renderHomeHelp inserts the block, which only happens
   behind window.HOME_CHIPS='on' or ?chips=on. His tap turns them on, not ours. */
.hh-chips{display:flex;flex-wrap:nowrap;gap:8px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding:10px 2px 4px;scrollbar-width:none}
.hh-chips::-webkit-scrollbar{display:none}
.hh-chip{flex:0 0 auto;min-height:44px;padding:8px 16px;border:1.5px solid var(--navy);border-radius:999px;background:var(--surface);color:var(--navy);font-size:15px;font-weight:700;line-height:1.2;white-space:nowrap;cursor:pointer}
.hh-chip:active{background:var(--sky-soft)}

/* BUILD-EVENTS 2026-08-21: price chip + labeled event detail lines. Additive; both render ONLY when a
   record carries the fields (eventRow, app.js). Paid chip is var(--navy) on var(--surface) text, which
   stays readable in all four appearance maps (light, dark line 378, high-contrast 554, dark HC 560)
   because the two variables flip together. Free chip uses the green pair like .chip.youth. Detail links
   inherit the 44px tap floor. Undo: delete this block; backup app.css.PRE_BUILDEVENTS_20260821_222130.bak
   in _BUILD_BACKUPS. */
.chip.ev-cost{background:var(--navy);color:var(--surface);margin-left:6px}
[dir=rtl] .chip.ev-cost{margin-left:0;margin-right:6px}
.chip.ev-cost-free{background:var(--gold);color:var(--bw-navy)}   /* 2026-09-18 W3.1-5: was green-soft/green; the FREE badge is a Community Gold badge, Deep Navy 5.35. Not the 211 door. */
.ev-dets{margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}
.phone.dark .ev-dets{border-top-color:rgba(255,255,255,.10)}
.ev-det{font-size:15px;line-height:1.5;margin-top:6px}
.ev-det:first-child{margin-top:0}
.ev-det-lbl{font-weight:700}
.ev-det-cost strong{font-size:16px}
.ev-det-callfirst{font-weight:700}
.ev-det a{display:inline-flex;align-items:center;min-height:44px;padding:0 2px;color:var(--sky);font-weight:700;text-decoration:none}

/* ==================================================================================================
 * EVENTS BY MONTH. The styling half of the fix; the bucketing half is a six-line change in
 * app.js's renderEvents, which is NOT this agent's file and is written out literally in the
 * BUILD-UI handoff for the holder of app.js to apply.
 *
 * WHY. The events file grew from 27 records to 155 on 2026-08-21, 54 of them Stadium Theatre shows
 * running to May 2027 under a new `arts` chip. renderEvents buckets into exactly two blocks, "This
 * week" and "Coming up". MEASURED in a real Chromium render at 390x780: 79 upcoming rows, in two
 * blocks, and the second block held about three quarters of them. On a phone that is a wall.
 *
 * WHAT THIS PAINTS. One <details> per calendar month, first month open, the rest closed with the
 * count on the right, so the page opens at roughly one screen instead of seventy-nine rows and a
 * person looking for something in November taps once to get there. <details>/<summary> is native:
 * it is keyboard-operable and announced as a disclosure with no ARIA to get wrong, and it degrades
 * to an open list if the JavaScript that builds it never runs.
 *
 * The month name comes from toLocaleDateString(locale(),{month:"long",year:"numeric"}), so all
 * sixteen languages get their own month names and NO new translation string is needed anywhere.
 *
 * NO RED. The chevron and the count badge are --sky and --sky-soft. Red stays crisis-only.
 * Until app.js is changed these rules match nothing and paint nothing.
 * ============================================================================================== */
.ev-month{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:0;margin-bottom:12px;overflow:hidden}   /* 2026-09-18 W3.1-25: was border-radius:16px */
.ev-month-sum{display:flex;align-items:center;gap:10px;min-height:56px;padding:14px 16px;cursor:pointer;
  font-family:var(--font-label);font-weight:700;font-size:16.5px;color:var(--navy);list-style:none}
.ev-month-sum::-webkit-details-marker{display:none}
.ev-month-sum::marker{content:""}
.ev-month-lbl{flex:1 1 auto;min-width:0}
.ev-month-n{flex:none;background:var(--sky-soft);color:var(--sky);border-radius:999px;padding:3px 10px;font-size:13px;font-weight:800}
.ev-month-sum::after{content:"";flex:none;width:9px;height:9px;margin-inline-start:2px;
  border-inline-end:2.5px solid var(--sky);border-bottom:2.5px solid var(--sky);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .2s}
.ev-month[open] .ev-month-sum::after{transform:rotate(225deg) translate(-2px,-2px)}
.ev-month-sum:focus-visible{outline:3px solid var(--sky);outline-offset:-3px;border-radius:var(--radius)}   /* SHAPE 2026-09-22: was border-radius:14px */
.ev-month-body{padding:0 16px 14px}
.phone.dark .ev-month-n{background:var(--sky-soft);color:var(--navy)}   /* 2026-09-18 W3.2: was #1f2d3d/#9cc2ec; gold on #1A3A4A 4.58 */
.phone.dark .ev-month-sum{color:var(--ink)}   /* 2026-09-18 W3.2: was #dfe8f2 */

/* ==================================================================================================
 * THE SEARCH BAR STAYS PUT. OPEN.json OI-269, open since 2026-08-13.
 *
 * `[RYAN 2026-08-13]` "on this and other pages i want you to audit the mlk compaction and the top
 * search bar, it shouldnt get covered up or get janky when the user scrolls back up."
 *
 * MEASURED BEFORE, in a real Chromium render, Help page, scrolled down and then back up:
 *   390x780, Normal text .... the box was 265px ABOVE the top of the screen at scrollY 400 and was
 *                             still 65px above it at scrollY 200. It was simply gone.
 *   390x780, 150% ........... at scrollY 320 the box was on screen at top=29 and the MLK banner
 *                             <img> was the element painted over it. Covered, exactly as he said.
 *   320x780, banner shown ... same, 272px off the top.
 * The banner collapses on scroll and app.js:305 recomputes --hdrh when it does, but the search
 * itself had no sticky rule of any kind, so it just left.
 *
 * MEASURED AFTER: "search always visible and uncovered" at every one of the 12 combinations of
 * 320/390 px wide, 100/150/200 percent text, and announcement banner present or absent.
 *
 * WHY NOT top:var(--hdrh). --hdrh is published by app.js as .hdr.offsetHeight, which is UNZOOMED
 * layout px, and it is then consumed inside .phone, which zoom multiplies. Measured at 390px:
 * --hdrh reads 96px at the 150 percent step while the header really ends 192 real px down, so an
 * element stuck at that offset sits 48px UNDER the header. --bw-stick is published by index.html
 * as (header + announcement bar) real px DIVIDED BY the phone's live zoom, so it lands correctly
 * at every text size. app.js is not touched; --hdrh keeps its own meaning for .topbar.
 *
 * IT UN-STICKS ON A SHORT SCREEN, ON PURPOSE. W3C technique C34 warns that sticky content is a
 * real problem for people who need Reflow, and a phone with the keyboard open is a short screen.
 * https://www.w3.org/WAI/WCAG22/Techniques/css/C34
 * Below 520px of viewport height the box goes back to scrolling away exactly as it does today.
 * Verified: at 390x420 the sticky behaviour is measurably absent.
 *
 * NN/g on sticky headers: keep them small and un-animated. There is no transition here and the
 * box is the same box it always was. https://www.nngroup.com/articles/sticky-headers/
 *
 * z-index 36 sits UNDER .hdr (40) and .topbar (38) so it can never hide the banner or an
 * announcement, and over page content so the typeahead listbox still paints on top of results.
 * ============================================================================================== */
/* HOME STICKS THE WRAPPER, NOT THE CARD, AND THAT IS NOT A STYLE CHOICE. A sticky box can only
   travel inside its own parent. On Help and News the search is a direct child of a full-height
   <section>, so it has the whole page to travel. On Home the card lives inside #home-help, a div
   exactly as tall as the card itself, so `position:sticky` computed correctly, reported
   top:191.031px, and moved NOTHING: measured, the Home search was still 313px above the top of
   the screen at scrollY 400. The range has to come from the parent, so #home-help is the sticky
   box. It gets --canvas so the collapsed margin below the card cannot show page text through.
   Home is UNCHANGED by the 2026-08-30 fix below (still `position:sticky`, kept on purpose): its
   whole travel range is bounded by #home-help's own height, which is why the measurement above
   found it moved nothing extra to cover - there is no long, independently-scrolling results list
   below it the way Help and News both have. Re-proven by stickreserve_2026-08-30's own falsifier. */
/* CORRECTED 2026-09-24 (LAYOUT2). `position:sticky` is deleted from this rule, not annotated.
   `[RYAN 2026-09-24]` "the help and news bars stay static in place and it covers up the other
   content". He is on a PC. The 2026-09-02 SCREENSPACE pass below un-stuck all three top boxes on a
   PHONE and deliberately left them stuck at 901px and up; nobody had measured a desktop width since,
   and the occlusion harness run tonight shows exactly what he is looking at: at 320, 375 and 390 the
   only two things that ever paint over a word are the header and the dock, both intended chrome; at
   1024 and 1440 there are FOUR, and the two extra are these boxes. 40 covered words at desktop, 0 at
   any phone width. The rule the file already states in its own words a few blocks down, "ON A PHONE,
   NOTHING AT THE TOP IS FIXED EXCEPT THE HEADER", was right and was simply not carried far enough.
   It now holds at every width.
   `position:relative`, NOT `static`: z-index only applies to a positioned box, and app.css:992
   (`#home-help:has(.res-suggest.open){z-index:402 !important}`) plus the z-index:36 below are the
   whole of the typeahead stacking story written out at app.css:901-963. `relative` with no offsets
   occupies the identical space `static` does and keeps that story intact. The PHONE path is not
   touched by this line at all: the @media (max-width:900px) block below still sets `static` there,
   at equal specificity and later in the file, exactly as it shipped.
   Undo: NEVER_OVERWRITE_BACKUPS/layout2_2026-09-24/app.css.PRE_LAYOUT2. */
#home-help{position:relative;z-index:36;margin-top:0;background:var(--canvas)}
#home-help .res-search{margin-top:0}

/* ==================================================================================================
 * 2026-08-30: HELP AND NEWS SEARCH STOP BEING STICKY AND BECOME A RESERVED FIXED BAR. WHY.
 *
 * MEASURED THIS SESSION, real headless Chromium, Help page, a query with a "Call 211" fallback
 * card rendered, `elementFromPoint` at that link's own on-screen centre at every scroll position
 * where it is fully inside the viewport: 100% of sampled positions landed on something else, not
 * the 211 link - the sticky search box (z-index 36) painting over it. This is not a corner case;
 * it is how `position:sticky` behaves BY SPEC. A sticky element reserves space in the flow equal
 * to its own STATIC box, not to the space it visually occupies once stuck - so once scrolled past
 * its natural position, every sibling below it keeps flowing into the screen band the stuck box now
 * paints over, and whichever one currently sits there loses its own tap to the box on top of it.
 * `#view-community > .res-search` and `#news-search-wrap` sit directly above a long, independently
 * long results list (Help: every matching resource card; News: every story) - exactly the shape
 * that makes this failure mode unavoidable under `sticky`, unlike `#home-help` above (bounded travel,
 * left alone).
 *
 * THE FIX: `position:fixed` (truly out of flow, no ambiguity about what "reserves" its space) PLUS
 * a real reserved gap ahead of it, built the same way `--bw-dockh` already reserves the bottom for
 * the fixed dock (bottom_dock_2026-08-15.css/.js) - a small, JS-measured spacer element inserted
 * immediately before the search box, in normal flow, sized to the box's own real
 * getBoundingClientRect() height by stickreserve_2026-08-30.js. Nothing below the search box can
 * ever scroll into its screen band again, because that band is no longer part of the normal flow
 * anything else occupies - the spacer already claimed it, honestly, the same way the dock's bottom
 * padding does. THE UN-STICK RULES BELOW ARE UNCHANGED IN INTENT: short viewport and 150/200% text
 * still fall back to `position:static` (C34), and the spacer height collapses to 0 in lockstep so no
 * double gap appears. z-index, top offset and the 45%-budget reasoning above are untouched - only
 * the mechanism (fixed+spacer, not sticky) changed, because sticky was the one that could not
 * describe reserved space to the elements meant to obey it. Guarded by falsify_stickreserve_2026-08-30.mjs.
 * Undo: restore #view-community > .res-search / #news-search-wrap to the commented sticky rule in
 * app.css.bak_20260830_180225_stickreserve, and remove stickreserve_2026-08-30.js's <script> tag. */
/* JANK_INFO_NEWS_SETTINGS 2026-09-02: split from one shared rule into two. Both boxes still share
 * every property except `top`. Help's `.res-search` is the FIRST element in `#view-community`, so
 * the fixed band at `top:var(--bw-stick)` IS its correct resting place - unchanged below, byte for
 * byte, proven unaffected by `_HARNESS/_jank_infonews_settings_2026-09-02/probe_news.mjs`.
 * `#news-search-wrap` sits AFTER `<h2>News</h2>` in the DOM (index.html), so pinning it to the SAME
 * viewport offset painted the fixed box directly over that heading - measured this session,
 * h2Hidden:true, overlapPx:37.1875 at 390x844. `--bw-newsh2h` is a live getBoundingClientRect()
 * measurement of the News h2's own real height, published by stickreserve_2026-08-30.js
 * (measureHead()), correct at every language string length and text-size step, never a guessed
 * number. Undo: restore both rules from app.css.bak_20260902_171642_newssearchbox. */
/* CORRECTED 2026-09-24 (LAYOUT2). `position:fixed` and every piece of geometry that existed only to
   serve it (top, left, right, max-width, margin:0 auto) are DELETED from both rules, not annotated.
   `[RYAN 2026-09-24]` "the help and news bars stay static in place and it covers up the other
   content". These two boxes ARE the help bar and the news bar, and he is reading them on a PC at a
   width no pass had measured since they were un-fixed on the phone on 2026-09-02.
   MEASURED TONIGHT, real headless Chromium over real HTTP, _HARNESS/LAYOUT2_OCCLUSION_2026-09-24.mjs,
   180 cells, 6 views x 5 widths x 3 scroll positions x English and Arabic, hit-testing the centre of
   every painted run of text against elementsFromPoint:
     320 / 375 / 390   two covering bars, .hdr and #bw-dock, both intended chrome, both freed by a
                       scroll (the dock covers nothing at all at the bottom of any page, the header
                       covers nothing at the top of any page).
     1024 / 1440       FOUR covering bars. The two extra are these, and they are not freed by a
                       scroll in the way the chrome is, because they float over a list that is
                       longer than the screen at every position it can reach. 28 covered words.
   The 2026-08-30 fixed+spacer mechanism is therefore retired at every width, and with it the reason
   for .bw-stick-spacer. The intent it was built to serve is unchanged and is now served the simple
   way: the box is in normal flow, so the space it occupies is the space it occupies.
   `position:relative`, NOT `static`, for the same reason given at #home-help above: z-index only
   applies to a positioned box and app.css:901-963 depends on the 36 below. `relative` with no
   offsets occupies the identical space `static` does. THE PHONE PATH IS UNTOUCHED: the
   @media (max-height:568px), (max-width:900px) block below still sets `static` and the in-flow
   margins there, at equal specificity and later in the file, exactly as it shipped.
   Undo: NEVER_OVERWRITE_BACKUPS/layout2_2026-09-24/app.css.PRE_LAYOUT2. */
#view-community > .res-search{
  position:relative;
  margin:0 0 8px;   /* HELPLAY 2026-10-01: was `2px 0 16px`. Gutter between two cards, not reading space; 8px is the gap this page already uses between the crisis card and the 211 card. #news-search-wrap below keeps 2px 0 16px: this is the Help page only. */
  z-index:36;
}
#news-search-wrap{
  position:relative;
  margin:2px 0 16px;
  z-index:36;
}
/* the spacer stickreserve_2026-08-30.js inserts immediately before the search box. Its box is gone
   at every width now that nothing above it is out of flow; the script still writes a height into it
   from getComputedStyle(position), which is never `fixed` any more, so it writes 0 and this rule
   says the same thing independently, so a stale inline height cannot leave a hole. */
.bw-stick-spacer{display:none;width:100%;height:0}

/* ==================================================================================================
 * --bw-topfixh / scroll-padding-top. THE FIX tapfix_2026-08-27.css NAMED AND COULD NOT BUILD.
 *
 * tapfix_2026-08-27.css's own "STILL OPEN (b)" note, dated 2026-08-27: "THE STICKY TOP OCCLUDES
 * TOO, and is not fixed here... The same one-line cure exists - scroll-padding-top - but there is
 * no measured variable for the sticky height the way --bw-dockh exists for the dock... It is
 * reported, not guessed at." stickreserve_2026-08-30.js now publishes that variable, --bw-topfixh,
 * the same way bottom_dock_2026-08-15.js publishes --bw-dockh: a live getBoundingClientRect()
 * measurement of whichever top-anchored persistent chrome (.hdr, .topbar, and the fixed search box
 * above, only when it is actually fixed) is really on screen right now, not a number typed by
 * anybody. THE REGRESSION THAT MADE THIS URGENT, MEASURED THIS SESSION: poweredby_2026-08-30.js's
 * new credit line grew `.hdr` by roughly 14-16px, unrelated to anything in this fix, and pushed
 * this exact long-documented gap from marginal to total - 0 of 12 Help doors opened on the first
 * tap at 320x568, every one landing on `#powered-by` or `.powered-by-mark` (falsify_occlusion_
 * 2026-08-28.mjs, run against the live wall, this session). scroll-padding-top makes a scroll that
 * comes to rest UNDER that live-measured band impossible, the same guarantee tapfix_2026-08-27.css
 * already gives the BOTTOM of the screen via scroll-padding-bottom + --bw-dockh - mirrored here,
 * not reinvented. Guarded by falsify_stickreserve_2026-08-30.mjs. Undo: delete this rule and
 * stickreserve_2026-08-30.js's measureTopStack() call. */
:root,
body{
  scroll-padding-top: var(--bw-topfixh, 132px);
}

/* ON A PHONE, NOTHING AT THE TOP IS FIXED EXCEPT THE HEADER. 2026-09-02, SCREENSPACE.
   `[RYAN 2026-09-02]` "when did the search bars get permanently affixed, then on help with the stack
   at the bottom there is no screen space to actually get help"
   `[RYAN 2026-09-02 CARD_DECLAN]` "Keep 44px. Screen space matters more."
   `[RYAN 2026-08-13]` "the top search bar, it shouldnt get covered up or get janky when the user
   scrolls back up." - still honoured: the header no longer changes height on the way back up (the
   MLK banner that did is gone), so the box comes back uncovered and without a jump; the SCREENSPACE
   falsifier scrolls down 400px and back and hit-tests the input at its own centre.

   MEASURED 2026-09-02 (real Chromium, real webfonts, _HARNESS/_screenspace_2026-09-02/, 2,400 cells:
   5 sizes x 16 languages x 2 themes x 3 text steps x 5 views), chrome = header + every fixed box +
   the dock, as a share of the viewport once scrolled:
        Help  390x844 100%    50.1%  fixed box   ->  35.9%  box in flow   (middle 421px -> 541px)
        Help  360x640 100%    67.8%              ->  49.2%                (middle 206px -> 325px)
        Home  390x844 100%    46.1%  #home-help  ->  30.4%
        News  390x844 100%    40.6%              ->  30.4%
        agingwellinc.org, the site he built himself, at 390x844: 25.3% (sticky masthead 140px +
        74px tab bar; NO fixed search box).
   The table this block used to carry ("Normal 39% / 39% / 35%, all under the 45% budget") was
   measured at 390x780 against the old image banner and was stale: the text masthead is 119-152px
   and the dock on Help is three layers (crisis floor 47 + safety row 52 + nav 84 = 185px), so the
   fixed box put Help at half the screen. Deleted, not banner-stacked (LAW L13).

   THE RULE: the three top boxes (#view-community > .res-search, #home-help, #news-search-wrap) are
   in normal flow at every phone width (max-width:900px, the same phone breakpoint brand_phone.css
   uses), at every short viewport (max-height:568px: keyboard open, W3C C34), and at the 150/200
   percent text steps (below). They stay `position:fixed` ONLY on a wide, fine-pointer screen
   (901px+), where the 2026-08-30 stickreserve mechanism measured 25.1% chrome at 1280x800 and the
   box has 600px of page under it. THE SPACER MUST NOT OUTLIVE ITS BOX: whenever the box is not
   fixed, stickreserve_2026-08-30.js writes the spacer to 0 (it checks getComputedStyle position,
   never the viewport), and this rule forces the same 0 independently, so a stale reservation cannot
   leave a hole. --bw-topfixh (scroll-padding-top) and --bw-newsh2h are likewise published only
   from what is actually fixed, so both fall back to the header alone here.
   Guarded by _HARNESS/_screenspace_2026-09-02/falsify_screenspace_2026-09-02.mjs.
   Undo: restore app.css from the backup named in _HARNESS/_screenspace_2026-09-02/backup_name.txt. */
@media (max-height:568px), (max-width:900px){
  #view-community > .res-search,
  #home-help,
  #news-search-wrap{position:static}
  /* fixed-only geometry must not leak into the static fallback - the box goes fully back in flow,
     exactly as it did before 2026-08-30, and the reserved spacer collapses to 0 in the same breath
     so no double gap appears above it. */
  #view-community > .res-search{left:auto;right:auto;max-width:none;margin:0 0 8px !important}   /* HELPLAY 2026-10-01: split out of the pair below so the Help page gets the tighter gutter and News is left byte-identical. Was `margin:2px 0 16px !important` shared with #news-search-wrap. */
  #news-search-wrap{left:auto;right:auto;max-width:none;margin:2px 0 16px !important}
  .bw-stick-spacer{height:0 !important;display:none}
}
.phone.text-large  #view-community > .res-search,
.phone.text-large  #home-help,
.phone.text-large  #news-search-wrap,
.phone.text-xlarge #view-community > .res-search,
.phone.text-xlarge #home-help,
.phone.text-xlarge #news-search-wrap{position:static}
.phone.text-large  #view-community > .res-search,
.phone.text-xlarge #view-community > .res-search{left:auto;right:auto;max-width:none;margin:0 0 8px !important}   /* HELPLAY 2026-10-01: split out so the Help page gets the tighter gutter at the Large and Extra Large steps too. Was shared with #news-search-wrap at `margin:2px 0 16px !important`. */
.phone.text-large  #news-search-wrap,
.phone.text-xlarge #news-search-wrap{left:auto;right:auto;max-width:none;margin:2px 0 16px !important}
.phone.text-large  .bw-stick-spacer,
.phone.text-xlarge .bw-stick-spacer{height:0 !important;display:none}

/* THE HEADER IS CAPPED AT THE 200 PERCENT STEP FOR THE SAME REASON THE DOCK IS. On Help at 200
   percent the header (138px) and the dock (226px) were 46 percent of a 780px screen between them
   with nothing stuck at all, and the crisis floor inside that dock is not negotiable. The header
   is the only other thing there. brand.css:3 sets .hdr-row{display:none !important}, so the only
   things visible in it are the MLK banner IMAGE and the settings gear: an image of text, which SC
   1.4.4 explicitly excepts, and an icon button that is still half again its normal size at .75 x 2.
   No readable text is capped by this. Help drops from 46 percent of the screen to 42. */
.phone.text-xlarge .hdr{zoom:.75}


/* ==================================================================================================
 * FOLDED IN AT SOURCE, 2026-09-02. `[RYAN 2026-09-02]` "Open the wall files up. Go fix things at the
 * source and delete the patches that exist only to work around them." Three dated layer files whose
 * entire payload was ONE declaration each, kept as separate files only because this file could not
 * be edited. Each is retired to _to_delete/<name>.FOLDED_2026-09-02 (never deleted) and its
 * index.html tag is now a comment saying so. Record: C:\!Brain\LFG4\NEVER_OVERWRITE\FOLDED_2026-09-02.md
 * ================================================================================================== */

/* was tap44ev_2026-08-29.css (4,014 B, one request). `.wrr-flag` itself is defined in
   wrongreport_2026-08-28.css, which loads AFTER app.css; this rule still wins because
   `.event-row > .wrr-flag` is (0,2,0) against that file's (0,1,0), so specificity decides it and load
   order never has to. Measured before the fold: 0 of 9 sampled flags tap-testable at 390px without
   it, 9 of 9 with it. */
.event-row .ev-tags > .wrr-flag{ z-index: 2; } /* ARROWWRAP 2026-09-04: was `.event-row > .wrr-flag`; the flag moved inside .ev-ctl. CARDROW 2026-09-09: .ev-ctl retired, flag is now a direct child of .ev-tags, which already carries position:relative;z-index:2 as a row - this per-child rule is now redundant with that but kept, harmless, so no selector in this file silently stops matching anything. */

/* was demofix_2026-08-26.css (1,882 B, one request). The one class demofix_2026-08-26.js uses to hide
   a row instead of deleting it (LAW.md L5, never delete). Nothing else in the build declares it. */
.dfx-off{ display: none !important; }

/* was crisisfix_2026-08-27.css (4,524 B, one request). CRISIS PATH, LAW.md L1. It restored the
   floor bar to the full 2x at the 200 percent step (2 x .75 x 1.3333) while the rest of the dock
   and the header sit at a net 1.5x.
   CORRECTED 2026-09-02, SCREENSPACE, with a render: at 2x on a 320px phone the three doors are
   95/79/85px wide and the labels broke MID-WORD ("Emerg ency", "Crisis / Ment al Healt h", "Text
   in- stead"), the bar stood 197px tall, the dock 368px, the header 247px, and Help was 100% chrome:
   zero pixels left to get help (_HARNESS/_screenspace_2026-09-02/dock_320x568_200_en.png). At the
   net 1.5x the 150 percent step already uses, the same bar is 87.5px with every word whole
   (dock_320x568_200_en_z1.png), and the three doors are still twice the 44px floor.
   `[RYAN 2026-09-02 CARD_DECLAN]` "Keep 44px. Screen space matters more." The NUMBERS 911 / 988 /
   text 988 are untouched; only the 200-percent zoom multiplier is gone, so the floor bar scales
   exactly as it does at 150 percent. Re-proved after the change: 911, call 988, text 988 and 211
   hit-testable at centre and four radius-aware corners, 16 languages x 5 sizes x light/dark x
   100/150/200 (falsify_screenspace_2026-09-02.mjs, CRISIS gate). Undo: put `zoom:1.3333333` back. */
.phone.text-xlarge #res-helpbar-fixed{ zoom: 1; }


/* ==================================================================================================
 * SEARCHPERFECT 2026-09-02. FOUR SEARCH BOXES, ONE CONTRACT, AT SOURCE.
 * `[RYAN 2026-09-02]` "a help page with a perfect search bar that works perfectly, and all search
 * bars throughout the app all work perfectly and are connected". The JS is app.js (searchBusy,
 * searchLand, SearchHub, searchWords, renderResources windowing), index.html (the Info panel) and
 * search_unify_2026-08-23.js (Events, News). Record: LFG4\NEVER_OVERWRITE\SEARCHPERFECT_2026-09-02.md
 * ================================================================================================== */

/* FEEDBACK. While a search is working the magnifier in the one search button becomes a turning ring.
   The ring is animated on transform and promoted to its own layer (will-change), so it keeps turning
   while the main thread is busy rendering the results, which is exactly the moment she needs to see
   it. Measured need: 445-763ms per keystroke at 1x, 2,196-3,722ms at 6x, nothing on screen. */
.res-search-row.is-busy .res-go{position:relative}
.res-search-row.is-busy .res-go > span{visibility:hidden}
.res-search-row.is-busy .res-go::after{content:"";position:absolute;left:50%;top:50%;width:22px;height:22px;margin:-11px 0 0 -11px;border-radius:50%;border:3px solid rgba(255,255,255,.35);border-top-color:#fff;animation:sp-spin .8s linear infinite;will-change:transform;box-sizing:border-box}
@keyframes sp-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){ .res-search-row.is-busy .res-go::after{animation:none;border-top-color:rgba(255,255,255,.35);border-right-color:#fff} }

/* CONNECTED. The one hand-off row at the bottom of a dropdown ("Find help: <her words>") and the same
   thing as a block under an empty Events/News list. Same 48px row contract as every other row. */
.res-suggest li.hub-sug{background:var(--sky-soft);align-items:flex-start;border-top:1px solid var(--border)}   /* 2026-09-18 W3.1-15: was #C9D6E6 */
.res-suggest li.hub-sug .cs-main{display:flex;flex-direction:column;gap:2px;flex:1}
.res-suggest li.hub-sug .cs-lbl{font-weight:750;color:var(--navy)}
.res-suggest li.hub-sug .cs-desc{font-size:12.5px;color:var(--muted);font-weight:400;line-height:1.35}
.hub-handoff{display:flex;align-items:center;gap:11px;width:100%;box-sizing:border-box;margin:10px 0 0;padding:12px 14px;min-height:56px;border:1.5px solid var(--border);border-radius:var(--radius);background:var(--sky-soft);color:var(--ink);font:inherit;font-size:16px;text-align:start;cursor:pointer}   /* 2026-09-18 W3.1-15: was #C9D6E6, radius 12px, text-align:left */
.hub-handoff .ic{font-size:20px;flex:none}
.hub-handoff .hh-main{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.hub-handoff .hh-lbl{font-weight:750;color:var(--navy);line-height:1.3}
.hub-handoff .hh-desc{font-size:12.5px;color:var(--muted);line-height:1.35}
.hub-handoff .hh-arrow{flex:none;font-size:20px;color:var(--navy)}
.hub-handoff:active,.hub-handoff:hover{background:var(--border)}   /* 2026-09-18 W3.1-15: was #e2ecf8 */
.phone.dark .hub-handoff{background:var(--sky-soft);border-color:var(--border);color:var(--ink)}   /* 2026-09-18 W3.2: was #1f2f45/#3B4A5E/#fff; cream on #1A3A4A 11.13 */
.phone.dark .hub-handoff .hh-lbl,.phone.dark .hub-handoff .hh-arrow{color:#fff}
.phone.dark .res-suggest li.hub-sug{background:var(--sky-soft);border-top-color:var(--border)}   /* 2026-09-18 W3.2: was #1f2f45/#3B4A5E */
.phone.dark .res-suggest li.hub-sug .cs-lbl{color:#fff}

/* THE WINDOW. "Show N more" after a category's first 15 cards (was paginate_2026-09-01.js's inline
   style; same look, now one rule). 56px tall, full width, one tap. */
/* 2026-09-18 W3.1-17: was border var(--border,#ccc) dashed, radius 12px, color var(--navy,#1c2530). Burgundy outline, 2px, 9.02 on cream. */
.bw-showmore{display:block;width:100%;min-height:56px;margin:10px 0;padding:14px;border:2px solid var(--navy);border-radius:var(--radius);background:transparent;color:var(--navy);font:inherit;font-weight:700;font-size:15px;cursor:pointer}
.bw-showmore:active,.bw-showmore:hover{background:var(--sky-soft)}
.phone.dark .bw-showmore{color:var(--navy);border-color:var(--navy)}   /* 2026-09-18 W3.2: was #fff/#3B4A5E; gold on ink 6.47 */


/* ==================================================================================================
 * FOLDED IN AT SOURCE, 2026-09-02, second tier. Same ruling as the block above.
 * ================================================================================================== */

/* was tapfix_2026-08-27.css (8,334 B, one request, two rules). A control that scrollIntoView() or
   window.scrollTo() brings to the bottom edge must stop above the opaque fixed dock. scroll-padding
   applies to the SCROLL CONTAINER (the document, so :root; body duplicated in case a build ever makes
   body the scroller). scroll-margin is the element-side twin so the same 144px is respected whichever
   end the browser starts from. Scoped to real controls, never `*`. Nothing else in the wall sets
   scroll-padding-bottom (re-checked 2026-09-02). Guarded by CHECK_TAPFIX_2026-08-27.mjs, whose
   preflight now accepts the rule living here. */
:root,
body {
  scroll-padding-bottom: calc(var(--bw-dockh, 144px) + 16px);
}
.res-crisis a[href^="tel:"],
.res-crisis a[href^="sms:"],
#res-211 a[href^="tel:"],
#view-community a[href],
#view-community button,
#view-community input,
#view-community select,
#view-community textarea,
#view-community [role="button"] {
  /* VIEWZOOM 2026-09-28 (DOCK2). SUPERSEDED VALUE, UNDO IS ONE LINE:
       scroll-margin-bottom: calc(var(--bw-dockh, 144px) + 16px);
     These selectors all match elements INSIDE `.phone`, and scroll-margin is resolved in the
     element's own coordinate space, so a rendered-pixel count is multiplied by .phone's zoom here
     exactly as `.view`'s padding is. Over-reserving, so it has never let anything come to rest under
     the dock; it has been stopping scrolls 52 to 144 px higher than it needed to at Large and Extra
     Large. Divided by the MEASURED --bw-viewzoom, same publisher, same reason as `.view` above.
     THE `:root, body { scroll-padding-bottom }` RULE ABOVE IS DELIBERATELY LEFT UNDIVIDED AND IS
     CORRECT: html and body are OUTSIDE `.phone` and carry no zoom, so rendered pixels are already
     the right unit there. That is measured, not assumed, and it is why these two rules that look
     like twins are not treated alike. Undo: put the one-term line above back. */
  scroll-margin-bottom: calc(var(--bw-dockh, 144px) / var(--bw-viewzoom, 1) + 16px);
}

/* was contrast_2026-09-01.css (2,012 B, one request). Its first half, `a.res-211{color:#fff !important}`,
   existed only to beat an inline style="color:inherit" on index.html's Get Help 211 card; that inline
   colour is removed at source today, so .res-211{color:#fff} (above, line ~201) applies on its own
   and no !important is needed. Its second half is a real base rule and lives here now: the plain
   <a class="btn"> had no colour rule at all and fell back to browser link blue, ~1.6:1 on the dark
   surface. */
.btn:not(.primary){ color:var(--navy); }
.phone.dark .btn:not(.primary){ color:var(--navy); }   /* 2026-09-18 W3.2: was #BFE0FF; gold on navy 5.35, on ink 6.47 */
/* DESIGNSYSTEM 2026-09-04 [A3-goldbtn-contrast]. THE RULE: a control whose FILL is the same in both themes keeps the same
   TEXT COLOUR in both themes. .btn.gold is filled with var(--gold) #F0CC00, which does not flip, so
   the line above painted #BFE0FF on bright gold: measured 1.15:1, the worst pair in the app, on the
   Info page in every language. #0B4FB0 is exactly what light mode renders on the same fill.
   Undo: delete this one rule. */
.phone.dark .btn.gold{ color:#102F3D; }   /* 2026-09-18 W3.2: was #0B4FB0 (blue on gold); Deep Navy on Gold 5.35 */

/* ---- ONE UNDERLINE, EVERYWHERE. THE PLAIN PROSE LINK. 2026-09-04. -----------------------------
 * Found while answering [RYAN 2026-09-04] "I noticed the orgs it now loks like it is double
 * underlined iti is odd." The org name had TWO lines; eighteen links in the Info view had NONE.
 * Measured 2026-09-04, real Chromium: every `<a href>` with no class at all - the tel: number, the
 * four "See the official page ›", the eight "[U+2709 U+FE0F] Email", the three "Website" - had no underline, no
 * (SHAPE 2026-09-22: the envelope in the quotation above was the literal character until tonight. It is
 *  written as its code points now for the same reason as the magnifier at the ONELINE block below, and
 *  the note there carries the full finding. SUPERSEDED, AND THE VALUE IS THE CODE POINT ITSELF: it was
 *  U+2709 ENVELOPE followed by U+FE0F VARIATION SELECTOR-16. Nothing was deleted and the sentence still
 *  says exactly what it said. app.css now holds ZERO emoji code points, in a rule or in a comment, and
 *  it never held one in a `content:` string: GLYPH_2026-09-22 section 2 recorded "app.css: a magnifier
 *  and an envelope in content: 2" and that attribution was wrong on both counts. Both were comments.)
 * border, no background and no fill. They were distinguished from the sentence around them by
 * COLOUR AND NOTHING ELSE. WCAG 1.4.1 forbids exactly that, and this app is built for older adults
 * and people with disabilities, for whom colour alone is the least reliable cue there is.
 *
 * THE CRISIS EXCLUSIONS ARE NOT DECORATION, THEY ARE THE POINT, AND THEY WERE FOUND THE HARD WAY.
 * The first draft of this rule was `.phone a[href]:not([class])` and nothing else, written on the
 * assumption that everything on the crisis path carries a class. IT DOES NOT. The falsifier's own
 * crisis guard caught five classless crisis anchors on the very first planted-bad run:
 * index.html:739 `<a href="tel:211" aria-label="Call 2 1 1...">` inside the Get Help 211 card,
 * index.html:1076 the Settings sentence `call <a href="tel:911">911</a> now ... <a href="tel:988">`,
 * and app.js:1005/1006/1021/1022, the crisis bar's own 911 and 988 doors. A rule one character
 * looser than this one would have restyled the crisis floor. LAW.md L1: never. So the three numbers
 * are named and excluded, literally, and a check that goes RED if any classless anchor survives on
 * the crisis path ships with this rule (_HARNESS/_underline_2026-09-04/).
 *
 * Whether the Settings sentence's inline 911/988 should get an underline of their own is a real
 * question and it is RYAN'S, not this file's. Not changed here.
 *
 * @layer IS LOAD-BEARING AND IT IS THE SECOND THING THE FALSIFIER CAUGHT ME GETTING WRONG.
 * The draft before this one was an ordinary rule and rested on "every button in this app carries a
 * class, so :not([class]) cannot reach one". THAT IS ALSO FALSE. The Find Help view renders its
 * action chips as CLASSLESS anchors inside `.acts` - `<a href="tel:4014454237">Call ...</a>`,
 * `<a href="https://health.ri.gov/...">Website</a>` - and app.css:463 `.res .acts a` gives them a
 * 1.5px navy border and text-decoration:none. Specificity (0,6,1) beat (0,2,1), so the draft rule
 * put an underline INSIDE a bordered button: a second double underline, created by the very fix
 * for the first one. The falsifier found it on the run after the edit, on a view I had not thought
 * to look at. Two wrong assumptions, both caught by measurement, neither by reading.
 *
 * A cascade layer states the real intent, which specificity cannot: UNDERLINE THE LINKS NOTHING
 * ELSE HAS STYLED. Any unlayered rule anywhere in the app - `.res .acts a`, `#citysvc .csv-gov a`,
 * a future one nobody has written yet - beats a layered rule no matter how specific either is, so
 * a link that already has a box keeps its box, automatically, with no list to maintain and no
 * possibility of this rule outranking a component again. A link with no other rule gets the
 * underline. A browser too old for @layer ignores the whole block and sees today's behaviour: the
 * failure mode is "no improvement", never "wrong".
 *
 * Same three declarations as the organisation name (org_card_2026-08-23.css:259) so there is one
 * look for a link in this app: 2px so it survives a high-DPI phone, 3px offset so it clears
 * descenders and Khmer subscript marks, skip-ink on. text-decoration adds no box, so no reflow.
 * Colour is the existing theme token; unchanged in light, and dark already resolves --navy to
 * #7da7d9, measured 7.12:1 on the dark canvas. */
@layer prose-links {
  .phone a[href]:not([class]):not([href^="tel:911"]):not([href^="tel:988"]):not([href^="tel:211"]):not([href^="sms:988"]){ color:var(--navy); text-decoration:underline; text-decoration-thickness:2px; text-underline-offset:3px; text-decoration-skip-ink:auto; }
  .phone a[href]:not([class]):not([href^="tel:911"]):not([href^="tel:988"]):not([href^="tel:211"]):not([href^="sms:988"]):focus-visible{ outline:3px solid var(--navy); outline-offset:2px; border-radius:3px; }
}

/* JANKLOAD 2026-09-02: the event row's "Website" pill, folded in from event_deeplink_row_2026-08-15.js style(), byte-for-byte the two
   rules that file injected as <style id="ev-site-style"> after load. app.js eventRow() renders the link itself now, so
   its style has to be here, in the first paint, or the link shows as a plain blue underline for the ~150 ms (1x) to
   ~500 ms (6x) before that file ran (measured 2026-09-02). That file still checks for #ev-site-style and injects the
   same text; identical rules twice change nothing. */
/* 2026-09-18 W3.1-7: the #2C6FB5 fallbacks on var(--sky) here and on .csv-hsds-sum were dead (--sky is defined) but blue when read alone; dropped. Radius 22px to var(--radius). */
.ev-site{display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:0 12px;margin-inline-start:8px;border:1.5px solid var(--sky);border-radius:var(--radius);color:var(--sky);font-size:13px;font-weight:700;text-decoration:none;vertical-align:middle;line-height:1}
.ev-site:focus-visible{outline:3px solid var(--sky);outline-offset:2px}

/* ---- HSDS FIELDS ON A GOVERNMENT BENEFIT PROGRAMME (2026-09-03). ------------------------------
 * Styles the disclosure app.js citySvcCard() / hsdsDetailsHtml() render under the sixteen
 * GOV_PROGRAMS rows: who it is for, cost, sign up, IDs & documents. It is here, in app.css, because
 * the markup is built in app.js, at source. No separate patch stylesheet.
 *
 * ONE-COLUMN ON PURPOSE, AND THAT IS THE SCREEN-SPACE FIX. The label sits ABOVE the value, never
 * beside it. The known reflow failure on this app is a text column squeezed next to something else
 * until it renders one letter per line at 150-200% text; a block label over a full-width value
 * cannot do that at any text size, in any of the sixteen languages, because there is no second
 * column to squeeze. It also costs less vertical space than a wrapped two-column row at 200%.
 *
 * 44px, KEPT. `[RYAN 2026-09-02]` "Keep 44px. Screen space matters more." The summary is the only
 * new tap target and it is 44px minimum. It is a SIBLING of the .csv-item link, never inside it, so
 * it steals nothing: the link keeps its full hit area.
 * COLLAPSED COSTS 44px AND NOTHING ELSE. Nothing inside a closed <details> is laid out or painted.
 * RTL: logical properties only (padding-inline-start, border-inline-start), so Arabic mirrors with
 * no separate rule and no [dir] selector. */
.csv-wrap{display:block}
.csv-wrap .csv-item{margin-bottom:0}   /* unscoped 2026-09-04 with the base rule above: the HSDS
   details block is a sibling of the link on BOTH pages, so the "no gap between the row and its own
   details drawer" rule has to reach both too. It was `.res-citysvc .csv-wrap .csv-item`, Help only. */
.csv-hsds{margin:0 0 8px;border:1px solid var(--border);border-top:0;border-radius:0 0 var(--radius) var(--radius);background:var(--surface)}   /* SHAPE 2026-09-22: was border-radius:0 0 12px 12px */
.csv-hsds-sum{display:flex;align-items:center;gap:8px;min-height:44px;padding:10px 14px;cursor:pointer;font-size:14px;font-weight:700;color:var(--sky);list-style:none}
.csv-hsds-sum::-webkit-details-marker{display:none}
.csv-hsds-sum::after{content:"";flex:0 0 auto;width:8px;height:8px;margin-inline-start:auto;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg) translateY(-2px)}
.csv-hsds[open] .csv-hsds-sum::after{transform:rotate(-135deg) translateY(-2px)}
.csv-hsds-sum:focus-visible{outline:3px solid var(--sky);outline-offset:-3px}
.csv-hsds-body{padding:2px 14px 12px}
.csv-hsds-row{margin:0 0 12px}
.csv-hsds-lb{font-size:13px;font-weight:700;color:var(--muted);line-height:1.45;margin:0 0 2px}
.csv-hsds-v{font-size:15px;line-height:1.5;color:var(--ink);overflow-wrap:break-word}
.csv-hsds-list{margin:2px 0 0;padding-inline-start:20px;font-size:15px;line-height:1.5;color:var(--ink)}
.csv-hsds-list li{margin:0 0 4px;overflow-wrap:break-word}
.csv-hsds-note{margin:10px 0 0;padding-inline-start:10px;border-inline-start:3px solid var(--border);font-size:13px;line-height:1.45;color:var(--muted)}
.phone.dark .csv-hsds{background:transparent}
.phone.dark .csv-hsds-v,.phone.dark .csv-hsds-list{color:var(--ink)}   /* 2026-09-18 W3.2: was #dfe7f2 */
/* GIVE THE WORDS BACK THEIR WIDTH AT 150% AND 200% TEXT. MEASURED, NOT GUESSED.
   `.phone.text-large{zoom:1.5}` / `.phone.text-xlarge{zoom:2}` (app.css:564-565) mean the layout
   box a 390px phone gives its contents is 260 CSS px at 150% and 195 CSS px at 200%. Out of that
   195, this block's own chrome was taking 14+14 body padding plus a 20px list indent = 48px, a
   quarter of the line, so a documents item broke at about eight characters and one sentence ran
   twenty lines. Trimming to 8+8 and 14 returns 18 CSS px, roughly a tenth of the usable line, on
   the surface where the line is shortest. Nothing is hidden and no list item is truncated: what a
   person has to bring is the one thing that stops a wasted trip across town, so it is never cut.
   Measured effect at 390x844, 200% text: see ELIGIBILITY_2026-09-03.md. */
.phone.text-large .csv-hsds-body,.phone.text-xlarge .csv-hsds-body{padding-left:8px;padding-right:8px}
.phone.text-large .csv-hsds-list,.phone.text-xlarge .csv-hsds-list{padding-inline-start:14px}
.phone.text-large .csv-hsds-sum,.phone.text-xlarge .csv-hsds-sum{padding-left:8px;padding-right:8px}

/* ==================================================================================================
 * SHIPPICKS 2026-09-06. THE WHEN-TO-CALL LINES, AT SOURCE, UNDER THE DOORS.
 * `[RYAN 2026-06-24]` asked for "when to call" guidance on the crisis buttons. The words were written
 * that night (cb911s / cb988s / cbText988s), translated into all sixteen languages, and never once
 * rendered: crisisrow_2026-08-27.js kept them behind ?crisis=1|2, and ?crisis=1 painted INTO a row
 * that bar_variants_2026-08-28.css hides on Find Help (html[data-bar="3"] .crisis-bar-row{display:
 * none}), so it could never show anything. `[RYAN 2026-09-06 LOOK_FOUR]` "C2 = Ship ?crisis=2. The
 * lines go under the doors." So the lines are STATIC MARKUP in index.html now (.crisis-when, inside
 * .res-crisis-acc, right after .crisis-bar-row), painted by the browser with JavaScript dead, and
 * translated by the same data-i18n pass as every other label on the card. No flag, no script, no
 * attribute. crisisrow_2026-08-27.js/.css are retired (renamed .RETIRED_2026-09-06).
 *
 * NO NEW ENGLISH, NO DIGIT WRITTEN HERE. Each line is the door's own shipped label (cb911 /
 * cbCall988 / cbText988, which carry the literal ASCII digits in all sixteen) and then the shipped
 * guidance key. NO NEW COLOUR: var(--ink) on the card, red stays for the doors. Nothing here touches
 * a door, an href, an aria-label, a size or the fixed floor bar. 13.5px is above the 12px floor
 * decision_invariants.mjs enforces. Cost measured 2026-09-05 at 390x844: +85.7px of crisis card,
 * all of it below the fold, zero chrome.
 * Undo: NEVER_OVERWRITE_BACKUPS/shippicks_2026-09-06/ (app.css and index.html PRE_SHIPPICKS_*).
 * ================================================================================================== */
.res-crisis-acc .crisis-when{margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:7px;list-style:none}
.res-crisis-acc .crisis-when-line{display:block;font-size:13.5px;line-height:1.35;color:var(--ink);overflow-wrap:break-word;hyphens:auto;margin:0;padding:0}
.res-crisis-acc .crisis-when-line .crisis-when-head{font-weight:800}
.res-crisis-acc .crisis-when-line .crisis-when-body{font-weight:500;opacity:.95}
.phone.dark .res-crisis-acc .crisis-when-line{color:var(--ink)}
@media (max-width:349px){.res-crisis-acc .crisis-when-line{font-size:13px}}
/* ---- END SHIPPICKS WHEN-TO-CALL ---- */

/* ONELINE 2026-09-09. [RYAN] "why is the search bar 2 lines now on the home page?"
   Measured on Home at 390x844: #home-help was 133px in two stacked parts.
     .res-search-head  23px  "[U+1F50D] What do you need? Type it here."
                             SHAPE 2026-09-22, JOB 3, THE LAST CSS MAGNIFIER: the line above is a
                             QUOTATION of what that heading rendered on 2026-09-09, not a rule, and it
                             held the only emoji code point anywhere in app.css. It is written as
                             [U+1F50D] now instead of the literal character, so a whole-file emoji
                             sweep reports this stylesheet clean and nobody has to re-open the file to
                             find out that its one hit was a comment. SUPERSEDED, AND THE VALUE IS THE
                             CODE POINT ITSELF: the character was U+1F50D LEFT-POINTING MAGNIFYING
                             GLASS. Nothing was deleted; the quotation still says exactly what it said.
                             THE HONEST FINDING, BECAUSE THE BRIEF ASKED FOR THIS FILE'S MAGNIFIER:
                             app.css has NO live magnifier and never reaches the screen with one. Its
                             `content:` strings hold three escapes in total and they are \2197, \25BE
                             and \25B4, all Arrows/Geometric Shapes, none of them emoji. Measured, not
                             reasoned: a text-node AND pseudo-element walk of all six views in English
                             light, English dark, Arabic and Khmer, 24 cells, found ZERO U+1F50D on
                             screen. GLYPH3_2026-09-22 section 6.2 attributed 2 of Help's remaining 11
                             emoji to "index.html / app.css"; BOTH were index.html (.res-search-head
                             .ic and .res-go), and ABOUT_2026-09-22 cleared both, which is why its own
                             count moved Help 13 to 11. The `bwg('search')` stroke glyph pasted at the
                             seven static sites in index.html was NOT copied here and no second
                             magnifier was drawn: there is still exactly ONE magnifier shape in the app.
     .res-search-row   70px   the box itself, whose own placeholder reads
                              "Search for help: food, rent, doctor, voting..."
   Two prompts for one box, one directly above the other, saying the same thing in
   different words. That is the ambient copy he has been cutting all week: "if this
   sentence disappears, does the user lose anything important?" It does not - the box
   still tells her exactly what to do, with examples, which the heading did not.
   HOME ONLY. Help keeps its heading, where the box sits under a page title rather than
   under a masthead and the label is doing real work.
   Buys 33px back at the top of Home, where the vote alert was already measured falling
   below the fold at 320x568 with large text.
   UNDO: delete this rule. NEVER_OVERWRITE_BACKUPS/navpin_2026-09-09/app.css.PRE_ONELINE_* */
#home-help .res-search-head{display:none}

/* PHONEFIT 2026-09-24. THE HELP HEADING GOES TOO, AND HERE IS WHY THE LINE ABOVE WAS HALF A FIX.
   `[RYAN 2026-09-24]` "search bar on home looks big and then the help page search bar it takes you
   to has text above it".
   He is describing the handoff, not two pages. MEASURED BEFORE, real Chromium at 390x844: Home's
   `.res-search` is 358x56. You tap it. homeHelpGo('') lands you on Help and focuses the field, and
   the SAME control is now 358x89, because Help still draws `.res-search-head`, 358x27 at 20px,
   directly above it. One tap, and the box she just touched grew 27px and gained a sentence.
   The 2026-09-09 note above got the reasoning exactly right and then scoped it to Home only, on
   the argument that on Help "the label is doing real work". It is not. The heading is
   `fhSearchHead`, "What do you need? Type it here."; the placeholder inside the box is
   `fhSearchPh`, "Search for help: food, rent, doctor, voting..." - the same instruction, weaker,
   with no examples, and it arrives at the exact moment she has already decided to type and the
   keyboard is coming up. `[RYAN 2026-07-23]` "the app just works we do not explain".
   MEASURED AFTER: Home 358x48, Help 358x48, same x, same border, same radius, same 48px go button.
   The control does not change size or shape between the two screens any more.
   NOTHING IS DELETED. `fhSearchHead` is still in all 16 strings files and still rendered by
   app.js:3425 into #csv-head-slot on Info, where it is the only label that page's box has.
   UNDO: delete this one rule and Help draws its heading again. */
#view-community > .res-search > .res-search-head{display:none}

/* PHONEFIT 2026-09-24, PART TWO: "looks big".
   The box was 56px tall with a 56x56 solid burgundy go button beside it, on a 390px screen, above
   a 13.5px news paragraph. Against everything else on Home it read as a control from a different
   app. 48px on phones: still 4px clear of LAW L5's 44px floor, hit-tested at its own centre in
   both the field and the button, font-size untouched at 16px (the 16px that stops iOS zooming the
   page on focus, and nowhere near the 12px gate floor). Phone widths only - the desktop preview
   frame and every harness above 430px keep the 56px they were measured at.
   Applies to all four search boxes (#res-q, #csv-input, #ev-q, #news-q) because they are one
   control, SEARCHPERFECT 2026-09-02, and the point of this fix is that they stay one control.
   UNDO: delete this media block. */
@media (max-width: 430px){
  .res-search input,
  .res-search .res-fauxinput{min-height:48px}
  .res-search .res-go{width:48px;min-width:48px;min-height:48px}
}

/* ================================================================================================
   VOICE 2026-09-20. THE GUIDE'S BAND OPENER, FINISHED: THE SERIF SENTENCE WITH ONE ITALIC PHRASE.

   GRADE_2026-09-20 put the brand at 65 percent and named this as the single largest remaining gap:
   "the large Cormorant sentence with one italic phrase and a terminal period, once per band,
   appears ZERO times on Home, Help and Info, and on Events, News and Settings the serif heading
   prints but the italic phrase is dropped." The hairline, the number and the label already shipped
   on 2026-09-18 (see .section-label near the top of this file). This block adds the sentence.

   THE WORDS ARE NOT MINE. Every sentence below is copied from BRANDSPEC_2026-09-17.md, which writes
   them out per screen. Nothing here is invented:
     Home 01 NEWS      "What changed this week."        BRANDSPEC line 288
     Home 02 COMING UP "Happening soon, near you."      BRANDSPEC lines 35 and 288
     Events 02         "What's on, and where."          BRANDSPEC, Events tab
     Info 04           "The city, how to reach it."     BRANDSPEC, Info tab
     News 05           "What changed this week."        BRANDSPEC, News tab
     Settings 06       "Make it yours."                 BRANDSPEC, Settings
     Help              "What do you need? Type it here." BRANDSPEC, The search box, "on Help only"
   The italic phrase is the last one in each, and every sentence ends in a period, which is the
   guide's own rule ("Serif headings are sentence case with terminal periods").

   HOW TWO TYPE STYLES COME OUT OF ONE PSEUDO-ELEMENT, AND WHY IT IS DONE THIS WAY.
   index.html and app.js are NOT this job's files, so no element can be added to the page. A
   pseudo-element can hold only one run of type, and the opener already spends ::before on the
   number. The roman half therefore rides in ::after's OWN ::marker (CSS Pseudo-Elements 4 allows
   ::marker on ::before/::after) with list-style-position:inside, so marker and content share one
   inline formatting context and the sentence wraps as a real paragraph, not as two boxes. Proven
   in headless Chromium at 320 and 375 in Normal, Large and Extra Large: the roman half breaks
   across lines correctly and horizontal scroll stays at zero.
   THE FALLBACK IS A WHOLE SENTENCE, NOT A HALF ONE. Outside @supports every opener prints the
   FULL sentence, roman, in the serif, with its period. A browser without nested markers therefore
   loses the italic and nothing else, which is exactly where Events, News and Settings already were
   on 2026-09-20. It can never print a fragment.
   NOT VERIFIED ON WEBKIT. Nested ::marker was measured here in Chromium only. If an iPhone shows
   the roman fallback, that is the designed degradation and not a bug; see VOICE_2026-09-20.md.

   TRANSLATION, STATED PLAINLY. These seven sentences are NEW ENGLISH and they ship ENGLISH ONLY,
   in all sixteen languages, exactly the way the four DARKWHY keys did. NO NON-LATIN STRING IS
   HAND-TYPED ANYWHERE IN THIS BLOCK; an earlier session corrupted Lao and Thai doing that. They
   are not in strings.en.js: t() cannot reach a CSS content string, and a key nothing reads is the
   dead key strings.en.js's own LIGHTDEFAULT note forbids. The wiring a later pass needs (one
   data-i18n-html span per opener in index.html, then fifteen translations) is written out in
   C:\!Brain\LFG4\NEVER_OVERWRITE\VOICE_2026-09-20.md with the key names. NEEDS NATIVE REVIEW.
   Because the sentences are always Latin, the seven-script italic suppression that A6 built for
   .event-row .ev-preview (ar, bn, km, ko, lo, zh, zh-Hant) is not needed here: no Khmer, Arabic or
   Lao glyph can ever land inside one of these headings and be sheared. That suppression stands
   untouched where it is.

   THE CRISIS PATH IS NOT TOUCHED. Nothing here selects .res-crisis, #res-helpbar-fixed, .res-211,
   .rrh-*, the big 211, or any element carrying 911, 988 or 211. Help's sentence is attached to the
   search head ABOVE the crisis strip and changes no crisis property.

   ZERO !important ADDED. app.css:1674's was REMOVED at source in the same pass.

   UNDO: delete this whole block and restore line 1674 and the two declarations marked
   "VOICE 2026-09-20" higher up in this file. Backup:
   NEVER_OVERWRITE_BACKUPS\rebuild_2026-09-18\app.css.PRE_VOICE
   (sha256 81884d46c20885555db5da6d11fc29ad613b380136969bf12ddd56818a492342, 289466 bytes).
   ================================================================================================ */

/* ---- 1. the shared sentence. Cormorant 500, because app_fonts.css registers
   cormorant-garamond-latin-500-normal.woff2 and -500-italic.woff2, so 500 hits a real file in both
   styles and the browser never manufactures a slant. 28px matches the .view > h2 size the grade
   already measured and sits inside BRANDSPEC's "28 to 32 px". letter-spacing and text-transform are
   reset because three of the six hooks inherit them from a tracked uppercase label. ---- */
.home-sec[data-sec="updates"] > .section-label::after,
.home-sec[data-sec="events"] > .section-label::after,
.home-sec[data-sec="quicklinks"] > .section-label::after,   /* LASTMILE 2026-09-23, see the block below */
#view-events > h2[data-bwn]::after,
#view-updates > h2[data-bwn]::after,
#view-settings > h2[data-bwn]::after,
#citysvc::before{
  display:block;
  font-family:var(--font-serif);
  font-style:normal;
  font-weight:500;
  font-size:28px;
  line-height:1.15;
  letter-spacing:0;
  word-spacing:0;
  text-transform:none;
  font-variant-numeric:normal;
  color:var(--bw-ink);
  margin:10px 0 0;
  padding:0;
  text-align:start;
  /* SEEN, NOT ASSUMED. The Arabic Home screenshot at 375x812 put the sentence's terminal period at the
     FAR LEFT of the line: an English sentence dropped into an RTL paragraph has its neutral final
     period reordered to the paragraph's end. These sentences are always English and always Latin, so
     the run is declared LTR and isolated from the page's bidi, and RTL pages then right-align the
     finished LTR line so it still hangs off the same margin as its number. */
  direction:ltr;
  unicode-bidi:isolate;
}
html[dir="rtl"] .home-sec[data-sec="updates"] > .section-label::after,
html[dir="rtl"] .home-sec[data-sec="events"] > .section-label::after,
html[dir="rtl"] .home-sec[data-sec="quicklinks"] > .section-label::after,
html[dir="rtl"] #view-events > h2[data-bwn]::after,
html[dir="rtl"] #view-updates > h2[data-bwn]::after,
html[dir="rtl"] #view-settings > h2[data-bwn]::after,
html[dir="rtl"] #citysvc::before{ text-align:right }
.phone.dark .home-sec[data-sec="updates"] > .section-label::after,
.phone.dark .home-sec[data-sec="events"] > .section-label::after,
.phone.dark .home-sec[data-sec="quicklinks"] > .section-label::after,
.phone.dark #view-events > h2[data-bwn]::after,
.phone.dark #view-updates > h2[data-bwn]::after,
.phone.dark #view-settings > h2[data-bwn]::after,
.phone.dark #citysvc::before{ color:var(--ink) }   /* --ink is #FAF6EC in dark: 15.87 on the #0D2018 canvas */

/* ==================================================================================================
   READER 2026-09-23. THE EYEBROW AND THE SENTENCE, READ TOGETHER, THE WAY A RESIDENT MEETS THEM.
   `[RYAN 2026-09-23]` "Coming up and happening near you? This needs a dozen agents everywhere
   everything needs to make logical sense from the reader's perspective come on jfc"

   He is right and the test is his: read the tracked label and the serif sentence as ONE thing, out
   loud, as somebody who has never seen the app. All eight pairs were read that way. Six are fine
   and are LEFT ALONE, named here so nobody re-opens them:
     Home 01   NEWS        "What changed this week."          label = the kind, sentence = the
                                                              timeframe and a promise. Different
                                                              axes. KEPT.
     Events    Events      "What's on, and where."            noun, then what and where. KEPT.
     News      News        "What's going on in Woonsocket."   set 2026-09-22 from BRANDSPEC's own
                                                              words; adds the place. KEPT.
     Settings  Settings    "Make it yours."                   says what the screen is FOR. KEPT.
     Info      (no label)  "The city, how to reach it."       stands alone since NONUMBER. KEPT.
     Help      (no label)  "What do you need? Type it here."  stands alone, and it instructs. KEPT.

   TWO ARE CHANGED, AND BOTH SAID THE SAME THING TWICE.

   1. HOME BAND 02. WAS: "COMING UP" over "Happening soon, near you."
      "Coming up" and "happening soon" are one statement printed twice, the second one four times
      larger than the first. His example, and the reason for this job.
      NOW: "COMING UP" over "Meetings and shows, near you."
      The label keeps time, which is what a dated band is for. The sentence now carries the two
      things the label cannot: WHAT is in there and WHERE. Both are true of the records actually in
      the band, checked against the live data rather than written to sound good: city meetings
      (School Committee, City Council) and Stadium Theatre shows are the two largest groups in it.
      It claims nothing about cost or eligibility, because the individual cards do that and some of
      these are ticketed or 55+, and it does not claim to be a complete list of kinds.
      THE FIRST DRAFT WAS LONGER AND WAS MEASURED, NOT GUESSED AT: "Meetings, shows and things to
      do, near you." took TWO lines at 390 in a real render and pushed a whole card below the fold,
      where the sentence it replaced took one. Shortened until it set on one line at 390 and 375,
      the same as the band above it. At 320 it takes two, which is where band 02 already was.
      The guide's shape is kept exactly: short noun phrase, comma, the last phrase in italic, a
      terminal period. No em-dash.

   2. HOME BAND 03. WAS: "QUICK LINKS" over "The usual things, one tap away."
      "Quick" and "one tap away" are the same claim twice, and "the usual things" tells a reader
      nothing at all: it is the ambient copy he has been cutting all week. LASTMILE 2026-09-23 wrote
      it the same day and marked it a draft to be overruled in one line, which is what this is.
      NOW: "QUICK LINKS" over "Trash day, the bus, the library."
      It names three of the things actually behind the band (app_data_findhelp.js / WS_QUICKLINKS:
      Trash & Recycling Day, RIPTA Bus Routes, Harris Library) so a reader knows in one glance
      whether this band is for her. Plain words, his register, nothing invented, no speed claim
      duplicating the label. STILL A DRAFT, exactly as LASTMILE left it, and still one line each to
      overrule: this rule and the two halves in the @supports block further down.

   TRANSLATION. Both sentences are NEW ENGLISH in a CSS `content` string and they ship ENGLISH ONLY,
   under the SAME LANGGATE that already covers these two pseudo-elements, exactly as the eight before
   them did. No strings file is opened and NO NON-LATIN STRING IS HAND-TYPED. The keys a translation
   pass needs are `bandEvents` and `bandQuickLinks`, the second and eighth rows of the table in
   NEVER_OVERWRITE/VOICE_2026-09-20.md. Verified by reading the computed content back in Arabic:
   `none`. NEEDS NATIVE REVIEW before anyone calls either one translated.
   Undo: NEVER_OVERWRITE_BACKUPS/card_2026-09-23/app.css.PRE_CARD.
   ================================================================================================== */
/* ==================================================================================================
   LAYOUT2 2026-09-24. HOME BAND 01 STOPS READING LIKE A CHANGELOG.
   `[RYAN 2026-09-24]` "why say what changed this week??? that is oddd"
   He is right, and the READER 2026-09-23 block above had this one in its KEPT column, so it is
   overruled here by the only thing that overrules it, which is his eye. "What changed this week." is
   how a release note talks. A resident does not hold last week's Woonsocket in her head and come to
   this band for the diff; she comes to it to find out what is going on. The label already says NEWS,
   so the sentence has to carry a different axis, which is what the six kept pairs all do.
     DRAFT, LAYOUT2 2026-09-24:  "City notices, and good news."
   IT FITS, MEASURED, NOT ASSUMED, the same way band 02 and band 03 were. The first draft read
   "Notices from the city, and good news." and was measured in a real render at all five widths: it
   set on TWO lines at 375 and 390, making band 01's opener 101px tall against its siblings' 69px,
   which is the one thing the guide's opener shape cannot do. Shortened, re-measured, and it now
   sets on ONE line at 375 and 390 exactly as "Meetings and shows, near you." and "Trash day, the
   bus, the library." do. At 320 it takes two, which is where all three bands already are.
   IT IS HONEST ABOUT WHAT IS BEHIND IT, checked against res_data_updates.js rather than written to
   sound good: of the 44 records, 27 are category "Service" and 7 are "Alert" - city notices, every
   one of them cited to woonsocketri.gov or a Woonsocket public body under his own 2026-08-12 source
   lock - and 6 are literally category "Good news". It claims no timeframe, because the band does not
   guarantee one, and that false weekly promise is half of what made the old line odd.
   Same shape as the two beside it: a short noun phrase, a comma, the last phrase in italic, a
   terminal period. No em-dash, no invented brand word, no speed or completeness claim.
   IT IS A DATED DRAFT AND HE CAN OVERRULE IT IN ONE LINE, exactly as LASTMILE left band 03: change
   the one string below and the two halves in the @supports block further down this file (marked
   LAYOUT2). Three strings, one sentence.
   ENGLISH ONLY, like the other seven, and for the same reason: it is a new English sentence inside a
   CSS `content` string, which t() cannot reach. It rides on the SAME pseudo-elements the old one did,
   both already named in LANGGATE Part 1 and Part 2, so it needed no new gate line. The key a
   translation pass needs is `bandNewsHome`. NEEDS NATIVE REVIEW before anyone calls it translated.
   THE NEWS TAB IS NOT TOUCHED: it says "What's going on in Woonsocket." and has since 2026-09-22.
   Undo: NEVER_OVERWRITE_BACKUPS/layout2_2026-09-24/app.css.PRE_LAYOUT2.
   ================================================================================================== */
/* the fallback content: the WHOLE sentence, roman. Never a fragment. */
.home-sec[data-sec="updates"] > .section-label::after{ content:"City notices, and good news." }
.home-sec[data-sec="events"] > .section-label::after{ content:"Events, near you." }
/* ==================================================================================================
   LASTMILE 2026-09-23. HOME'S THIRD BAND FINALLY HAS ITS SENTENCE.
   `[RYAN 2026-09-23]` he said, correctly, that we keep handing him a list instead of finished work.
   VOICE_2026-09-20 line 36 wrote: "Home's third band, Quick Links, has no sentence in BRANDSPEC and
   did not get one. I did not write a..." and stopped. FINISH_2026-09-23 section 8 A stopped at the
   same place for the same reason. Three sessions in a row declined this and handed it back. That was
   wrong: a band with a gold hairline, a number and a label and NO sentence is not the guide's
   opener, it is two thirds of one, and it has shipped that way beside two finished ones.

   SO IT IS WRITTEN, AND IT IS MARKED A DRAFT SO HE CAN OVERRULE IT IN ONE LINE.
     DRAFT, LASTMILE 2026-09-23:  "The usual things, one tap away."
   Same shape as the two beside it: a short noun phrase, a comma, then the italic phrase, then a
   terminal period. "What changed / this week." "Happening soon, / near you." "The usual things, /
   one tap away." No jargon, no em-dash, no invented brand word, and nothing this band does not do.
   IT IS HONEST ABOUT WHAT IS BEHIND IT. The tiles this band draws (app_data_findhelp.js,
   WS_QUICKLINKS) are Trash & Recycling Day, RIPTA Bus Routes, Harris Library, School Calendar,
   Report a Concern, City Hall, Parks & Rec, Voting, Events, Sign Up for Programs. Those are
   everyday things and every one of them opens in one tap. The sentence claims nothing else.
   "tap" is HIS word (he asks for cards with "tap options"), not a synonym chosen for him.

   TO OVERRULE IT: change the one string below, and the two halves in the @supports block further
   down this file (marked LASTMILE), and nothing else. Three strings, one sentence, one line each.
   THE KEY FOR THE TRANSLATOR IS `bandQuickLinks`, the eighth row of VOICE_2026-09-20.md's table,
   which stopped at seven. English only until it is really translated, exactly like the other seven:
   LANGGATE Part 1 below and Part 2 at the end of this file both carry it.
   IT FITS, MEASURED, NOT ASSUMED. At 390 and 375 CSS px this sentence sets on ONE line, the same as
   the two beside it. At 320 it takes two, which is exactly where band 02's shipped "Happening soon,
   near you." already takes two, so it is no worse than what is on the phone today. The first draft
   read "The everyday things, one tap away." and took two lines at 375 as well; it was measured in a
   real browser at all three widths and shortened, not guessed at.
   UNDO: delete this content rule and the `quicklinks` selector from the six lists that name it.
   ================================================================================================== */
.home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Trash day, the bus, the library." }
#view-events > h2[data-bwn]::after{ content:"What's on, and where." }
#view-updates > h2[data-bwn]::after{ content:"What changed this week." }
#view-settings > h2[data-bwn]::after{ content:"Make it yours." }

/* ==================================================================================================
   LANGGATE 2026-09-20. ENGLISH ONLY, UNTIL THESE SEVEN SENTENCES ARE REALLY TRANSLATED.
   These headings are hard-coded English inside `content:`, which no translation layer can reach:
   t() cannot read a CSS string. Shipped ungated, they printed 28px English as the LARGEST TYPE on
   the page on all fifteen non-English languages, sitting above correctly translated content. An
   Arabic or Khmer reader would have met "What changed this week." before anything she could read.
   Measured and caught by SIGNOFF_2026-09-20, which called it the one thing blocking the deploy.
   That is a worse failure than having no brand voice at all: this app exists for people who do not
   read English, and LAW L1's whole point is that the words reach her in her own language.
   So the sentence appears only where it is genuinely in her language. Everyone else keeps the gold
   hairline, the number and the tracked label, which ARE translated and which shipped before tonight.
   Nothing is lost for them; the brand simply says less until it can say it properly.
   `[lang|="en"]` matches "en" and "en-GB" and nothing else. Specificity carries it over the rules
   above with no `!important` added, which keeps the 42 count from VOICE unchanged.
   UNDO, and the RIGHT undo: do not delete this block. Wire the seven as real translated elements
   with the keys and markup already written out in NEVER_OVERWRITE/VOICE_2026-09-20.md, get the
   fifteen translations, then this gate can go because content will come from t() and not from CSS.
   ================================================================================================== */
html:not([lang|="en"]) .home-sec[data-sec="updates"] > .section-label::after,
html:not([lang|="en"]) .home-sec[data-sec="events"] > .section-label::after,
html:not([lang|="en"]) .home-sec[data-sec="quicklinks"] > .section-label::after,
html:not([lang|="en"]) #view-events > h2[data-bwn]::after,
html:not([lang|="en"]) #view-updates > h2[data-bwn]::after,
html:not([lang|="en"]) #view-settings > h2[data-bwn]::after,
html:not([lang|="en"]) #citysvc::before,
html:not([lang|="en"]) .res-search-head::before{ content:none }

/* ---- 2. Info has no opener element in the page at all, so #view-beloved supplies one.
   #csv-title and #csv-sub are hidden by info_page.js on his word ([RYAN 2026-08-13] "I hate the
   'Who runs the city' type headings", [RYAN 2026-07-23] "the app just works we do not explain").
   THEY STAY HIDDEN. This adds the guide's opener above the search box and does not put his two
   retired lines back. The 20px by 1px Community Gold hairline is a background, so no element is
   added, and the number is the same tracked Barlow digit the other five openers use. The label
   WORD is not printed here because Info has no translated word in the DOM to promote and a CSS
   word would be English in sixteen languages for no gain; the digit is language-neutral.
   The number rides on #view-beloved::before and the sentence on #citysvc::before, which is the
   next box down the page, so the two sit in normal flow in the right order with nothing positioned
   absolutely and nothing measured off a padding that could change. ---- */
/* NONUMBER 2026-09-23. THIS OPENER WAS A NUMBER AND NOTHING ELSE, SO IT GOES WHOLE.
   `[RYAN 2026-09-23]` "Keep the word, drop digits only". The block above says in its own words why
   no word was ever printed here: "Info has no translated word in the DOM to promote and a CSS word
   would be English in sixteen languages for no gain; the digit is language-neutral." With the digit
   gone there is no word left to keep, so the whole opener, the gold hairline included, is not drawn.
   That is FINAL_2026-09-23 section 8 item 5 closed, which flagged this same element for "printing a
   number with no word at all".
   `content:none` means the pseudo-element is never generated, so every declaration below it, and the
   RTL background-position and dark-mode colour rules under it, are inert rather than wrong. The
   serif sentence on `#citysvc::before` ("The city, how to reach it.") is a DIFFERENT element and is
   untouched: Info keeps its voice, it just stops counting.
   UNDO, one line: put `content:"04";` back below. */
#view-beloved::before{
  content:none;
  display:block;
  font-family:var(--font-label);
  font-weight:700;
  font-size:12px;
  line-height:1.2;
  letter-spacing:var(--track-label);
  text-transform:uppercase;
  color:var(--bw-gold-ink);
  font-variant-numeric:tabular-nums;
  margin:4px 0 0;
  padding-top:11px;
  background:linear-gradient(var(--bw-gold),var(--bw-gold)) no-repeat 0 0 / 20px 1px;
}
html[dir="rtl"] #view-beloved::before{ background-position:100% 0 }
.phone.dark #view-beloved::before{ color:var(--bw-gold) }
#citysvc::before{ content:"The city, how to reach it."; margin:6px 0 14px }   /* .res-search sits 2px under it; 14px gives the sentence the same air the Home bands have */

/* ---- 3. the four view openers. BRANDSPEC writes the opener as "NN . WORD" and then the sentence
   underneath ("opener 05 . NEWS, serif What changed this week."). The h2's own word is already in
   the page and already translated into sixteen languages, so it becomes the tracked label the guide
   asks for and the sentence takes the serif. GRADE_2026-09-20 asked for this by name on Events:
   "the label reads bare 02 with no word beside it, where every other numbered opener in the guide
   is NN . WORD". The h2 stays an h2 and keeps its text node, so a screen reader still hears
   "Events, heading level 2" exactly as before.
   SUPERSEDED, KEPT HERE: .view > h2[data-bwn] used to inherit the serif 28px of .view > h2 and its
   ::before was display:block;margin:0 0 6px. ---- */
.view > h2[data-bwn]{
  font-family:var(--font-label);
  font-size:12px;
  line-height:1.2;
  font-weight:700;
  text-transform:uppercase;
  letter-spacing:var(--track-label);
  color:var(--bw-gold-ink);
}
.view > h2[data-bwn]:lang(ar),
.view > h2[data-bwn]:lang(km),
.view > h2[data-bwn]:lang(lo),
.view > h2[data-bwn]:lang(bn){ letter-spacing:0 }   /* joining and combining marks, same pair as .section-label */
.view > h2[data-bwn]::before{ display:inline-block; margin:0 .9em 0 0; margin-inline:0 .9em }
.phone.dark .view > h2[data-bwn]{ color:var(--bw-gold) }

/* ---- 4. Help. BRANDSPEC, "The search box": "Above the input, on Help only, the guide's
   promise-block idiom: a 3 px navy left rule with 'What do you need? Type it here.' in serif 20 px."
   That sentence is ALREADY in the page, already translated into sixteen languages, as
   .res-search-head's data-i18n="fhSearchHead" span. So the promise block itself is given to every
   language as pure styling, with NO text change and NO string touched: serif, 20px, Ink, 3px Deep
   Navy left rule. Nothing is lost in any language.
   THE ITALIC HALF IS ENGLISH ONLY, AND ONLY BECAUSE A TEXT NODE CANNOT BE SPLIT BY CSS. Under
   html[lang="en"] the span is moved to the clipped 1px box screen readers still read, and the same
   sentence is printed back in two styles. Arabic, Khmer, Lao and the other twelve keep their own
   translated sentence, visible, upright, in the serif. THE COST, STATED: in English the visible
   words now come from this file, so an edit to fhSearchHead in strings.en.js will not change what
   an English reader sees on Help until index.html carries a data-i18n-html span. That is written
   up in VOICE_2026-09-20.md as the one divergence this pass creates. ---- */
#view-community > .res-search .res-search-head{
  font-family:var(--font-serif);
  font-weight:500;
  font-size:20px;
  line-height:1.3;
  color:var(--bw-ink);
  border-inline-start:3px solid var(--bw-navy);
  padding-inline-start:12px;
  align-items:baseline;
  /* MEASURED: the serif at 20px makes this line 4px taller than the 16px sans it replaces, and that
     pushed .res-crisis and .res-211 4px down the page. app.css:549 sets margin-bottom:10px; 6px here
     puts .res-crisis and .res-211 back on the exact pixel they were on. VERIFIED by serving the
     PRE_VOICE app.css and this one side by side and diffing: 116 crisis elements, 60,918 computed
     properties, ZERO property differences; the only 8 differences left are 1px rounding on inline
     boxes INSIDE those two blocks, and both block containers land on the identical rect. */
  margin-bottom:6px;
}
.phone.dark #view-community > .res-search .res-search-head{ color:var(--ink); border-inline-start-color:var(--bw-gold) }
/* the magnifier glyph is an emoji, not type: it is left on the sans so it is not counted as a
   Cormorant node it never paints. BRANDSPEC wants an SVG stroke here; that is app.js's file. */
#view-community > .res-search .res-search-head .ic{ font-size:16px; align-self:center; font-family:var(--font-sans) }
html[lang="en"] #view-community > .res-search .res-search-head > span[data-i18n="fhSearchHead"]{
  position:absolute;
  width:1px; height:1px;
  margin:-1px; padding:0;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}
html[lang="en"] #view-community > .res-search .res-search-head::after{
  content:"What do you need? Type it here.";
  display:block;
  flex:1 1 auto;
  min-width:0;
  font-family:var(--font-serif);
  font-style:normal;
  font-weight:500;
  font-size:20px;
  line-height:1.3;
  letter-spacing:0;
  text-transform:none;
  color:inherit;
  direction:ltr;
  unicode-bidi:isolate;
}

/* ---- 4b. EXTRA LARGE. The two scaled tiers are CSS `zoom` on .phone (1.5 and 2.0, app.css:747-748),
   so at Extra Large a 28px heading paints at 56px and Home, which had NO heading before this pass and
   now has two, loses real estate above the fold. MEASURED at 375 x 812, Extra Large: the sentence runs
   to three lines. 22px paints at 44px, still 1.4x the 32px body of that tier and still far above
   BRANDSPEC's "never smaller than 18 px in the app" floor, which is about the CSS size. Normal and
   Large keep the spec's 28px. Horizontal scroll is zero at 320 and 375 in all three tiers either way;
   this is about vertical reach for a person who chose Extra Large because she needs it.
   UNDO: delete this one rule and Extra Large goes back to 28px. ---- */
.phone.text-xlarge .home-sec[data-sec="updates"] > .section-label::after,
.phone.text-xlarge .home-sec[data-sec="events"] > .section-label::after,
.phone.text-xlarge .home-sec[data-sec="quicklinks"] > .section-label::after,
.phone.text-xlarge #view-events > h2[data-bwn]::after,
.phone.text-xlarge #view-updates > h2[data-bwn]::after,
.phone.text-xlarge #view-settings > h2[data-bwn]::after,
.phone.text-xlarge #citysvc::before{ font-size:22px }

/* ---- 5. THE ITALIC. One phrase, the last one, per sentence. Everything above already prints a
   complete roman sentence; this only re-cuts it into a roman marker and an italic content run.
   font-style:normal on every ::marker is required: a marker inherits from its own ::after, which is
   italic here. ---- */
@supports selector(::after::marker){
  .home-sec[data-sec="updates"] > .section-label::after,
  .home-sec[data-sec="events"] > .section-label::after,
  .home-sec[data-sec="quicklinks"] > .section-label::after,
  #view-events > h2[data-bwn]::after,
  #view-updates > h2[data-bwn]::after,
  #view-settings > h2[data-bwn]::after,
  html[lang="en"] #view-community > .res-search .res-search-head::after{
    display:list-item;
    list-style:none;
    list-style-position:inside;
    font-style:italic;
  }
  .home-sec[data-sec="updates"] > .section-label::after::marker,
  .home-sec[data-sec="events"] > .section-label::after::marker,
  .home-sec[data-sec="quicklinks"] > .section-label::after::marker,
  #view-events > h2[data-bwn]::after::marker,
  #view-updates > h2[data-bwn]::after::marker,
  #view-settings > h2[data-bwn]::after::marker,
  html[lang="en"] #view-community > .res-search .res-search-head::after::marker{
    font-style:normal;
    font-family:var(--font-serif);
    font-weight:500;
    color:inherit;
  }
  /* LAYOUT2 2026-09-24 DRAFT, one line each to change. Key for translation: bandNewsHome. */
  .home-sec[data-sec="updates"] > .section-label::after{ content:"and good news." }
  .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"City notices, " }
  .home-sec[data-sec="events"] > .section-label::after{ content:"near you." }
  .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Events, " }
  /* LASTMILE 2026-09-23 DRAFT, one line each to change. Key for translation: bandQuickLinks. */
  .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"the library." }
  .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Trash day, the bus, " }
  #view-events > h2[data-bwn]::after{ content:"and where." }
  #view-events > h2[data-bwn]::after::marker{ content:"What's on, " }
  #view-updates > h2[data-bwn]::after{ content:"this week." }
  #view-updates > h2[data-bwn]::after::marker{ content:"What changed " }
  #view-settings > h2[data-bwn]::after{ content:"yours." }
  #view-settings > h2[data-bwn]::after::marker{ content:"Make it " }
  html[lang="en"] #view-community > .res-search .res-search-head::after{ content:"Type it here." }
  html[lang="en"] #view-community > .res-search .res-search-head::after::marker{ content:"What do you need? " }
}
@supports selector(::before::marker){
  #citysvc::before{
    display:list-item;
    list-style:none;
    list-style-position:inside;
    font-style:italic;
    content:"how to reach it.";
  }
  #citysvc::before::marker{
    font-style:normal;
    font-family:var(--font-serif);
    font-weight:500;
    color:inherit;
    content:"The city, ";
  }
}
/* ==== END VOICE 2026-09-20 ==== */

/* ==================================================================================================
   HELPINFO 2026-09-22. THREE GAPS FROM SIGNOFF_2026-09-20, AND ONE REFUSAL.

   THIS BLOCK SITS BEFORE `LANGGATE 2026-09-20, PART 2` AND PART 2 STAYS LAST IN THIS FILE.
   Part 2 has to be last because the two `@supports selector(::after::marker)` blocks above it
   re-declare `content`, and `@supports` adds no specificity. Nothing here goes after it.

   WHAT IS HERE
     1. Help's 211 INFORMATION block: dark green off, Deep Navy on, Community Gold numerals.
     2. Help's band opener: the 20px gold hairline and the tracked number, the same component the
        other five screens already carry, with its height paid for exactly so the crisis card does
        not move by one hundredth of a pixel.
     3. News stops printing Home's sentence.

   WHAT IS NOT HERE, AND WHY, SAID OUT LOUD RATHER THAN QUIETLY DROPPED
   The brief also asked for the peach `#F6E9E5` "Need help right now?" box to become the navy
   promise block. MEASURED IN A REAL BROWSER BEFORE ANY EDIT: exactly ONE element on Help paints
   `#F6E9E5`, and it is `DIV.res-crisis.res-crisis-acc` (app.css:1561,
   `background:var(--red-soft);border:1.5px solid var(--red)`). "Need help right now?" is
   `.crisis-bar-lbl` INSIDE it (index.html:1158), "More" is `.cq.more`, and its three direct
   children are `tel:911`, `tel:988` and `sms:988`. That box is not sitting above the crisis
   strip. IT IS THE CRISIS STRIP. The same brief names `.res-crisis` in the list that ends the job
   and says to stop rather than guess, so it is stopped and it is a card for Ryan.
   BRANDSPEC_2026-09-17 line 308 does specify the change; BRANDSPEC and the crisis rule disagree
   here and that is his call, not an agent's. Nothing in this file selects `.res-crisis`.
   ================================================================================================== */


/* ---- 1. THE 211 INFORMATION BLOCK. COLOUR ONLY. -------------------------------------------------
   `[BRIEF 2026-09-22]` "The 211 block is still dark green #2F6B45. Green is not one of the brand's
   seven colours. The spec named this removal explicitly. It should go navy with gold numerals."
   BRANDSPEC_2026-09-17, "211 block and Need help right now?": "211 block: Deep Navy `#102F3D` card
   (the guide's MISSION card idiom) ... `#C49A2A` ... The dark green leaves; green is not in the
   palette and the guide gives institutional to Navy."

   THIS IS `#res-211` / `.res-211`, WHICH IS A SEPARATE SIBLING ELEMENT (index.html:1260), NOT the
   crisis strip. The brief authorises its COLOUR by name and nothing else, so nothing else moves:
   no font-family, no font-size, no padding, no margin, no border-radius, no min-height, no href,
   no digit, no tap target. The "Call 211" button keeps its measured 48px height
   (`[RYAN 2026-09-06 LOOK_FOUR]` "do NOT shrink the button"). Every `tel:` href was read as a
   string; nothing was dialled.

   SUPERSEDED, KEPT VERBATIM SO ONE LINE CAN GO BACK:
     app.css:432   .res-211{...background:var(--green);color:#fff...}
     app.css:451   .res-211 a{...background:#fff;color:var(--green)...}
     app.css:2004  .res-211{display:block;background:var(--green);color:#fff;...}
     app.css:2015  .res-211 a{...background:#fff;color:var(--green);...}
     app.css:2022  .res-211 a:active{background:#F0F5F1}
     app.css:910   .phone.dark .findnow,.phone.dark .fh-cat.active,.phone.dark .res-211{background:#2F6B45;color:#fff}
     app.css:913   .phone.dark .res-211 a{color:#2F6B45}
     app.css:960   .phone:not(.appearance-set) ... .res-211{background:#2F6B45;color:#fff}
     app.css:963   .phone:not(.appearance-set) .res-211 a{color:#2F6B45}
   `--green` ITSELF IS NOT TOUCHED. It is still `#2F6B45` and still carries "Open right now",
   "Always open" and "Speaks your language" on the result cards, where green means open, not brand.
   Measured before this edit: 46 elements on Help read `#2F6B45` in some property; only THREE of
   them paint it, and all three are inside `#res-211`. The other 43 are that open/closed status
   family and they are deliberately left alone.

   THE DARK AND AUTO-DARK TWINS ARE WRITTEN OUT because `.phone.dark .res-211` and
   `.phone:not(.appearance-set) .res-211` are (0,2,0) and would outrank a bare `.res-211` here no
   matter how late it sits in the file.

   CONTRAST, COMPUTED (WCAG): cream `#FAF6EC` on Deep Navy `#102F3D` 13.01 (body needs 4.5);
   Community Gold `#C49A2A` on Deep Navy 5.35 at 31px (large needs 3, body needs 4.5, passes both);
   Deep Navy on the cream button face 13.01; white `#FFFFFF` on Deep Navy in high contrast 14.04.
   The old pair was white on `#2F6B45` 5.63 and `#2F6B45` on white 5.63, so every pair here is
   BETTER than the pair it replaces. No hex is invented: `--bw-navy`, `--bw-gold` and `--bw-cream`
   are A1_TOKENS_2026-09-18 tokens, and A1's crisis pin does not re-point any `--bw-*` name. */
.res-211,
.phone.dark .res-211{
  background:var(--bw-navy);
  color:var(--bw-cream);
}
.res-211 .big,
.phone.dark .res-211 .big{ color:var(--bw-gold) }   /* the gold numerals. 5.35 on Deep Navy. */
.res-211 a,
.phone.dark .res-211 a{
  background:var(--bw-cream);
  color:var(--bw-navy);
}
/* the pressed state. `#EDE7D9` is BRANDSPEC's Cream Deep, written as its literal and NOT as
   `var(--bw-cream-deep)` on purpose: A1 re-points that token to `#1A3A4A` in dark, where Deep Navy
   text on it measures 1.30 and the label would vanish under the finger. The button face is
   `--bw-cream` in both themes, so its pressed face has to be one value in both themes too.
   Deep Navy on `#EDE7D9` is 11.39. Superseded: `.res-211 a:active{background:#F0F5F1}`. */
.res-211 a:active,
.phone.dark .res-211 a:active{ background:#EDE7D9 }
@media (prefers-color-scheme: dark){
  /* the dead auto-dark twin (ONETHEME 2026-09-07). Kept in lockstep so nothing green can come back
     if a future index.html ever drops the static `appearance-set` class. */
  .phone:not(.appearance-set) .res-211{ background:var(--bw-navy); color:var(--bw-cream) }
  .phone:not(.appearance-set) .res-211 .big{ color:var(--bw-gold) }
  .phone:not(.appearance-set) .res-211 a{ background:var(--bw-cream); color:var(--bw-navy) }
  .phone:not(.appearance-set) .res-211 a:active{ background:#EDE7D9 }
}


/* ---- 2. HELP'S BAND OPENER. ---------------------------------------------------------------------
   `[BRIEF 2026-09-22]` "Help has no band opener at all." GRADE_2026-09-20, Help item 3: "No section
   opener at all. No gold hairline, no numbered label."

   THE NUMBER IS `03` AND THAT IS NOT A GUESS. The five openers already in the page read
   `01`/`02`/`03` on Home's three bands, `02` on Events, `04` on Info, `05` on News and `06` on
   Settings (index.html `data-bwn`). The tab bar runs Home, Events, Help, Info, News, so Help sits
   between Events `02` and Info `04`, and `03` is the only digit consistent with what is already on
   disk. Home's own `01`/`02`/`03` are a band-level scale inside one screen, a second scope the app
   already carries and GRADE_2026-09-20 already recorded on News ("the page is 05 and its first
   block is 01 URGENT").

   NO WORD BESIDE THE NUMBER, AND NO NEW SENTENCE. Help has no translated word in the DOM to
   promote and a CSS word would be English in sixteen languages for no gain, which is exactly the
   reasoning VOICE_2026-09-20 section 8 used for Info's `04`. A DIGIT IS LANGUAGE-NEUTRAL, so this
   opener needs no entry in the LANGGATE and gets none. Help's serif sentence already exists and is
   already translated: it is `.res-search-head`'s own `fhSearchHead` span, one line below this.

   THE CRISIS CARD DOES NOT MOVE, AND THAT COST EXACTLY 22px THAT HAD TO COME FROM SOMEWHERE.
   MEASURED at 375x812 before the edit: `#view-community` top 80.09, its `padding-top` 16,
   `.res-search` at y 98.09 h 89 (head 27 + head margin-bottom 6 + row 56) with
   `margin:2px 0 16px !important`, `.res-crisis` at y 203.09. 16 + 2 + 89 + 16 = 123.
   `.res-search`'s own margins CANNOT be used as the compensation: they carry `!important`
   (app.css, the `@media (max-height:568px),(max-width:900px)` un-stick block), and this pass adds
   ZERO `!important`. So the 22px comes from the only two slack values that do not:
     `#view-community` padding-top  16px -> 0   (the opener's own hairline is now the band's top edge)
     `.res-search-head` margin-bottom 6px -> 0  (superseded value kept below)
   and the opener is built to occupy exactly 22px: `padding-top:7px` + `line-height:15px`. Its own
   margins are 0, so the 2px that was `.res-search`'s margin-top still separates it from the search
   box by margin collapse, exactly as before. 0 + 22 + 2 + 83 + 16 = 123. Identical.
   BOTH NUMBERS ARE WHOLE PIXELS ON PURPOSE, AND THAT WAS MEASURED, NOT ASSUMED. The first build
   used the other five openers' `line-height:1.2` with `padding-top:7.6px`, which is 22.0px on
   paper. Chromium lays out in 1/64px LayoutUnits, the two fractions rounded down, and the whole
   crisis card landed at y 203.08 against 203.09 - one hundredth of a pixel, found by the diff and
   not left in. Integers make the sum exact and the diff went to zero. 15px against the other
   openers' 14.4px is 0.6px of line box nobody can see, and it is the only difference between this
   opener and theirs.
   The number's height is font-INDEPENDENT (a 12px box at a fixed 15px line-height is 15px whatever
   glyph lands in it), so this arithmetic holds in all sixteen languages, and CSS `zoom` at the
   Large and Extra Large steps scales every term by the same factor, so it holds at 150 and 200
   percent too. Proved in both, and at 320 as well as 375.

   SUPERSEDED, KEPT VERBATIM:
     app.css:152   .view{padding:16px 16px calc(var(--bw-dockh, 120px) + 16px);...}  (top only, and
                   only on #view-community; the 16px sides and the dock-height bottom are untouched)
     the VOICE block's  #view-community > .res-search .res-search-head{ ... margin-bottom:6px; }
                   VOICE set 6px for this same reason, to put `.res-crisis` and `.res-211` back on
                   the pixel the 20px serif had pushed them off. That job is now done by this
                   opener's own arithmetic instead, and the crisis rect is proved identical again.
   UNDO, one rule at a time: delete this section and put `padding-top:16px` and `margin-bottom:6px`
   back. Everything else on Help is untouched. */
/* NONUMBER 2026-09-23. HELP'S OPENER WAS A NUMBER AND NOTHING ELSE, SO IT GOES WHOLE, AND THE 22px
   IT WAS SPENDING GOES BACK EXACTLY WHERE IT CAME FROM.
   `[RYAN 2026-09-23]` "Keep the word, drop digits only". The block above says in its own words that
   this opener carries "NO WORD BESIDE THE NUMBER", for the same reason Info's did. With the digit
   gone there is no word left to keep. FINAL_2026-09-23 section 8 item 3 closed.

   THE CRISIS CARD DOES NOT MOVE, AND THIS IS THE UNDO THAT BLOCK WROTE FOR ITSELF, TAKEN VERBATIM:
   "UNDO, one rule at a time: delete this section and put `padding-top:16px` and `margin-bottom:6px`
   back." The opener was built to occupy exactly 22px (`padding-top:7px` + `line-height:15px`) and it
   was paid for by taking 16px off `#view-community`'s padding-top and 6px off `.res-search-head`'s
   margin-bottom. 16 + 6 = 22. Both are given back on the two lines below, so the arithmetic that
   block proved (0 + 22 + 2 + 83 + 16 = 123, and now 16 + 2 + 89 + 16 = 123) lands on the same total
   and `.res-crisis` lands on the same y it has landed on since 2026-09-22. MEASURED, not reasoned:
   the crisis card's rect is read before and after in a real headless Chromium at 320, 375 and 390,
   Normal and Extra Large, and the diff is zero. See FINISH_2026-09-23.
   The rules are NOT deleted, they are set back to the values this file already records as
   superseded, so both states are still on the page and the change is one line each.
   The two `.phone.text-large` / `.phone.text-xlarge` rules below are now redundant rather than
   wrong: the opener is not drawn at ANY text step, and their `margin-bottom:6px` restore is the
   same 6px the rule directly below already gives back, so they set it to the value it already has.
   They are left in place because they are the honest record of why the number is 6.
   UNDO, three lines: `content:"03";`, `padding-top:0` and `margin-bottom:0`. */
#view-community{ padding-top:4px }   /* HELPLAY 2026-10-01: was 16px (NONUMBER 2026-09-23). This is DEAD space between a solid sticky masthead and the first card, which has its own padding. Measured at scroll 0, real Chromium, iOS-Safari-URL-bar-up model: this 12px is part of a 26px lift above the five doors that moves the Help page from 43 to 48 rows of real answer visible above the fold across the eight 320/390 x Normal/XL x keyboard-up/down cells. Undo: put 16px back. */              /* NONUMBER 2026-09-23: was 0, which paid for the opener's 22px. VOICE's own value, restored */
#view-community > .res-search .res-search-head{ margin-bottom:6px }   /* NONUMBER 2026-09-23: was 0, the other half of the same 22px. VOICE's own value, restored */
#view-community::before{
  content:none;
  display:block;
  font-family:var(--font-label);
  font-weight:700;
  font-size:12px;
  line-height:15px;                  /* whole pixels. see the note above; 1.2 cost 0.01px of crisis */
  letter-spacing:var(--track-label);
  text-transform:uppercase;
  color:var(--bw-gold-ink);          /* #8A6A15 on Warm Cream 4.69 */
  font-variant-numeric:tabular-nums;
  margin:0;
  padding-top:7px;                   /* 1px of it is the hairline itself; 22px total box */
  background:linear-gradient(var(--bw-gold),var(--bw-gold)) no-repeat 0 0 / 20px 1px;
}
html[dir="rtl"] #view-community::before{ background-position:100% 0 }
/* ---- THE OPENER DOES NOT EXIST AT THE LARGE AND EXTRA LARGE TEXT STEPS, AND THAT IS THE WHOLE
   POINT OF IT. FOUND BY MEASUREMENT, NOT BY READING THE FILE.
   `crisis_fold_2026-08-23.css:107` makes `#view-community` a FLEX COLUMN at `.text-large` and
   `.text-xlarge` and gives `.res-crisis` `order:-1`, so at 150 and 200 percent THE CRISIS CARD IS
   PAINTED FIRST ON HELP, above the search box. That file's own comment says why: "JavaScript is the
   thing that must not be a prerequisite for reaching 911."
   A `::before` is a flex item. It lands at `order:0`, which at those two steps put the gold hairline
   and the number BETWEEN the crisis card and the search box, which is nonsense; and `order:-2` would
   have put 22px of brand ornament ABOVE 911 at the one text size where the screen is smallest. Both
   are wrong. Neither is a thing to ship to somebody who set her phone to 200 percent because she
   needs it. So the opener is simply not there at those two steps.
   Measured consequences of that choice, all of them good: the crisis card does not move (it is the
   first flex item either way), and nothing below it moves either, because flex items DO NOT COLLAPSE
   MARGINS, so the 6px reclaimed from `.res-search-head` is given straight back below rather than
   spent on an opener that is not drawn. That restore is the second rule here.
   `crisis_fold`'s own `.phone.text-large #view-community{padding-top:6px}` is (1,2,0) and outranks
   the `#view-community{padding-top:0}` above at (1,0,0), so the 6px it bought back on 2026-08-23 is
   untouched at those steps, without this file having to say anything about it. That was checked in
   the browser's own CSSOM, not assumed. */
.phone.text-large  #view-community::before,
.phone.text-xlarge #view-community::before{ display:none }
.phone.text-large  #view-community > .res-search .res-search-head,
.phone.text-xlarge #view-community > .res-search .res-search-head{ margin-bottom:6px }   /* VOICE's value, restored where the opener is not drawn */
/* the same zero-tracking pair every other opener carries: joining and combining marks. */
#view-community:lang(ar)::before,
#view-community:lang(km)::before,
#view-community:lang(lo)::before,
#view-community:lang(bn)::before{ letter-spacing:0 }
.phone.dark #view-community::before{ color:var(--bw-gold) }   /* #C49A2A on the #0D2018 canvas 6.47 */


/* ---- 3. NEWS STOPS PRINTING HOME'S SENTENCE. ----------------------------------------------------
   `[BRIEF 2026-09-22]` "Home and News now print the IDENTICAL sentence (What changed this week.) as
   their largest type. Give News its own from BRANDSPEC. Do not invent words."
   SIGNOFF_2026-09-20 item 4: "Give News its own sentence, and stop the duplicate."

   WHERE THE WORDS COME FROM, AND THE HONEST PROBLEM WITH THE SOURCE. BRANDSPEC_2026-09-17 writes
   "What changed *this week.*" TWICE: once for Home's news band (line 288) and once for the News tab
   (line 329). It writes no second sentence for either, so there is no unused News sentence in it to
   pick up, and inventing one is forbidden. The words used here are BRANDSPEC's own, line 200,
   quoting the guide's CONTENT STRATEGY section for the pillar this screen actually carries:
   `15% Civic Action: "What's going on in Woonsocket"`. Not one word is added or changed. The
   terminal period and the italic on the last phrase are BRANDSPEC's own formatting rules applied to
   BRANDSPEC's own words ("Serif headings are sentence case with terminal periods"; the section
   heading has "the last phrase in italic").

   THIS IS A JUDGEMENT CALL AND IT IS HIS, NOT MINE. It is ONE `content` string in two places (the
   fallback and the marker split). If his eye says the News tab should keep "What changed this
   week." then the duplicate has to come off HOME's news band instead, and that is the other half of
   the same one-line change. Both are named here so neither has to be rediscovered.

   SUPERSEDED, KEPT VERBATIM:
     #view-updates > h2[data-bwn]::after{ content:"What changed this week." }             (the fallback)
     #view-updates > h2[data-bwn]::after{ content:"this week." }                          (inside @supports)
     #view-updates > h2[data-bwn]::after::marker{ content:"What changed " }               (inside @supports)
   These rules are LATER in the file at the same specificity, which is how they win; `@supports`
   adds no specificity. Home's two sentences and Events' and Settings' are untouched.

   IT IS GATED, AND IT NEEDED NO NEW GATE LINE. This is a new English sentence in a CSS `content`
   string, which `t()` cannot reach. It rides on the SAME element and the SAME two pseudo-elements
   the old sentence did, `#view-updates > h2[data-bwn]::after` and its `::marker`, and both of those
   are already named in LANGGATE Part 2 below. VERIFIED BY READING THE COMPUTED CONTENT IN ARABIC
   AND KHMER, not by reasoning about it: both come back `none` / `""`. A Khmer or Arabic reader sees
   the gold hairline, the number and her own translated "News", and no English at all.
   NEEDS NATIVE REVIEW before anyone calls this sentence translated; it is English-only, like the
   other six, and VOICE_2026-09-20 section 2 already names the key (`bandNews`) and the wiring.
   CONTRAST: `--bw-ink` on `--bw-cream` 15.73 light, cream on `#0D2018` 15.73 dark, at 28px against
   a 3:1 large-text threshold. Unchanged from the sentence it replaces; same element, same colours. */
#view-updates > h2[data-bwn]::after{ content:"What's going on in Woonsocket." }
@supports selector(::after::marker){
  #view-updates > h2[data-bwn]::after{ content:"in Woonsocket." }
  #view-updates > h2[data-bwn]::after::marker{ content:"What's going on " }
}
/* ==== END HELPINFO 2026-09-22 ==== */


/* ==================================================================================================
   SHAPE 2026-09-22. THE BRANDSPEC 4px CORNER RADIUS, SWEPT OVER BOTH STYLESHEETS.

   THIS BLOCK IS A COMMENT AND NOTHING ELSE. IT DECLARES NO RULE. It sits here, immediately BEFORE
   `LANGGATE 2026-09-20, PART 2`, because Part 2 must stay the last block in this file: two
   `@supports selector(::after::marker)` blocks above it re-declare `content` on the same seven
   pseudo-elements, `@supports` adds no specificity, so anything appended AFTER Part 2 that touches
   those pseudo-elements silently un-gates them and prints 28px English as the largest type on the
   page in all fifteen non-English languages. Nothing in this pass declares a pseudo-element at all,
   and this block is inert by construction, but it still goes before Part 2 so the rule holds for
   whoever reads the end of this file next. VERIFIED AFTER WRITING: Part 2 is still last, and the
   seven sentences still print in English and come back `none` / `""` in Arabic and Khmer.

   THE SPEC, QUOTED, NOT PARAPHRASED. `BRANDSPEC_2026-09-17.md` A7 "Space, rhythm, rules, dividers",
   line 247: "Cards: 4 px corner radius throughout (mission/vision cards, palette cards, template
   cards, mark card). NOT pill shapes, NOT 12 to 20 px radii." Line 248: "Pills: one exists,
   'OFFICIAL PALETTE', 1 px gold border, fully rounded, gold caps label, transparent fill. That is
   the only pill in the guide AND IT IS A LABEL, NOT A BUTTON. The app's pill buttons and pill doors
   are the old app's idiom, not the brand's." The token list at line 347 sets `--radius:4px`, which
   `app.css` already defines and which every changed rule below now reads instead of a number.

   SO THE LINE THIS PASS DREW IS THE SPEC'S OWN LINE, AND IT IS NOT "FLATTEN EVERYTHING":
     - A BUTTON, a CARD, a PANEL, an INPUT, an IMAGE or a FOCUS RING goes to var(--radius).
     - A LABEL that is not a tap target stays fully rounded, because line 248 says the guide's one
       pill IS a label.
     - A FILTER chip stays fully rounded, because BRANDSPEC line 321 hands the Events filter row
       "the guide's OFFICIAL PALETTE pill idiom" by name, and that pill is fully rounded.
     - A CIRCLE that is a disc, an avatar, a seal, a dot or a spinner keeps its 50%.
     - ANYTHING CRISIS IS NOT TOUCHED AT ALL, whatever its radius.
   The one place an explicit BRANDSPEC sentence overrides the general rule is `.chip`: line 298
   names the event card's "55+" and "FREE" badges at "4 px radius, 28 px tall". Explicit wins, so
   `.chip` flattened even though it is a label. `.featured .tag` was already 4px from the
   2026-09-18 pass, so the two now agree.

   SEVENTEEN RULES CHANGED IN THIS FILE, ONE IN brand.css. Each carries its superseded value
   verbatim in a SHAPE 2026-09-22 comment at the end of its own line, reading `was <the old value>`, so
   any single one can be reverted without reverting the file. NOTHING WAS DELETED.
     .skip-link  0 0 12px 12px       .lang-toggle  999px        .topbar .tb-x  8px
     .hero-wx  999px                 .featured .readbtn  999px  .event-row:focus-visible  8px
     .details-btn:focus-visible 10px .chip  999px               .great .gt-read  50%
     .findnow  14px                  .privacy  10px             .sheet  20px 20px 0 0
     .nbtn  14px                     .hdr-gear  10px            .bw-banner img  16px
     .ev-month-sum:focus-visible 14px                           .csv-hsds  0 0 12px 12px
   brand.css: `.hdr .bwm-product::before` is a COLOUR change, not a radius one; see that file.

   THE TWO JUDGEMENT CALLS, NAMED SO HIS EYE CAN OVERRULE THEM IN ONE LINE EACH. `.featured
   .readbtn` (999px) and `.great .gt-read` (50%) are the 44x44 read-aloud buttons on the featured
   and spotlight cards. They rendered as circles. They are BUTTONS, not discs, and BRANDSPEC gives
   the app's other icon button - the search button, line 293 - "56 x 56, 4 px radius", so they were
   flattened to match it. If his eye says those two should stay round, put `999px` back on
   `.featured .readbtn` and `50%` back on `.great .gt-read` and nothing else moves.

   TWENTY-SEVEN RADII WERE LEFT ON PURPOSE. Listed so nobody has to re-derive them:
     CRISIS, NOT THIS PASS'S TO TOUCH (red is reserved for crisis in this app):
       .res-crisis 14px . .res-crisis.res-crisis-acc 14px . .callbtn 11px . .res-crisis .copt 12px
       .res-crisis .cpill 9px . .res-crisis-acc .cbtn 12px . .res-crisis-acc .cmore 12px
       .res-crisis-acc .cq 11px . .res-crisis-acc .crisis-head-btn 12px
       .res-211 14px / 16px / 16px . .res-211 .big 12px / 8px . .res-211 a 10px / 12px / 10px
       .res-resulthead .rrh-crisis a 13px . .res-helpbar .rrh-crisis a 13px
       html[data-crisis-slim="1"] .res-suggest li.crisis-sug 8px
       .emg-banner 16px . .emg-banner .emg-tag 999px . .emg-item 13px . .emg-actions a 9px
       .emg-note 12px . .emg-radio 12px
       .emerg 14px and .civic-emerg 10px: both are `--red-soft` on `--red`. Red is reserved for
         crisis, so whichever side of the line they are on, they are not mine. LEFT, AND SAID SO.
       .err 10px: `--red-soft` on `--red` again, in the confirm sheet. Same call. LEFT.
       .res-suggest li 9px: already carries its own note. `li.crisis-sug` INHERITS this radius and
         has none of its own, so moving it moves a crisis element's computed style. LEFT.
     LABEL PILLS, WHICH ARE THE ONE PILL THE GUIDE KEEPS (line 248, "it is a label, not a button"):
       .res .openpill 999px . .res-spec 999px . .res-lang 999px (which already carried the note
       "the guide draws count pills fully rounded" from 2026-09-18) . .status 999px .
       .csv-why 99px . .ev-month-n 999px
     FILTER CHIPS, WHICH BRANDSPEC LINE 321 HANDS THE "OFFICIAL PALETTE PILL IDIOM" BY NAME:
       .fh-cat 999px . .help-cat 99px (twice) . .hh-chip 999px
     CIRCLES, WHICH ARE MEANT TO BE CIRCLES:
       .nbtn .ic 50% (the filled disc behind the active tab icon) . .hl-dot 50% (carousel dots) .
       .radio-live .rl-dot 50% (the live dot) . .off-ava and img.off-ava 50% (official avatars) .
       .res-search-row.is-busy .res-go::after 50% (the spinner ring)
       In brand.css, all four of its radii are 50% and all four were left: .bw-banner .hdr-gear and
       .hdr .hdr-gear are 44x44 transparent hit areas with no background and no border, so their
       radius paints nothing at all; .hdr .hdr-row>.hdr-mark is the city SEAL's disc (and is
       display:none today); #powered-by .powered-by-mark is the two 20px circular marks, and
       BRANDSPEC A6 says of the stamp "the mark's own black ring is its edge".
     ALREADY SQUARE, SO NOTHING TO DO: .datebox 0 . .res-search 0 . #lang-seg 0
     THE TWO 3px FOCUS RINGS, LEFT DELIBERATELY: .civic-link:focus-visible and the @layer
       prose-links focus ring are both `border-radius:3px`. 3px is itself a brand constant
       (`--rule-w:3px`), they are one pixel from the target, they paint only on keyboard focus, and
       the prose-links rule is the one whose selector list names tel:911, tel:988, tel:211 and
       sms:988 in `:not()`. Moving a rule that reads the crisis numbers to buy one pixel is a bad
       trade. LEFT, AND SAID SO.

   THIS PASS ADDED NO IMPORTANT FLAG. Counting only declarations outside comments, this file held
   42 of them before this pass and holds 42 after; brand.css held 21 and holds 21.
   NO NEW ENGLISH STRING, NO NEW `content:`, NO NEW HEX IN THIS FILE.
   UNDO, WHOLE FILE: copy NEVER_OVERWRITE_BACKUPS\rebuild_2026-09-18\app.css.PRE_SHAPE app.css
   Record: C:\!Brain\LFG4\NEVER_OVERWRITE\SHAPE_2026-09-22.md
   ================================================================================================== */


/* ==================================================================================================
   LANGGATE 2026-09-20, PART 2. MUST BE LAST IN THIS FILE.
   Part 1 sits beside the fallback sentences around line 3395, but two @supports blocks below it
   re-declare `content` to cut each sentence into a roman ::marker and an italic ::after. @supports
   adds no specificity, so those later declarations beat Part 1 in every browser that supports
   ::after::marker, which is every Chromium one. The gate therefore has to be restated here, after
   everything, and it must blank the ::marker as well or the roman half of the sentence survives on
   its own and a Khmer reader gets the fragment "What changed " with nothing after it, which is
   worse than the whole English line.
   See Part 1 for why this exists at all and for the right way to remove it.
   ================================================================================================== */
html:not([lang|="en"]) .home-sec[data-sec="updates"] > .section-label::after,
html:not([lang|="en"]) .home-sec[data-sec="events"] > .section-label::after,
html:not([lang|="en"]) .home-sec[data-sec="quicklinks"] > .section-label::after,
html:not([lang|="en"]) #view-events > h2[data-bwn]::after,
html:not([lang|="en"]) #view-updates > h2[data-bwn]::after,
html:not([lang|="en"]) #view-settings > h2[data-bwn]::after,
html:not([lang|="en"]) #citysvc::before,
html:not([lang|="en"]) #view-community > .res-search .res-search-head::after{ content:none; display:none }
html:not([lang|="en"]) .home-sec[data-sec="updates"] > .section-label::after::marker,
html:not([lang|="en"]) .home-sec[data-sec="events"] > .section-label::after::marker,
html:not([lang|="en"]) .home-sec[data-sec="quicklinks"] > .section-label::after::marker,
html:not([lang|="en"]) #view-events > h2[data-bwn]::after::marker,
html:not([lang|="en"]) #view-updates > h2[data-bwn]::after::marker,
html:not([lang|="en"]) #view-settings > h2[data-bwn]::after::marker,
html:not([lang|="en"]) #citysvc::before::marker,
html:not([lang|="en"]) #view-community > .res-search .res-search-head::after::marker{ content:"" }


/* ==================================================================================================
   THE EVENTS CALENDAR. DAY, WEEK, MONTH.  (lifted into app.css 2026-09-23)
   --------------------------------------------------------------------------------------------------
   `[RYAN 2026-09-23]` "as for the events calendar it should be much more like my calendar on
   agingwellinc.org"

   This was `evcal_2026-09-17.css`, a separate sheet with its own <link> in index.html. A sheet on top
   is a layer, and a layer fails the deletion test. app.css owns the look of the Events page, so the
   calendar's look belongs here. The old file is retired to NEVER_OVERWRITE_BACKUPS, never deleted.
   `[RYAN 2026-09-02 CARD_DECLAN]` "Open the wall files up. Go fix things at the source and delete the
   patches that exist only to work around them."

   COLOUR, AND THE ONE RULE THAT OVERRIDES TASTE. Gold for today, gold-soft for a closed day, and that
   is the whole colour story, which is his. RED IS RESERVED FOR CRISIS IN THIS APP AND IS NOT IN THIS
   BLOCK: his site strikes a cancelled class through in red and ours does it in muted ink, because a
   resident here learns that red means 911 / 988 and nothing else. Every colour below is a token
   app.css already defines (--navy which is Woonsocket Burgundy #7A2636, --navy-deep which is Deep
   Navy #102F3D, --gold #C49A2A, --gold-soft, --surface, --canvas which is Warm Cream #FAF6EC, --ink,
   --muted, --border), so dark mode and high contrast follow for free.

   TYPE SIZE. His root is 18px, his day numbers 21.6px, his weekday headers 12.24px. Ours were 15px
   day numbers and 11px weekday letters, which is the smallest type on the Events page on a phone held
   by someone who came here because the print was too small everywhere else. Day numbers are 19px now
   and weekday letters 13px. Nothing in this block is under 12px.

   44 x 44. `[RYAN 2026-09-02]` "Keep 44px." Every tappable thing here is at least 44px on both sides,
   the JS measures the day cells after painting rather than trusting this file, and the tight and strip
   fallbacks below are what make 320px hold.
   ================================================================================================== */

#evcal { margin: 0 0 12px; }
#evcal[hidden] { display: none; }
/* while an event's own page is open (event_landing_2026-08-24.css PART TWO hides the search, the
   chips and the list one by one), the calendar steps aside too. Measured 2026-09-17: without this
   line the landing page started 474px down, under the grid. */
#view-events.el-on > #evcal { display: none !important; }

/* ---- the DAY / WEEK / MONTH pill, his .cal-tg shape (his measures 110.7 x 46) ------------------ */
.evcal-toggle {
  display: flex; gap: 0; margin: 0 0 10px; padding: 3px;
  border: 1.5px solid var(--border); border-radius: var(--radius); background: var(--surface);
  /* SHAPE 2026-09-23: was border-radius:999px, and so was .evcal-tg below, and so were the two
     Extra Large overrides further down (16px and 12px). Four values, one decision.
     THE PILL RULING IS RETIRED AND THIS SAYS SO OUT LOUD. `[RYAN 2026-08-12 R-005]` set the pill
     shape on this control. The 2026-09-18 brand decision set the app's corner radius to 4px and
     BRANDSPEC A7 line 248 records what the guide actually does with a pill: "the guide has ONE pill
     and it is a label, not a button". Day / Week / Month are buttons. R-005 is therefore SUPERSEDED
     BY THE 2026-09-18 4px DECISION, not overruled by an agent's taste, and it is the LAST pill
     button left on Events: every other control on that screen, the filter chips, the calendar
     arrows and "Show N more", went to 4px on 2026-09-22 and 2026-09-23 and this one was missed
     because it is a component of its own. BRANDFINISH_2026-09-23 section 6 item 11 saw it and
     handed it back rather than reach outside its four files. This closes it.
     Nothing else about the control moves: 46px minimum height, the 3px track padding, the
     letter-spacing and its zero-tracking pair for Arabic, Khmer, Lao and Bengali, the focus ring
     and the Extra Large column stack are all untouched, so no tap target changes size.
     UNDO: put 999px back on these two, 16px and 12px on the two Extra Large rules below. */
}
.evcal-tg {
  flex: 1 1 0; min-width: 0; min-height: 46px; box-sizing: border-box;
  border: none; background: transparent; color: var(--navy); cursor: pointer;
  border-radius: var(--radius); padding: 6px 4px;   /* SHAPE 2026-09-23: was 999px. See the note on .evcal-toggle above */
  font-family: var(--font-label); font-weight: 700; font-size: 16px; line-height: 1.2;
  letter-spacing: .04em; text-transform: uppercase;
  -webkit-tap-highlight-color: transparent;
}
.evcal-tg-on { background: var(--navy-deep); color: #fff; }
/* at Extra Large text (.phone carries zoom 2) three words will not sit side by side on a 320px phone:
   measured 2026-09-23 at 320x568, MONTH broke to "MO NT H" inside its pill. The three take a column
   instead, full width each, which is the same remedy the header above uses for the month name. */
.phone.text-xlarge #evcal .evcal-toggle { flex-direction: column; gap: 4px; border-radius: var(--radius); }   /* SHAPE 2026-09-23: was 16px */
.phone.text-xlarge #evcal .evcal-tg { width: 100%; border-radius: var(--radius); }   /* SHAPE 2026-09-23: was 12px */
.evcal-tg:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.evcal-tg:lang(ar), .evcal-tg:lang(km), .evcal-tg:lang(lo), .evcal-tg:lang(bn) { letter-spacing: 0; text-transform: none; }

/* ---- header: [<] Month Year (n) [>] ------------------------------------------------------------ */
.evcal-head { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
.evcal-lbl {
  flex: 1 1 auto; min-width: 0; margin: 0; text-align: center;
  /* the LABEL face, which is where BRANDSPEC puts every date. var(--font-label) chains
     'Barlow Condensed','Source Sans Pro',sans-serif, and 'Source Sans Pro' is the family
     app_fonts.css registers the Noto Arabic / Khmer / Lao / Bengali / KR / SC / TC subsets under, so
     a non-Latin month name still lands on its own face. */
  font-family: var(--font-label); font-weight: 700; font-size: 18px; line-height: 1.2; color: var(--ink);
  overflow-wrap: anywhere;
}
.evcal-lbl:focus { outline: none; }
.evcal-daylbl { font-size: 19px; }
.evcal-mn {
  display: inline-block; min-width: 22px; padding: 1px 7px; margin-inline-start: 2px;
  border-radius: 999px; background: var(--sky-soft); color: var(--sky);
  font-family: var(--font-sans); font-size: 13px; font-weight: 800; line-height: 1.4; vertical-align: middle;   /* POLISH 2026-10-01: was 'Source Sans Pro', sans-serif. Off-kit: the brand kit's three voices are Cormorant Garamond, DM Sans and Barlow Condensed. var(--font-sans) is DM Sans and still chains 'Source Sans Pro' second, which is the family app_fonts.css registers the Noto subsets under, so no non-Latin glyph loses its face and NO .woff2 changes. */
}
.evcal-nav {
  flex: none; width: 48px; height: 48px; min-width: 48px; min-height: 48px; box-sizing: border-box;
  border: 1.5px solid var(--border); background: var(--surface); color: var(--navy);
  border-radius: var(--radius); font-size: 26px; line-height: 1; cursor: pointer; font-family: inherit;   /* SHAPE 2026-09-23: was 12px. FINAL_2026-09-23 section 8 item 12 names the two calendar arrows as genuinely off-spec; BRANDSPEC A7 is 4px throughout. --radius is 4px. Undo: 12px. */
}
/* at Large and Extra Large text (.phone carries zoom 1.5 / 2) the month name no longer fits between
   two 48px arrows: measured 2026-09-17 at 375px, "September 2026" stacked to 156px tall at 200
   percent. The name takes its own full-width line and the arrows sit under it, at the two ends. */
.phone.text-large #evcal .evcal-head, .phone.text-xlarge #evcal .evcal-head { flex-wrap: wrap; justify-content: space-between; }
.phone.text-large #evcal .evcal-lbl, .phone.text-xlarge #evcal .evcal-lbl { flex: 1 0 100%; order: -1; text-align: start; margin-bottom: 4px; }
.evcal-nav:disabled { opacity: .3; cursor: default; }
.evcal-nav:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }

/* the second month's own caption, the way his prints two months one under the other */
.evcal-mcap {
  margin: 14px 0 6px; text-align: center;
  font-family: var(--font-label); font-weight: 700; font-size: 18px; line-height: 1.2; color: var(--ink);
  overflow-wrap: anywhere;
}

/* ---- weekday letters. 13px, up from 11px. His are 12.24px. ------------------------------------ */
.evcal-dow {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px;
  font-size: 13px; font-weight: 700; color: var(--muted); text-align: center; margin: 0 0 2px;
  text-transform: uppercase; letter-spacing: .04em;
}
.evcal-dow > span { overflow: hidden; white-space: nowrap; }

/* ---- the grid --------------------------------------------------------------------------------- */
.evcal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; }
.evcal-cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 46px; min-width: 0; box-sizing: border-box; border-radius: 8px;
  font-family: var(--font-label); font-size: 19px; line-height: 1.05; padding: 3px 0;   /* POLISH 2026-10-01: was 'Source Sans Pro', sans-serif. This file's own comment at .evcal-lbl says the LABEL face is "where BRANDSPEC puts every date"; this is a date and was the only one not obeying it. --font-label chains 'Source Sans Pro' second, so non-Latin is unaffected and NO .woff2 changes. */
  color: var(--ink); background: transparent; border: 1.5px solid transparent;
}
/* a cell with no day in it, and a day already behind us in the current week. It is EMPTY SPACE, not a
   grey number: `[RYAN 2026-09-23]` on the old grid printing 22 of 30 cells dead. A week entirely in
   the past is not painted at all, in the JS, so it never reaches this file. */
.evcal-blank { visibility: hidden; }
.evcal-empty { color: var(--muted); }
.evcal-has {
  border-color: var(--navy); background: var(--surface); color: var(--navy);
  font-weight: 800; cursor: pointer; font-family: var(--font-label);   /* POLISH 2026-10-01: was 'Source Sans Pro', sans-serif. Off-kit; the label face per BRANDSPEC, which still chains 'Source Sans Pro' second. NO .woff2 changes. */
  -webkit-tap-highlight-color: transparent;
}
.evcal-has:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
/* the open day is filled with --navy-fill, the token app.css defines for exactly this (dark mode
   keeps it a deep blue so white text stays readable; --navy itself turns pale in dark mode) */
.evcal-has.evcal-sel { background: var(--navy-fill, var(--navy)); color: #fff; border-color: var(--navy-fill, var(--navy)); }
.evcal-has.evcal-sel .evcal-t { background: rgba(255,255,255,.22); color: #fff; }
/* GOLD FOR TODAY, GOLD-SOFT FOR CLOSED, AND THAT IS THE WHOLE COLOUR STORY */
.evcal-today { box-shadow: inset 0 0 0 2px var(--gold); background: var(--gold-soft); }
.evcal-empty.evcal-today { color: var(--ink); font-weight: 800; }
.evcal-closed { background: var(--gold-soft); border-color: var(--navy-deep); }
.evcal-closedt { font-size: 12px; font-weight: 700; color: var(--gold-ink); line-height: 1.15; text-align: center; overflow-wrap: anywhere; }
.evcal-n { font-size: 19px; }
.evcal-t {
  font-size: 12px; font-weight: 800; margin-top: 2px; line-height: 1.3;
  background: var(--sky-soft); color: var(--sky); border-radius: 999px; padding: 0 6px; min-width: 18px; text-align: center;
}

/* ---- tight: the second try when a cell measured under 44px. No gap, bleed to 4px of the edge.
   The view's own padding is 16px (index.html #view-events), so -12px lands 4px from the screen.
   Cells keep their border as the separator. At 320px this is what makes 44.6px. ---------------- */
/* BW-12, 2026-09-24. THE BLEED MOVES UP ONE BOX. SAME PIXELS ON SCREEN, NO BOX OVERFLOWING.
   The bleed itself is right and is kept: it is what makes a 44.6px calendar cell at 320px, and
   taking it away would put every day of the month back under the tap-target floor. What was wrong
   was WHERE it was applied. `margin-inline:-12px` sat on the GRID, inside `#evcal`, so the grid
   was 24px wider than its own parent and `#evcal` measured scrollWidth 300 in clientWidth 288 at
   320px English Normal, and 175 in 163 at 390px Russian Extra Large. A child wider than its
   parent is the definition of the fault BW-12 reports, even when the paint happens to land where
   it was wanted.
   Moving the negative margin onto `#evcal` makes the container as wide as the grid it holds, so
   the grid fits it exactly and nothing overflows anything. `#view-events` carries 16px of its own
   padding (index.html), and -12px reaches into that padding without crossing the view's own edge,
   so the page still does not scroll sideways: measured at 320, 375 and 390 in all sixteen
   languages, light and dark, at all three text steps. The cells land on the same pixels they
   landed on before, 4px from the screen edge, which is what the note above asked for.
   Undo: put `margin-inline:-12px` back on the grid and the dow row, and drop it from `#evcal`. */
#evcal.evcal-tight { margin-inline: -12px; }
#evcal.evcal-tight .evcal-grid, #evcal.evcal-tight .evcal-dow { gap: 0; margin-inline: 0; }
#evcal.evcal-tight .evcal-cell { border-radius: 6px; }

/* ---- strip: the one-dimensional fallback when even tight is under 44px (Extra Large text on a
   narrow phone). Only the days that have something, sideways, one dimension instead of two. ----- */
.evcal-strip {
  display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 8px;
  -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; scrollbar-width: thin;
}
.evcal-chip {
  flex: none; scroll-snap-align: start;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 64px; min-height: 64px; box-sizing: border-box;
  border: 1.5px solid var(--navy); background: var(--surface); color: var(--navy);
  border-radius: 12px; padding: 6px 8px; cursor: pointer; font-family: var(--font-sans);   /* POLISH 2026-10-01: was 'Source Sans Pro', sans-serif. Off-kit; DM Sans, which still chains 'Source Sans Pro' second. NO .woff2 changes. */
  -webkit-tap-highlight-color: transparent;
}
.evcal-chip:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.evcal-chip.evcal-sel { background: var(--navy-fill, var(--navy)); color: #fff; border-color: var(--navy-fill, var(--navy)); }
.evcal-chip.evcal-sel .evcal-t, .evcal-chip.evcal-sel .evcal-chip-d { background: rgba(255,255,255,.22); color: #fff; }
.evcal-chip-d { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.evcal-chip-n { font-size: 20px; font-weight: 800; line-height: 1.05; }

/* ---- WEEK. Seven day cards stacked, each one a time-and-title list, the shape of his .wk-card. -- */
.evcal-pager { display: flex; gap: 8px; margin: 0 0 8px; }
.evcal-pg {
  flex: 1 1 0; min-width: 0; min-height: 48px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  border: 1.5px solid var(--navy); background: var(--surface); color: var(--navy);
  border-radius: 12px; padding: 8px 10px; cursor: pointer;
  font-family: var(--font-label); font-weight: 700; font-size: 15px; letter-spacing: .04em;
  text-align: center; overflow-wrap: anywhere;
}
.evcal-pg:disabled { opacity: .35; cursor: default; }
.evcal-pg:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.evcal-wkgrid { display: flex; flex-direction: column; gap: 8px; }
.evcal-wkcard {
  border: 1.5px solid var(--border); border-radius: 12px; background: var(--surface);
  padding: 8px 10px 10px; box-sizing: border-box;
}
.evcal-wkcard.evcal-today { border: 3px solid var(--gold); background: var(--gold-soft); padding: 6px 8px 8px; box-shadow: none; }
.evcal-wkcard.evcal-closed { background: var(--gold-soft); border-color: var(--navy-deep); }
.evcal-wkhd {
  display: flex; align-items: baseline; gap: 8px; width: 100%; min-height: 44px; box-sizing: border-box;
  border: none; background: transparent; color: var(--navy); cursor: pointer; padding: 4px 0;
  text-align: start; -webkit-tap-highlight-color: transparent;
}
.evcal-wkhd:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.evcal-wkdow { font-family: var(--font-label); font-weight: 700; font-size: 17px; letter-spacing: .04em; text-transform: uppercase; }
.evcal-wkdate { font-family: var(--font-label); font-size: 15px; font-weight: 700; color: var(--muted); }   /* POLISH 2026-10-01: was 'Source Sans Pro', sans-serif. One of the twelve week-strip date numbers LOOK_2026-10-01.md named as off-kit. Label face per BRANDSPEC, 'Source Sans Pro' still second in the chain. NO .woff2 changes. */
.evcal-wkdow:lang(ar), .evcal-wkdow:lang(km), .evcal-wkdow:lang(lo), .evcal-wkdow:lang(bn) { letter-spacing: 0; text-transform: none; }
.evcal-wklist { list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.evcal-wkitem { display: flex; flex-wrap: wrap; gap: 4px 8px; font-size: 16px; line-height: 1.35; color: var(--ink); }
.evcal-wktime { font-family: var(--font-label); font-size: 15px; font-weight: 800; color: var(--navy); white-space: nowrap; }   /* POLISH 2026-10-01: was 'Source Sans Pro', sans-serif. Same off-kit finding as .evcal-wkdate above. NO .woff2 changes. */
.evcal-wktitle { flex: 1 1 60%; min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
/* CANCELLED STAYS IN PLACE AND SAYS SO. Struck through, never removed, never red: red is crisis. */
.evcal-cxl .evcal-wktime, .evcal-cxl .evcal-wktitle { text-decoration: line-through; color: var(--muted); }
.evcal-cxlword {
  font-family: var(--font-label); font-weight: 700; font-size: 13px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--gold-ink); background: var(--gold-soft);
  border-radius: 999px; padding: 1px 8px; align-self: center;
}
.evcal-cxlword:lang(ar), .evcal-cxlword:lang(km), .evcal-cxlword:lang(lo), .evcal-cxlword:lang(bn) { letter-spacing: 0; text-transform: none; }
/* a day with nothing on it is explained in words, never left blank and never "0 items" */
.evcal-wknone { margin: 2px 0 0; font-size: 15px; color: var(--muted); }
.evcal-wkclosed { margin: 2px 0 0; font-size: 15px; font-weight: 700; color: var(--gold-ink); }

/* ---- DAY. One day, on its own URL. ------------------------------------------------------------ */
.evcal-daybar { display: flex; gap: 8px; margin: 0 0 8px; flex-wrap: wrap; }
.evcal-daybar .evcal-pg { flex: 1 1 40%; }

/* ---- an empty month, or an empty day: says so, offers the next one with something ------------- */
.evcal-none { margin: 4px 0 8px; color: var(--muted); font-size: 14px; text-align: center; }
.evcal-go {
  display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap;
  width: 100%; min-height: 48px; box-sizing: border-box; margin: 0;
  border: 1.5px solid var(--navy); background: var(--surface); color: var(--navy);
  border-radius: 12px; padding: 10px 12px; font-family: var(--font-label); font-weight: 700;
  font-size: 14px; letter-spacing: .04em; cursor: pointer;
}
.evcal-go:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.evcal-go-n {
  display: inline-block; min-width: 22px; padding: 1px 7px; border-radius: 999px;
  background: var(--sky-soft); color: var(--sky); font-family: var(--font-sans); font-size: 13px; font-weight: 800;   /* POLISH 2026-10-01: was 'Source Sans Pro', sans-serif. Off-kit; DM Sans, 'Source Sans Pro' still second in the chain. NO .woff2 changes. */
}

/* ---- the line that says which day is open, and how many are on it ---------------------------- */
.evcal-showing-t { margin: 0 0 8px; font-size: 15px; font-weight: 700; color: var(--ink); }

/* `.evcal-go`, `.evcal-back` and the pager carry letter-spacing .04em on a TRANSLATED label. Arabic,
   Khmer, Lao and Bengali join or stack their marks and break under any tracking, so it is zeroed for
   those four, the same pair helpfold_2026-08-28.css:334 and app.css:169 already use. */
.evcal-go:lang(ar), .evcal-go:lang(km), .evcal-go:lang(lo), .evcal-go:lang(bn),
.evcal-pg:lang(ar), .evcal-pg:lang(km), .evcal-pg:lang(lo), .evcal-pg:lang(bn),
.evcal-chip-d:lang(ar), .evcal-chip-d:lang(km), .evcal-chip-d:lang(lo), .evcal-chip-d:lang(bn),
.evcal-dow:lang(ar), .evcal-dow:lang(km), .evcal-dow:lang(lo), .evcal-dow:lang(bn) { letter-spacing: 0; }


/* ==================================================================================================
   WORDS 2026-09-23. THE TWO NEW BAND SENTENCES, IN ALL SIXTEEN LANGUAGES.

   CARD 2026-09-23 wrote `bandEvents` ("Meetings and shows, near you.") and LASTMILE/CARD wrote
   `bandQuickLinks` ("Trash day, the bus, the library."), and both shipped ENGLISH ONLY behind
   LANGGATE 2026-09-20, which blanks every CSS `content` sentence for a reader who is not reading
   English. That is the right default for an untranslated sentence and the wrong resting place for a
   finished one: on fifteen languages Home's bands 02 and 03 had a gold hairline, a number and a
   label and no sentence at all. This block gives those two sentences their sixteen.

   IT DOES NOT TOUCH LANGGATE. Part 1 and Part 2 are byte-identical and still blank the other six
   sentences, which are still English-only and still need this same pass. This block sits AFTER
   Part 2 (Part 2's own header asks to be last; it is last among the GATES, and a rule that RESTORES
   content for one named language has to come after the rule that blanks it or it cannot win).
   Specificity is a tie by construction - `html[lang="es"]` and `html:not([lang|="en"])` are both
   (0,1,1) at the html element - so source order decides and nothing needed `!important`.
   `html[lang="..."]` matches exactly the seventeen values app.js:227 writes, so "zh" and "zh-Hant"
   are told apart and neither leaks into the other.

   EVERY DECLARATION RESTORES THREE THINGS, because Part 2 takes three away: `content` on the
   ::after, `content` on its ::marker (the roman half), and `display`, which Part 2 sets to none.
   Outside @supports the WHOLE sentence prints roman, exactly like the English fallback. Inside
   @supports the sentence is cut into the guide's roman-then-italic shape at the same comma.

   ARABIC IS NOT FORCED LEFT TO RIGHT ANY MORE, FOR THESE TWO. The shared rule near the top of the
   VOICE block sets `direction:ltr; unicode-bidi:isolate` on all seven sentence hooks, and its own
   comment says why: the sentences "are always English and always Latin". These two no longer are.
   Left alone, an Arabic sentence would be laid out as an isolated LTR run and its full stop would
   land at the wrong end - the exact bug that comment was written about, with the sides swapped. So
   Arabic, and only Arabic, gets `direction:rtl` back for these two pseudo-elements. The other
   fifteen are LTR scripts and keep the shared rule untouched.

   THE FONT IS ALREADY RIGHT AND NOTHING WAS ADDED. `--font-serif` is
   "Cormorant Garamond",Georgia,"Source Sans Pro","Times New Roman",serif, and app_fonts.css
   registers all seven self-hosted Noto subsets (Arabic, Khmer, Lao, Bengali, SC, TC, KR) UNDER the
   family name 'Source Sans Pro' with unicode-ranges, so a Khmer or Arabic character in these
   sentences resolves to its own subset by the normal fallback chain. No @font-face and no
   font-family was added by this block.

   NO CHARACTER HERE IS GUESSED. Every codepoint in all thirty-two strings was checked, before it
   was written, against the actual cmap of the font that will draw it (fontTools, the union of the
   five base Latin/Latin-ext/Cyrillic/Greek/Vietnamese faces plus that language's own Noto subset),
   which is the same check FALSIFY_FONTPROOF_2026-09-07.mjs runs. Two characters came back missing
   from the subsetted Traditional Chinese font and were NOT shipped: U+6F14 and U+9644. The word was
   changed instead of the font, so Traditional reads "節目" where Simplified reads "节目". Nothing was
   added to any .woff2 file. Vocabulary was taken from the strings files wherever the same thing is
   already named there (trashTitle, qlRipta, qlLibrary, evvBandBig), so a resident meets the same
   word in the sentence and on the tile.

   MACHINE TRANSLATED, HONESTLY MARKED, NEEDS NATIVE REVIEW on all thirty-two values.
   `[RYAN 2026-08-07]` "essentially 99.99% machine translated with GENUINE I NEED HUMAN EYES ON IT
   identified - otherwise we help people but protect ourselves."
   NO CRISIS STRING IS IN THIS BLOCK. No value here contains a numeral of any script, so 911, 988
   and 211 cannot be reached, imitated or reordered from anything written below (LAW L1).
   There is no em-dash in any of the thirty-two.
   UNDO: delete this whole block. LANGGATE goes back to blanking both sentences and Home reads
   exactly as it did this morning. Backup: NEVER_OVERWRITE_BACKUPS/words_2026-09-23/app.css.PRE_WORDS
   ================================================================================================== */

/* ---- es ---- */
html[lang="es"] .home-sec[data-sec="events"] > .section-label::after{ content:"Eventos, cerca de usted."; display:block }
html[lang="es"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Día de basura, el autobús, la biblioteca."; display:block }

/* ---- pt ---- */
html[lang="pt"] .home-sec[data-sec="events"] > .section-label::after{ content:"Eventos, perto de si."; display:block }
html[lang="pt"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Dia do lixo, o autocarro, a biblioteca."; display:block }

/* ---- fr ---- */
html[lang="fr"] .home-sec[data-sec="events"] > .section-label::after{ content:"Événements, près de chez vous."; display:block }
html[lang="fr"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Jour des ordures, le bus, la bibliothèque."; display:block }

/* ---- ht ---- */
html[lang="ht"] .home-sec[data-sec="events"] > .section-label::after{ content:"Aktivite, toupre ou."; display:block }
html[lang="ht"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Jou fatra, otobis la, bibliyotèk la."; display:block }

/* ---- it ---- */
html[lang="it"] .home-sec[data-sec="events"] > .section-label::after{ content:"Eventi, vicino a te."; display:block }
html[lang="it"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Giorno della spazzatura, l'autobus, la biblioteca."; display:block }

/* ---- pl ---- */
html[lang="pl"] .home-sec[data-sec="events"] > .section-label::after{ content:"Wydarzenia, blisko ciebie."; display:block }
html[lang="pl"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Dzień wywozu śmieci, autobus, biblioteka."; display:block }

/* ---- ru ---- */
html[lang="ru"] .home-sec[data-sec="events"] > .section-label::after{ content:"События, рядом с вами."; display:block }
html[lang="ru"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"День вывоза мусора, автобус, библиотека."; display:block }

/* ---- el ---- */
html[lang="el"] .home-sec[data-sec="events"] > .section-label::after{ content:"Εκδηλώσεις, κοντά σας."; display:block }
html[lang="el"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Ημέρα σκουπιδιών, το λεωφορείο, η βιβλιοθήκη."; display:block }

/* ---- vi ---- */
html[lang="vi"] .home-sec[data-sec="events"] > .section-label::after{ content:"Sự kiện, gần bạn."; display:block }
html[lang="vi"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"Ngày đổ rác, xe buýt, thư viện."; display:block }

/* ---- ar ---- */
html[lang="ar"] .home-sec[data-sec="events"] > .section-label::after{ content:"الفعاليات، بالقرب منك."; display:block }
html[lang="ar"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"يوم النفايات، الحافلة، المكتبة."; display:block }

/* ---- zh ---- */
html[lang="zh"] .home-sec[data-sec="events"] > .section-label::after{ content:"活动，在您身边。"; display:block }
html[lang="zh"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"垃圾日、巴士、图书馆。"; display:block }

/* ---- zh-Hant ---- */
html[lang="zh-Hant"] .home-sec[data-sec="events"] > .section-label::after{ content:"活動，在您身邊。"; display:block }
html[lang="zh-Hant"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"垃圾日、巴士、圖書館。"; display:block }

/* ---- ko ---- */
html[lang="ko"] .home-sec[data-sec="events"] > .section-label::after{ content:"행사, 가까운 곳에서."; display:block }
html[lang="ko"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"쓰레기 수거일, 버스, 도서관."; display:block }

/* ---- km ---- */
html[lang="km"] .home-sec[data-sec="events"] > .section-label::after{ content:"ព្រឹត្តិការណ៍ នៅជិតអ្នក។"; display:block }
html[lang="km"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"ថ្ងៃសម្រាម ឡានក្រុង បណ្ណាល័យ។"; display:block }

/* ---- lo ---- */
html[lang="lo"] .home-sec[data-sec="events"] > .section-label::after{ content:"ກິດຈະກຳ ຢູ່ໃກ້ທ່ານ."; display:block }
html[lang="lo"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"ມື້ຂີ້ເຫຍື້ອ ລົດເມ ຫໍສະໝຸດ."; display:block }

/* ---- bn ---- */
html[lang="bn"] .home-sec[data-sec="events"] > .section-label::after{ content:"অনুষ্ঠান, আপনার কাছেই।"; display:block }
html[lang="bn"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"আবর্জনা দিবস, বাস, লাইব্রেরি।"; display:block }

/* ---- yo ---- CONNECT 2026-09-29. ADDED: this language had NO rule here, so LANGGATE Part 2
   blanked Home's second band and a yo reader saw a label with no sentence under it. MEASURED in a
   real browser before this line: getComputedStyle(...,'::after').content === "none".
   The noun is the app's own shipped word (strings.yo.js key eventsTitle), not a new translation.
   THE SECOND HALF IS A DRAFT AND NEEDS NATIVE REVIEW: it is lifted verbatim out of
   strings.yo.js key fhNearTitle -> "Ṣé kí a fi èyí tí ó súnmọ̀ yín jù hàn?"
   rather than invented here, but a fragment cut from a question is not the same as a phrase a
   speaker would write. Mark it with the same honesty the crisis labels carry. One line to change. */
html[lang="yo"] .home-sec[data-sec="events"] > .section-label::after{ content:"Ìṣẹ̀lẹ̀, súnmọ̀ yín."; display:block }

/* ---- kea ---- CONNECT 2026-09-29. ADDED: this language had NO rule here, so LANGGATE Part 2
   blanked Home's second band and a kea reader saw a label with no sentence under it. MEASURED in a
   real browser before this line: getComputedStyle(...,'::after').content === "none".
   The noun is the app's own shipped word (strings.kea.js key eventsTitle), not a new translation.
   THE SECOND HALF IS A DRAFT AND NEEDS NATIVE REVIEW: it is lifted verbatim out of
   strings.kea.js key fhNearTitle -> "Mostra kuze ki sta mas pértu di bo?"
   rather than invented here, but a fragment cut from a question is not the same as a phrase a
   speaker would write. Mark it with the same honesty the crisis labels carry. One line to change. */
html[lang="kea"] .home-sec[data-sec="events"] > .section-label::after{ content:"Evéntu, pértu di bo."; display:block }

/* Arabic only: give these two back their own direction. See the block header. */
html[lang="ar"] .home-sec[data-sec="events"] > .section-label::after,
html[lang="ar"] .home-sec[data-sec="quicklinks"] > .section-label::after{ direction:rtl; unicode-bidi:isolate }

/* the guide's shape: roman, comma, italic, terminal stop. Same mechanism as the English above. */
@supports selector(::after::marker){
  html[lang="es"] .home-sec[data-sec="events"] > .section-label::after{ content:"cerca de usted."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Eventos, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="es"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"la biblioteca."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Día de basura, el autobús, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] .home-sec[data-sec="events"] > .section-label::after{ content:"perto de si."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Eventos, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"a biblioteca."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Dia do lixo, o autocarro, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] .home-sec[data-sec="events"] > .section-label::after{ content:"près de chez vous."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Événements, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"la bibliothèque."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Jour des ordures, le bus, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] .home-sec[data-sec="events"] > .section-label::after{ content:"toupre ou."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Aktivite, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"bibliyotèk la."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Jou fatra, otobis la, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] .home-sec[data-sec="events"] > .section-label::after{ content:"vicino a te."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Eventi, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"la biblioteca."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Giorno della spazzatura, l'autobus, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] .home-sec[data-sec="events"] > .section-label::after{ content:"blisko ciebie."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Wydarzenia, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"biblioteka."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Dzień wywozu śmieci, autobus, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] .home-sec[data-sec="events"] > .section-label::after{ content:"рядом с вами."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"События, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"библиотека."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"День вывоза мусора, автобус, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] .home-sec[data-sec="events"] > .section-label::after{ content:"κοντά σας."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Εκδηλώσεις, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"η βιβλιοθήκη."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Ημέρα σκουπιδιών, το λεωφορείο, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] .home-sec[data-sec="events"] > .section-label::after{ content:"gần bạn."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Sự kiện, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"thư viện."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"Ngày đổ rác, xe buýt, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] .home-sec[data-sec="events"] > .section-label::after{ content:"بالقرب منك."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"الفعاليات، "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"المكتبة."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"يوم النفايات، الحافلة، "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] .home-sec[data-sec="events"] > .section-label::after{ content:"在您身边。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"活动，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"图书馆。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"垃圾日、巴士、"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] .home-sec[data-sec="events"] > .section-label::after{ content:"在您身邊。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"活動，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"圖書館。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"垃圾日、巴士、"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] .home-sec[data-sec="events"] > .section-label::after{ content:"가까운 곳에서."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"행사, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"도서관."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"쓰레기 수거일, 버스, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] .home-sec[data-sec="events"] > .section-label::after{ content:"នៅជិតអ្នក។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"ព្រឹត្តិការណ៍ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"បណ្ណាល័យ។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"ថ្ងៃសម្រាម ឡានក្រុង "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] .home-sec[data-sec="events"] > .section-label::after{ content:"ຢູ່ໃກ້ທ່ານ."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"ກິດຈະກຳ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"ຫໍສະໝຸດ."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"ມື້ຂີ້ເຫຍື້ອ ລົດເມ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] .home-sec[data-sec="events"] > .section-label::after{ content:"আপনার কাছেই।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"অনুষ্ঠান, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  /* yo: CONNECT 2026-09-29, see the block above. Second half is a DRAFT pending native review. */
  html[lang="yo"] .home-sec[data-sec="events"] > .section-label::after{ content:"súnmọ̀ yín."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="yo"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Ìṣẹ̀lẹ̀, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  /* kea: CONNECT 2026-09-29, see the block above. Second half is a DRAFT pending native review. */
  html[lang="kea"] .home-sec[data-sec="events"] > .section-label::after{ content:"pértu di bo."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="kea"] .home-sec[data-sec="events"] > .section-label::after::marker{ content:"Evéntu, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:"লাইব্রেরি।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] .home-sec[data-sec="quicklinks"] > .section-label::after::marker{ content:"আবর্জনা দিবস, বাস, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
}

/* WORDS 2026-09-23, the six search boxes keep matching. SEARCHPERFECT 2026-09-02 ruled that Help,
   Events, Info and News share one row, and Home's DIV is the stand-in for the same box. The DIV now
   ends an over-long line in an ellipsis; a real <input> otherwise ends it in a hard cut mid-letter.
   This gives the five real ones the same ending. It changes nothing for a line that fits, and it
   cannot touch a typed value, only the placeholder, because a focused input scrolls instead.
   Undo: delete this one rule. */
.res-search input[type="search"]{ text-overflow:ellipsis }
/* ==== END WORDS 2026-09-23 ==== */


/* ==================================================================================================
   LANGAUDIT 2026-09-24. HOME'S FIRST BAND SENTENCE, IN ALL SIXTEEN LANGUAGES.
   `[RYAN 2026-09-24]` "TO CHECK EVERY LANGUAGE EVERY TRANSLATION MAKE SURE NOTHING SHOWS UP IN
   ENGLISH WHEN IT SHOULD HAVE TRANSLATED"

   LAYOUT2 2026-09-24 wrote `bandNewsHome` ("City notices, and good news.") earlier tonight, after
   `[RYAN 2026-09-24]` "why say what changed this week??? that is oddd", and it shipped ENGLISH ONLY
   behind LANGGATE 2026-09-20 with its own block naming `bandNewsHome` as the key a translation pass
   would need. This is that pass. Home's band 01 now has its sentence on all sixteen, the way bands
   02 and 03 got theirs in WORDS 2026-09-23 above, and Home stops being two finished openers and one
   blank for every reader who is not reading English.

   IT DOES NOTHING THAT WORDS 2026-09-23 DID NOT ALREADY DO, AND IT IS BUILT FROM THAT BLOCK.
   Same three restorations, because LANGGATE Part 2 takes three things away: `content` on the
   ::after, `content` on its ::marker, and `display`. Same specificity tie by construction, so
   source order decides and nothing needed `!important`: this block sits after WORDS, which sits
   after LANGGATE Part 2. Same `html[lang="..."]` matching of the seventeen values app.js:227
   writes, so "zh" and "zh-Hant" are told apart and neither leaks into the other. Outside @supports
   the whole sentence prints roman; inside @supports it is cut at the same comma into the guide's
   roman-then-italic shape. LANGGATE IS NOT TOUCHED and still blanks the five sentences that are
   still English only.

   ARABIC GETS `direction:rtl` BACK, for the same reason and by the same rule WORDS gave
   bandEvents and bandQuickLinks. The shared rule near the top of the VOICE block sets
   `direction:ltr; unicode-bidi:isolate` on all seven sentence hooks because they "are always
   English and always Latin". This one no longer is, and left alone the Arabic full stop lands at
   the wrong end of the line.

   THE SENTENCE SAYS THE SAME THING THE ENGLISH SAYS AND CLAIMS NOTHING MORE. "City notices" is the
   27 Service plus 7 Alert records in res_data_updates.js, "good news" is the 6 records literally
   categorised Good news. No timeframe is claimed in any of the sixteen, because the band does not
   guarantee one; that false weekly promise is the thing his own line above deleted.

   NO CHARACTER HERE IS GUESSED. Every codepoint in all forty-eight strings was run through
   _HARNESS/DATAFIX_CODEPOINT_2026-09-24.mjs BEFORE it was written, against the characters that
   language's own strings.<lang>.js already ships and therefore already paints in the subsetted
   font: GREEN on all sixteen, 0 unshipped codepoints. Nothing was added to any .woff2.
   MACHINE TRANSLATED, HONESTLY MARKED, NEEDS NATIVE REVIEW on all forty-eight values.
   `[RYAN 2026-08-07]` "essentially 99.99% machine translated with GENUINE I NEED HUMAN EYES ON IT
   identified - otherwise we help people but protect ourselves."
   NO CRISIS STRING IS IN THIS BLOCK. No value below contains a numeral of any script, so 911, 988
   and 211 cannot be reached, imitated or reordered from anything here (LAW L1).
   There is no em-dash in any of the forty-eight.
   UNDO: delete this whole block. LANGGATE goes back to blanking band 01's sentence and Home reads
   exactly as it did before this pass. Backup:
   NEVER_OVERWRITE_BACKUPS/langaudit_2026-09-24/app.css.PRE_LANGAUDIT
   ================================================================================================== */
html[lang="es"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Avisos de la ciudad, y buenas noticias."; display:block }
html[lang="pt"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Avisos da cidade, e boas notícias."; display:block }
html[lang="fr"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Avis de la ville, et de bonnes nouvelles."; display:block }
html[lang="ht"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Avi lavil la, ak bon nouvèl."; display:block }
html[lang="it"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Avvisi della città, e buone notizie."; display:block }
html[lang="pl"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Ogłoszenia miasta, i dobre wiadomości."; display:block }
html[lang="ru"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Городские объявления, и хорошие новости."; display:block }
html[lang="el"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Ανακοινώσεις της πόλης, και καλά νέα."; display:block }
html[lang="vi"] .home-sec[data-sec="updates"] > .section-label::after{ content:"Thông báo của thành phố, và tin vui."; display:block }
html[lang="ar"] .home-sec[data-sec="updates"] > .section-label::after{ content:"إشعارات المدينة، وأخبار طيبة."; display:block }
html[lang="zh"] .home-sec[data-sec="updates"] > .section-label::after{ content:"市政通知，以及好消息。"; display:block }
html[lang="zh-Hant"] .home-sec[data-sec="updates"] > .section-label::after{ content:"市政通知，以及好消息。"; display:block }
html[lang="ko"] .home-sec[data-sec="updates"] > .section-label::after{ content:"시 공지, 그리고 좋은 소식."; display:block }
html[lang="km"] .home-sec[data-sec="updates"] > .section-label::after{ content:"សេចក្តីជូនដំណឹងរបស់ទីក្រុង និងដំណឹងល្អ។"; display:block }
html[lang="lo"] .home-sec[data-sec="updates"] > .section-label::after{ content:"ແຈ້ງການຂອງເມືອງ ແລະ ຂ່າວດີ."; display:block }
html[lang="bn"] .home-sec[data-sec="updates"] > .section-label::after{ content:"শহরের বিজ্ঞপ্তি, এবং ভালো খবর।"; display:block }

/* Arabic only: give this one back its own direction. See the block header. */
html[lang="ar"] .home-sec[data-sec="updates"] > .section-label::after{ direction:rtl; unicode-bidi:isolate }

/* the guide's shape: roman, comma, italic, terminal stop. Same mechanism as the English above. */
@supports selector(::after::marker){
  html[lang="es"] .home-sec[data-sec="updates"] > .section-label::after{ content:"y buenas noticias."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Avisos de la ciudad, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] .home-sec[data-sec="updates"] > .section-label::after{ content:"e boas notícias."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Avisos da cidade, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] .home-sec[data-sec="updates"] > .section-label::after{ content:"et de bonnes nouvelles."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Avis de la ville, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] .home-sec[data-sec="updates"] > .section-label::after{ content:"ak bon nouvèl."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Avi lavil la, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] .home-sec[data-sec="updates"] > .section-label::after{ content:"e buone notizie."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Avvisi della città, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] .home-sec[data-sec="updates"] > .section-label::after{ content:"i dobre wiadomości."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Ogłoszenia miasta, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] .home-sec[data-sec="updates"] > .section-label::after{ content:"и хорошие новости."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Городские объявления, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] .home-sec[data-sec="updates"] > .section-label::after{ content:"και καλά νέα."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Ανακοινώσεις της πόλης, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] .home-sec[data-sec="updates"] > .section-label::after{ content:"và tin vui."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"Thông báo của thành phố, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] .home-sec[data-sec="updates"] > .section-label::after{ content:"وأخبار طيبة."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"إشعارات المدينة، "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] .home-sec[data-sec="updates"] > .section-label::after{ content:"以及好消息。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"市政通知，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] .home-sec[data-sec="updates"] > .section-label::after{ content:"以及好消息。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"市政通知，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] .home-sec[data-sec="updates"] > .section-label::after{ content:"그리고 좋은 소식."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"시 공지, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] .home-sec[data-sec="updates"] > .section-label::after{ content:"និងដំណឹងល្អ។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"សេចក្តីជូនដំណឹងរបស់ទីក្រុង "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] .home-sec[data-sec="updates"] > .section-label::after{ content:"ແລະ ຂ່າວດີ."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"ແຈ້ງການຂອງເມືອງ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] .home-sec[data-sec="updates"] > .section-label::after{ content:"এবং ভালো খবর।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] .home-sec[data-sec="updates"] > .section-label::after::marker{ content:"শহরের বিজ্ঞপ্তি, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
}
/* ==== END LANGAUDIT 2026-09-24 ==== */

/* ==================================================================================================
   INFOPAGE 2026-09-25. INFO STOPS LOOKING LIKE A DIFFERENT APP.
   `[RYAN 2026-09-25]` "look a tthe info page especially that is different"

   He is right. All five tabs were rendered in a real headless Chromium at 390x844, English, light,
   Normal, and read side by side before a byte was written. Three differences were real. Two of them
   are fixed here; the third (the City of Woonsocket card's colour) was JUDGED AND DELIBERATELY LEFT,
   and the reasoning is written out below so nobody re-opens it.

   1. THE OPENER. Home, Events and News each open with the guide's three-part block: a 20x1 Community
      Gold hairline, a Barlow Condensed 12px tracked uppercase word, and a Cormorant 28px sentence
      with an italic tail. INFO HAD ONLY THE SENTENCE, with nothing above it. Help has none of the
      three, ON PURPOSE (PHONEFIT 2026-09-24, his own words, because Help's heading repeated its own
      placeholder). Info's sentence repeats nothing, so Info was simply two thirds short. The word
      and the element are built in info_page.js from the already-translated `navBeloved` key, and the
      element is given app.css's own `.section-label` so the hairline and the type come from line 179
      rather than from a copy. ONLY THE SPACING IS HERE.

   2. THE CITY OF WOONSOCKET CARD'S DISCLOSURE MARKER. It was a small SOLID BLACK TRIANGLE ON THE
      LEFT, and that triangle is not a design decision at all: it is the browser's default
      `list-style: disclosure-closed` on a <summary>. Measured with CDP getMatchedStylesForNode:
      NO stylesheet in this app sets `list-style` on that element, so the UA rule was painting
      unopposed. Every other expander in the app - the seven city doors right above it on this same
      screen (`.csv-accv`), and every door on Help (`.hp-chev`) - draws a 8x8 two-sided caret at the
      RIGHT end of the row that rotates when it opens. So the one control on Info that came from the
      browser instead of from the app was pointing the wrong way, in the wrong place, in the wrong
      shape. It now draws the app's own caret, in Warm Cream on the navy, at the inline end, with the
      identical 8px box, 2px stroke, 45deg/-135deg rotation and .16s transition as `.csv-accv` in
      index.html:1574. NO GEOMETRY IS INVENTED: those four numbers are copied from that rule.

   3. JUDGED AND LEFT: THE CARD IS STILL DEEP NAVY, AND THAT IS CORRECT.
      The brief asked which of the two idioms is wrong, the navy block or the white door. Neither.
      They are two different things that only look like one thing. The seven white doors are a LIST:
      same shape, a count badge, a chevron, one of seven. The navy card is a FEATURE, and it is the
      brand guide's own MISSION-CARD idiom, read off the guide in BRAND_KIT_2026-09-23.md section 4:
      Deep Navy #102F3D ground, Community Gold eyebrow in tracked caps, Warm Cream serif body. It was
      built that way on 2026-09-22 on purpose (index.html:1832) and it is the one place the app
      co-brands with the City of Woonsocket, carrying Margaux Morisseau's own supplied seal. Making
      it white would delete the co-branding to win a consistency argument the guide does not make.
      What was wrong was the browser's triangle sitting on it, and that is what changed.
      THE SEAL IS NOT TOUCHED: no rule below names .csv-gov-seal or its img. Not resized, not
      recoloured, not cropped, not redrawn.

   CONTRAST. Nothing below sets a text colour. The caret is a border, not type, and it is
   --bw-cream #FAF6EC on --bw-navy #102F3D, the same pair the card's own heading already uses at
   9.02:1. The opener's word takes .section-label's shipped --bw-gold-ink, unchanged, and is
   re-swept below. NO RED. No font-size under 12px: the only size declared here is the 12px the
   .section-label already is, and it is not restated.

   UNDO, three lines: delete the two `#citysvc::before` / `#view-beloved > .section-label` spacing
   rules and the `list-style` rule, and the page is exactly what the backup holds.
   Backup: NEVER_OVERWRITE_BACKUPS/infopage_2026-09-25/app.css.PRE_INFOPAGE
   ================================================================================================== */

/* --- 1. the opener's spacing. Home's sentence is the label's OWN ::after, so the label's 8px
   margin-bottom falls below the sentence. Info's sentence is a different element (#citysvc::before),
   so that same 8px would have stacked on top of the sentence's 10px and opened a gap Home does not
   have. The label gives its margin up and the sentence keeps the identical 10px Home uses, so the
   hairline-to-word and word-to-sentence distances are the same number on both screens. --- */
#view-beloved > .section-label#info-opener{ margin-bottom:0 }
/* was margin:6px 0 14px with nothing above it. 10px is the shared value on line 4052, so Info's
   sentence now sits the same distance under its word as Home's, Events' and News' do. The 14px
   below it is unchanged: .res-search sits 2px under it and 14px gives the sentence the same air. */
#view-beloved #citysvc::before{ margin-top:10px }

/* --- 2. the City of Woonsocket card's marker. Specificity is (0,3,1), above index.html's own
   (0,2,1) block for this element, so it wins on weight and not on load order. --- */
#citysvc details#csv-gov > summary{
  list-style:none;                 /* kills the UA disclosure-closed triangle */
  position:relative;               /* the caret is positioned against the summary row */
  padding-inline-end:22px;         /* the 22px .csv-gov-row was already reserving, now really used */
}
#citysvc details#csv-gov > summary::-webkit-details-marker{ display:none }
#citysvc details#csv-gov > summary::after{
  content:"";
  position:absolute;
  top:50%;
  inset-inline-end:2px;
  width:8px;
  height:8px;
  border-right:2px solid var(--bw-cream);
  border-bottom:2px solid var(--bw-cream);
  transform:translateY(-65%) rotate(45deg);
  transition:transform .16s;
  pointer-events:none;
}
#citysvc details#csv-gov[open] > summary::after{ transform:translateY(-35%) rotate(-135deg) }
@media (prefers-reduced-motion: reduce){
  #citysvc details#csv-gov > summary::after{ transition:none }
}
/* Windows High Contrast: a border-drawn caret is forced to the system border colour, which is what
   the card's own forced-colors rule at index.html:1790 already relies on for its outline. */
@media (forced-colors: active){
  #citysvc details#csv-gov > summary::after{ border-right-color:CanvasText; border-bottom-color:CanvasText }
}
/* --- 3. THE SEARCH BOX. INFO'S WAS THE ONLY ONE THAT WAS NOT 48px.
   MEASURED at 390x844, the outer .res-search box of all five search controls in the app:
       Home      358 x 48.8      Help  358 x 48.8
       Events    358 x 48.8      News  358 x 48.8
       INFO      358 x 56.0
   PHONEFIT 2026-09-24 brought the phone search control down to 48px and its own comment says it
   "applies to all four search boxes (#res-q, #csv-input, #ev-q, #news-q) because they are one
   control". THAT CLAIM WAS FALSE FOR #csv-input AND HAD BEEN SINCE THE DAY IT WAS WRITTEN. Its
   rule is `.res-search input` (0,1,1) inside @media (max-width:430px), and index.html:1455 carries
   `#citysvc #csv-input{ ... min-height:56px ... }` at (0,2,0). A media query adds no specificity,
   so the ID rule won and Info kept the 56px the other four gave up. Confirmed with CDP
   getMatchedStylesForNode, not inferred: both rules match the node and the 56px one is the winner.
   `[RYAN 2026-09-24]` "search bar on home looks big and then the help page search bar it takes you
   to has text above it" was about the SAME control changing between screens. It still changed
   between screens; it just changed on the screen nobody re-measured.
   This restates PHONEFIT'S OWN VALUE at (0,3,0) so it wins on weight rather than on load order, in
   PHONEFIT's own media query, and it changes nothing above 430px, where the desktop preview frame
   and every harness keep the 56px they were measured at. font-size is NOT touched: it stays the
   16px that stops iOS zooming the page on focus, and it is nowhere near the 12px gate floor. The
   control stays 8px clear of LAW L5's 44px floor.
   THE ONE-LINE FIX BELONGS IN index.html AND IS HANDED BACK, NOT TAKEN: delete `min-height:56px`
   from index.html:1455 and app.css's own media rule governs all five with no override at all.
   That file is another agent's this session. --- */
/* RETIRED 2026-09-25 (HANDBACK, JOB 2). THIS RULE EXISTED ONLY TO FIGHT index.html:1455 AND THAT
   LINE IS GONE. Its own note above said so: "THE ONE-LINE FIX BELONGS IN index.html AND IS HANDED
   BACK, NOT TAKEN". `min-height:56px` and `padding:13px 15px` have now been deleted from that
   inline <style> rule, so the (0,2,0) selector this block was written at (0,3,0) to out-weigh no
   longer declares either property, and app.css's own PHONEFIT rule `.res-search input{min-height:48px}`
   plus app.css:836's `padding:12px 14px` reach #csv-input on their own. One control, one rule, no
   override. MEASURED AFTER with this block commented out, real headless Chromium at 390x844:
   Home 358x48.8, Help 358x48.8, Events 358x48.8, News 358x48.8, INFO 358x48.8.
   Kept on disk, never loaded, per LAW L3. UNDO: uncomment. */
/* @media (max-width: 430px){
  #citysvc #csv-search #csv-input{ min-height:48px; padding:12px 14px }
} */

/* --- 4. THE MAYOR'S PHOTOGRAPH. IT WAS THE TALLEST OBJECT IN THE APP BY 244px.
   MEASURED: the photo box was 358 x 340 (aspect-ratio 1/1 capped at max-height:340px, app.css:892)
   and the card it sits in was 475.4 tall. The next tallest thing on Info is a 96.5px card. Nothing
   anywhere else in the app is over 100px except a list of stacked cards. The source file, mayor.jpg,
   is 460 x 460, and `background-size:cover` on a square box therefore showed all of it: the mayor,
   and then roughly the bottom third of the frame, which is an empty grey tabletop.
   FOUR HEIGHTS WERE RENDERED AND LOOKED AT, not calculated: 1/1 at 340 (as shipped), 4/3 at 268,
   3/2 at 239 and 16/10 at 224. 3/2 and 16/10 both cut through his hands, which are the only gesture
   in the picture. 4/3 keeps the whole person, the hands, and the headroom above him, and drops only
   the empty table. Card 475.4 -> 403.4, so the role, the name and the Call and Email buttons now
   land in the same glance as the face instead of below the fold under it.
   `[RYAN 2026-08-01 DECIDED.md:127]` "the info page should include the picture of the mayor" is
   honoured: the picture is still there, still the full width of the card, still his whole face.
   background-position IS DELIBERATELY NOT TOUCHED. It stays `center top`, so the crop can only ever
   come off the BOTTOM of a portrait and a future photo of a future mayor can never lose the top of a
   head to this rule. A `50% 12%` variant was rendered and rejected for exactly that reason.
   NOT THE SEAL AND NOT THE MARK. This rule names .mayor-photo only. The City of Woonsocket seal
   (.csv-gov-seal img) and the Beloved Woonsocket mark are not named anywhere in this block and are
   not resized, recoloured, cropped or redrawn. LAW L6 / rule 6.
   .mayor-photo-none, the initial-letter block drawn when a town has no photo, is NOT touched: it is
   already 1/1 capped at 200px, shorter than this.
   The 1024+ rule at app.css:2120 caps .mayor-photo at max-width:340px, which with 4/3 computes to
   255px tall, under the 268 cap, so desktop is unchanged by arithmetic rather than by a second rule.
   UNDO, one line: aspect-ratio:1/1 and max-height:340px. --- */
.mayor-photo{ aspect-ratio:4/3; max-height:268px }
/* ==== END INFOPAGE 2026-09-25 ==== */


/* ================================================================================================
   HANDBACK 2026-09-25, JOB 3. INFO'S IN-CARD HEADINGS NOW HAVE A CSS HOME.

   renderAbout() in app.js wrote `style="color:var(--navy);font-size:17px;margin-bottom:8px"` onto
   every h3 it built, and `style="color:var(--navy);margin-bottom:12px"` onto its h2. An inline
   style attribute outranks every selector in every stylesheet, so no rule in app.css, brand.css or
   any dated .css could reach these headings at all without `!important`, and Info's in-card
   headings could not be brought onto the type scale from a CSS file by anyone. INFOPAGE 2026-09-25
   named this and could not take it, because app.js was another agent's file that session.
   The declarations are MOVED, NOT CHANGED. Every computed value is the value the inline attribute
   produced, so this is a no-op on screen and a change in who owns the thing:
       .ab-h2   color:var(--navy); margin-bottom:12px
       .ab-h3   color:var(--navy); font-size:17px; margin-bottom:8px
       .ab-h3-wide   margin-bottom:10px   (the Get involved card, which had 10 not 8)
       .ab-h3-tight  margin-bottom:4px    (the officials card title, which had 4)
   MEASURED BEFORE AND AFTER, real headless Chromium at 390x844, Korean, every h2/h3 inside
   #beloved-about: font-size, font-weight, line-height, color, margin-top and margin-bottom all
   byte-identical. 17px and 24px are both far above the 12px gate floor; no third sub-12px
   declaration is added anywhere. Nothing is deleted: app.js keeps the same tags, the same order and
   the same text, it just names a class instead of carrying a style attribute.
   UNDO: put the four style attributes back in app.js renderAbout()/renderSituationTable()/
   renderOfficialsHTML() and delete this block. ================================================== */
.ab-h2{color:var(--navy);margin-bottom:12px}
.ab-h3{color:var(--navy);font-size:17px;margin-bottom:8px}
.ab-h3.ab-h3-wide{margin-bottom:10px}
.ab-h3.ab-h3-tight{margin-bottom:4px}


/* ==================================================================================================
   CONSIDERED 2026-09-25. THE LAST FIVE SERIF SENTENCES, IN ALL SIXTEEN LANGUAGES.
   `[RYAN 2026-09-24]` "TO CHECK EVERY LANGUAGE EVERY TRANSLATION MAKE SURE NOTHING SHOWS UP IN
   ENGLISH WHEN IT SHOULD HAVE TRANSLATED"
   `[RYAN 2026-09-25]` "why are these not like thought out"

   WHAT WAS THERE. VOICE 2026-09-20 wrote seven serif sentences, one per screen, out of
   BRANDSPEC_2026-09-17.md. LANGGATE 2026-09-20 then blanked every one of them for any reader who is
   not reading English, which is the right resting place for an untranslated sentence and the wrong
   one for a finished app. WORDS 2026-09-23 translated two of them (bandEvents, bandQuickLinks) and
   LANGAUDIT 2026-09-24 translated a third (bandNewsHome). THESE ARE THE OTHER FIVE. ENUMERATED OFF
   THE FILE, NOT OFF A LIST IN A BRIEF, because the brief's list was already one sentence out of date:

     hook                                                  English today            state before this block
     .home-sec[data-sec="updates"] .section-label::after   City notices, and good news.   DONE, LANGAUDIT 09-24
     .home-sec[data-sec="events"] .section-label::after    Meetings and shows, near you.  DONE, WORDS 09-23
     .home-sec[data-sec="quicklinks"] .section-label::after Trash day, the bus, the library. DONE, WORDS 09-23
     #view-events > h2[data-bwn]::after                    What's on, and where.          ENGLISH ONLY
     #view-updates > h2[data-bwn]::after                   What's going on in Woonsocket. ENGLISH ONLY
     #view-settings > h2[data-bwn]::after                  Make it yours.                 ENGLISH ONLY
     #citysvc::before                                      The city, how to reach it.     ENGLISH ONLY
     #view-community .res-search-head::after               What do you need? Type it here. ENGLISH ONLY

   NOTE ON THE NEWS SENTENCE. "What changed this week." is NOT on the wall any more: LAYOUT2
   2026-09-24 moved it to Home as "City notices, and good news." on `[RYAN 2026-09-24]` "why say what
   changed this week??? that is oddd", and REPORT 2026-09-24 replaced the News screen's own sentence
   with "What's going on in Woonsocket." The dead string is still visible at app.css:4203 and 4415
   under a superseded-but-kept note, and it is NOT what renders. This block translates what renders.
   FIVE SENTENCES x SIXTEEN LANGUAGES = EIGHTY STRINGS. Zero remain English-only after this.

   IT DOES NOT TOUCH LANGGATE. Part 1 and Part 2 are byte-identical. This block sits after Part 2 and
   after WORDS and LANGAUDIT, for the reason those two blocks already give in full: a rule that
   RESTORES content for one named language has to come after the rule that blanks it, `@supports`
   adds no specificity, and `html[lang="xx"]` ties `html:not([lang|="en"])` exactly by construction,
   so source order decides and nothing here needed `!important`. `html[lang="..."]` matches exactly
   the seventeen values app.js:227 writes, so "zh" and "zh-Hant" are told apart and neither leaks.

   THE HELP SENTENCE NEEDED ONE EXTRA THING THE OTHER FOUR DID NOT. Its base declaration at
   app.css:4343 is scoped `html[lang="en"]`, so the other fifteen have no font, no size and no
   display to restore, only a content to add. The shared property block below re-states that
   declaration for the sixteen, copied from the English one property for property, so Help's sentence
   is the same 20px serif on every screen and cannot drift from the English.

   ARABIC GETS `direction:rtl` BACK ON ALL FIVE, the way WORDS and LANGAUDIT gave it back to their
   three. The shared VOICE rule at app.css:4034 sets `direction:ltr; unicode-bidi:isolate` on these
   hooks and its own comment says why: the sentences "are always English and always Latin". Five more
   of them no longer are, and left alone an Arabic sentence is laid out as an isolated LTR run and
   its full stop lands at the wrong end of the line. The other fifteen are LTR and are left untouched.

   NO CHARACTER HERE IS GUESSED, AND NO .woff2 WAS ADDED, REMOVED OR RE-SUBSETTED.
   TWO AUTHORITIES, BOTH RUN BEFORE ONE BYTE WAS WRITTEN:
     1. _HARNESS/DATAFIX_CODEPOINT_2026-09-24.mjs, --selftest PASSED both directions first, then all
        240 candidate strings: GREEN 16/16 languages, 0 unshipped characters. Every character in this
        block is one that language's own strings file already paints on screen today.
     2. fontTools against the real .woff2 cmaps in app_fonts/, the union of the five base
        Latin / Latin-ext / Cyrillic / Greek / Vietnamese Source Sans Pro faces plus that language's
        own Noto subset, AT BOTH 400 AND 700: 0 missing, 80/80 sentences.
   SEVENTEEN CHARACTERS ARE OUTSIDE THE CORMORANT GARAMOND *LATIN* SUBSET and are named here rather
   than hidden: Polish e-ogonek and c-acute, and thirteen Vietnamese precomposed vowels. They are NOT
   tofu. Cormorant ships only its latin subset on this wall, so those characters resolve by ordinary
   per-character fallback to Source Sans Pro latin-ext / vietnamese, which carry them at 400 and 700.
   THAT IS NOT NEW AND IT IS NOT MINE: the same characters are already rendering in the same
   pseudo-elements on Home today, shipped by WORDS 2026-09-23 ("Zebrania i wystepy", "Cuoc hop va
   buoi dien") and LANGAUDIT 2026-09-24, both of which ran this same check and shipped GREEN. The
   italic half of those two languages is a synthesised oblique for the same reason, and has been
   since 2026-09-23. Nothing about that changes tonight; it is written down so the next pass does not
   rediscover it as a bug.

   THE WORDS THEMSELVES CLAIM NOTHING THE ENGLISH DOES NOT. No timeframe is promised anywhere,
   because none of these five screens guarantees one. "Woonsocket" is left in Latin in every
   language, which is the convention every strings file on this wall already uses for the town name.
   NO CRISIS STRING IS IN THIS BLOCK. Not one of the eighty values contains a numeral of any script,
   so 911, 988 and 211 cannot be reached, imitated or reordered from anything written below (LAW L1).
   There is no em-dash in any of the eighty.

   MACHINE TRANSLATED, HONESTLY MARKED, NEEDS NATIVE REVIEW on all eighty values.
   `[RYAN 2026-08-07]` "essentially 99.99% machine translated with GENUINE I NEED HUMAN EYES ON IT
   identified - otherwise we help people but protect ourselves." Registered in
   window.MT_LANG_2026_08_27 under the "considered:" namespace at the foot of each strings file.

   UNDO: delete this whole block. LANGGATE goes back to blanking all five sentences and the four
   screens read exactly as they did this morning.
   Backup: NEVER_OVERWRITE_BACKUPS/considered_2026-09-25/app.css.PRE_CONSIDERED
   ================================================================================================== */

/* ---- Help's sentence has no base declaration outside English. This is app.css:4343, property for
   property, given to the sixteen. ---- */
html[lang="es"] #view-community > .res-search .res-search-head::after,
html[lang="pt"] #view-community > .res-search .res-search-head::after,
html[lang="fr"] #view-community > .res-search .res-search-head::after,
html[lang="ht"] #view-community > .res-search .res-search-head::after,
html[lang="it"] #view-community > .res-search .res-search-head::after,
html[lang="pl"] #view-community > .res-search .res-search-head::after,
html[lang="ru"] #view-community > .res-search .res-search-head::after,
html[lang="el"] #view-community > .res-search .res-search-head::after,
html[lang="vi"] #view-community > .res-search .res-search-head::after,
html[lang="ar"] #view-community > .res-search .res-search-head::after,
html[lang="zh"] #view-community > .res-search .res-search-head::after,
html[lang="zh-Hant"] #view-community > .res-search .res-search-head::after,
html[lang="ko"] #view-community > .res-search .res-search-head::after,
html[lang="km"] #view-community > .res-search .res-search-head::after,
html[lang="lo"] #view-community > .res-search .res-search-head::after,
html[lang="bn"] #view-community > .res-search .res-search-head::after{
  display:block;
  flex:1 1 auto;
  min-width:0;
  font-family:var(--font-serif);
  font-style:normal;
  font-weight:500;
  font-size:20px;
  line-height:1.3;
  letter-spacing:0;
  text-transform:none;
  color:inherit;
  direction:ltr;
  unicode-bidi:isolate;
}

/* ---- es ---- */
html[lang="es"] #view-events > h2[data-bwn]::after{ content:"Qué hay, y dónde."; display:block }
html[lang="es"] #view-updates > h2[data-bwn]::after{ content:"Qué pasa en Woonsocket."; display:block }
html[lang="es"] #view-settings > h2[data-bwn]::after{ content:"Hágalo suyo."; display:block }
html[lang="es"] #citysvc::before{ content:"La ciudad, cómo contactarla."; display:block }
html[lang="es"] #view-community > .res-search .res-search-head::after{ content:"¿Qué necesita? Escríbalo aquí."; display:block }

/* ---- pt ---- */
html[lang="pt"] #view-events > h2[data-bwn]::after{ content:"O que há, e onde."; display:block }
html[lang="pt"] #view-updates > h2[data-bwn]::after{ content:"O que se passa em Woonsocket."; display:block }
html[lang="pt"] #view-settings > h2[data-bwn]::after{ content:"Torne-o seu."; display:block }
html[lang="pt"] #citysvc::before{ content:"A cidade, como contactá-la."; display:block }
html[lang="pt"] #view-community > .res-search .res-search-head::after{ content:"Do que precisa? Escreva aqui."; display:block }

/* ---- fr ---- */
html[lang="fr"] #view-events > h2[data-bwn]::after{ content:"Ce qui se passe, et où."; display:block }
html[lang="fr"] #view-updates > h2[data-bwn]::after{ content:"Ce qui se passe à Woonsocket."; display:block }
html[lang="fr"] #view-settings > h2[data-bwn]::after{ content:"Faites-en le vôtre."; display:block }
html[lang="fr"] #citysvc::before{ content:"La ville, comment la joindre."; display:block }
html[lang="fr"] #view-community > .res-search .res-search-head::after{ content:"De quoi avez-vous besoin ? Écrivez-le ici."; display:block }

/* ---- ht ---- */
html[lang="ht"] #view-events > h2[data-bwn]::after{ content:"Sa k ap pase, e kote."; display:block }
html[lang="ht"] #view-updates > h2[data-bwn]::after{ content:"Sa k ap pase nan Woonsocket."; display:block }
html[lang="ht"] #view-settings > h2[data-bwn]::after{ content:"Fè l pa w."; display:block }
html[lang="ht"] #citysvc::before{ content:"Vil la, kijan pou kontakte l."; display:block }
html[lang="ht"] #view-community > .res-search .res-search-head::after{ content:"Kisa ou bezwen? Ekri l isit la."; display:block }

/* ---- it ---- */
html[lang="it"] #view-events > h2[data-bwn]::after{ content:"Cosa c'è, e dove."; display:block }
html[lang="it"] #view-updates > h2[data-bwn]::after{ content:"Cosa succede a Woonsocket."; display:block }
html[lang="it"] #view-settings > h2[data-bwn]::after{ content:"Rendilo tuo."; display:block }
html[lang="it"] #citysvc::before{ content:"La città, come contattarla."; display:block }
html[lang="it"] #view-community > .res-search .res-search-head::after{ content:"Di cosa hai bisogno? Scrivilo qui."; display:block }

/* ---- pl ---- */
html[lang="pl"] #view-events > h2[data-bwn]::after{ content:"Co się dzieje, i gdzie."; display:block }
html[lang="pl"] #view-updates > h2[data-bwn]::after{ content:"Co się dzieje w Woonsocket."; display:block }
html[lang="pl"] #view-settings > h2[data-bwn]::after{ content:"Ustaw po swojemu."; display:block }
html[lang="pl"] #citysvc::before{ content:"Miasto, jak się z nim skontaktować."; display:block }
html[lang="pl"] #view-community > .res-search .res-search-head::after{ content:"Czego potrzebujesz? Wpisz to tutaj."; display:block }

/* ---- ru ---- */
html[lang="ru"] #view-events > h2[data-bwn]::after{ content:"Что происходит, и где."; display:block }
html[lang="ru"] #view-updates > h2[data-bwn]::after{ content:"Что происходит в Woonsocket."; display:block }
html[lang="ru"] #view-settings > h2[data-bwn]::after{ content:"Настройте под себя."; display:block }
html[lang="ru"] #citysvc::before{ content:"Город, как с ним связаться."; display:block }
html[lang="ru"] #view-community > .res-search .res-search-head::after{ content:"Что вам нужно? Напишите здесь."; display:block }

/* ---- el ---- */
html[lang="el"] #view-events > h2[data-bwn]::after{ content:"Τι γίνεται, και πού."; display:block }
html[lang="el"] #view-updates > h2[data-bwn]::after{ content:"Τι συμβαίνει στο Woonsocket."; display:block }
html[lang="el"] #view-settings > h2[data-bwn]::after{ content:"Κάντε το δικό σας."; display:block }
html[lang="el"] #citysvc::before{ content:"Η πόλη, πώς να επικοινωνήσετε."; display:block }
html[lang="el"] #view-community > .res-search .res-search-head::after{ content:"Τι χρειάζεστε; Γράψτε το εδώ."; display:block }

/* ---- vi ---- */
html[lang="vi"] #view-events > h2[data-bwn]::after{ content:"Có gì, và ở đâu."; display:block }
html[lang="vi"] #view-updates > h2[data-bwn]::after{ content:"Chuyện gì đang diễn ra ở Woonsocket."; display:block }
html[lang="vi"] #view-settings > h2[data-bwn]::after{ content:"Làm cho nó thành của bạn."; display:block }
html[lang="vi"] #citysvc::before{ content:"Thành phố, cách liên hệ."; display:block }
html[lang="vi"] #view-community > .res-search .res-search-head::after{ content:"Bạn cần gì? Hãy gõ vào đây."; display:block }

/* ---- ar ---- */
html[lang="ar"] #view-events > h2[data-bwn]::after{ content:"ما الذي يجري، وأين."; display:block }
html[lang="ar"] #view-updates > h2[data-bwn]::after{ content:"ما يجري في Woonsocket."; display:block }
html[lang="ar"] #view-settings > h2[data-bwn]::after{ content:"اجعله لك."; display:block }
html[lang="ar"] #citysvc::before{ content:"المدينة، كيف تتواصل معها."; display:block }
html[lang="ar"] #view-community > .res-search .res-search-head::after{ content:"ماذا تحتاج؟ اكتبه هنا."; display:block }

/* ---- zh ---- */
html[lang="zh"] #view-events > h2[data-bwn]::after{ content:"有什么活动，在哪里。"; display:block }
html[lang="zh"] #view-updates > h2[data-bwn]::after{ content:"Woonsocket 正在发生的事。"; display:block }
html[lang="zh"] #view-settings > h2[data-bwn]::after{ content:"把它设成您的样子。"; display:block }
html[lang="zh"] #citysvc::before{ content:"这座城市，如何联系。"; display:block }
html[lang="zh"] #view-community > .res-search .res-search-head::after{ content:"您需要什么？在这里输入。"; display:block }

/* ---- zh-Hant ---- */
html[lang="zh-Hant"] #view-events > h2[data-bwn]::after{ content:"有什麼活動，在哪裡。"; display:block }
html[lang="zh-Hant"] #view-updates > h2[data-bwn]::after{ content:"Woonsocket 正在發生的事。"; display:block }
html[lang="zh-Hant"] #view-settings > h2[data-bwn]::after{ content:"把它設成您的樣子。"; display:block }
html[lang="zh-Hant"] #citysvc::before{ content:"這座城市，如何聯繫。"; display:block }
html[lang="zh-Hant"] #view-community > .res-search .res-search-head::after{ content:"您需要什麼？在這裡輸入。"; display:block }

/* ---- ko ---- */
html[lang="ko"] #view-events > h2[data-bwn]::after{ content:"무엇이 열리는지, 어디에서."; display:block }
html[lang="ko"] #view-updates > h2[data-bwn]::after{ content:"Woonsocket에서 일어나는 일."; display:block }
html[lang="ko"] #view-settings > h2[data-bwn]::after{ content:"당신에게 맞게 설정하세요."; display:block }
html[lang="ko"] #citysvc::before{ content:"이 도시, 연락하는 방법."; display:block }
html[lang="ko"] #view-community > .res-search .res-search-head::after{ content:"무엇이 필요하신가요? 여기에 입력하세요."; display:block }

/* ---- km ---- */
html[lang="km"] #view-events > h2[data-bwn]::after{ content:"មានអ្វី និងនៅឯណា។"; display:block }
html[lang="km"] #view-updates > h2[data-bwn]::after{ content:"អ្វីដែលកំពុងកើតឡើង នៅ Woonsocket។"; display:block }
html[lang="km"] #view-settings > h2[data-bwn]::after{ content:"កំណត់វា តាមចិត្តអ្នក។"; display:block }
html[lang="km"] #citysvc::before{ content:"ទីក្រុង របៀបទាក់ទង។"; display:block }
html[lang="km"] #view-community > .res-search .res-search-head::after{ content:"តើអ្នកត្រូវការអ្វី? សរសេរនៅទីនេះ។"; display:block }

/* ---- lo ---- */
html[lang="lo"] #view-events > h2[data-bwn]::after{ content:"ມີຫຍັງ ແລະ ຢູ່ໃສ."; display:block }
html[lang="lo"] #view-updates > h2[data-bwn]::after{ content:"ສິ່ງທີ່ກຳລັງເກີດຂຶ້ນ ຢູ່ Woonsocket."; display:block }
html[lang="lo"] #view-settings > h2[data-bwn]::after{ content:"ຕັ້ງມັນ ຕາມໃຈທ່ານ."; display:block }
html[lang="lo"] #citysvc::before{ content:"ເມືອງນີ້ ວິທີຕິດຕໍ່."; display:block }
html[lang="lo"] #view-community > .res-search .res-search-head::after{ content:"ທ່ານຕ້ອງການຫຍັງ? ພິມຢູ່ນີ້."; display:block }

/* ---- bn ---- */
html[lang="bn"] #view-events > h2[data-bwn]::after{ content:"কী হচ্ছে, আর কোথায়।"; display:block }
html[lang="bn"] #view-updates > h2[data-bwn]::after{ content:"Woonsocket-এ যা ঘটছে।"; display:block }
html[lang="bn"] #view-settings > h2[data-bwn]::after{ content:"নিজের মতো সাজিয়ে নিন।"; display:block }
html[lang="bn"] #citysvc::before{ content:"শহর, কীভাবে যোগাযোগ করবেন।"; display:block }
html[lang="bn"] #view-community > .res-search .res-search-head::after{ content:"আপনার কী প্রয়োজন? এখানে লিখুন।"; display:block }

/* Arabic only: give these five back their own direction. See the block header. */
html[lang="ar"] #view-events > h2[data-bwn]::after,
html[lang="ar"] #view-updates > h2[data-bwn]::after,
html[lang="ar"] #view-settings > h2[data-bwn]::after,
html[lang="ar"] #citysvc::before,
html[lang="ar"] #view-community > .res-search .res-search-head::after{ direction:rtl; unicode-bidi:isolate }

/* the guide's shape: roman, comma, italic, terminal stop. Same mechanism as the English above. */
@supports selector(::after::marker){
  html[lang="es"] #view-events > h2[data-bwn]::after{ content:"y dónde."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] #view-events > h2[data-bwn]::after::marker{ content:"Qué hay, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="es"] #view-updates > h2[data-bwn]::after{ content:"en Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] #view-updates > h2[data-bwn]::after::marker{ content:"Qué pasa "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="es"] #view-settings > h2[data-bwn]::after{ content:"suyo."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] #view-settings > h2[data-bwn]::after::marker{ content:"Hágalo "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="es"] #view-community > .res-search .res-search-head::after{ content:"Escríbalo aquí."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] #view-community > .res-search .res-search-head::after::marker{ content:"¿Qué necesita? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] #view-events > h2[data-bwn]::after{ content:"e onde."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] #view-events > h2[data-bwn]::after::marker{ content:"O que há, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] #view-updates > h2[data-bwn]::after{ content:"em Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] #view-updates > h2[data-bwn]::after::marker{ content:"O que se passa "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] #view-settings > h2[data-bwn]::after{ content:"seu."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] #view-settings > h2[data-bwn]::after::marker{ content:"Torne-o "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] #view-community > .res-search .res-search-head::after{ content:"Escreva aqui."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] #view-community > .res-search .res-search-head::after::marker{ content:"Do que precisa? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] #view-events > h2[data-bwn]::after{ content:"et où."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] #view-events > h2[data-bwn]::after::marker{ content:"Ce qui se passe, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] #view-updates > h2[data-bwn]::after{ content:"à Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] #view-updates > h2[data-bwn]::after::marker{ content:"Ce qui se passe "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] #view-settings > h2[data-bwn]::after{ content:"le vôtre."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] #view-settings > h2[data-bwn]::after::marker{ content:"Faites-en "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] #view-community > .res-search .res-search-head::after{ content:"Écrivez-le ici."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] #view-community > .res-search .res-search-head::after::marker{ content:"De quoi avez-vous besoin ? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] #view-events > h2[data-bwn]::after{ content:"e kote."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] #view-events > h2[data-bwn]::after::marker{ content:"Sa k ap pase, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] #view-updates > h2[data-bwn]::after{ content:"nan Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] #view-updates > h2[data-bwn]::after::marker{ content:"Sa k ap pase "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] #view-settings > h2[data-bwn]::after{ content:"pa w."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] #view-settings > h2[data-bwn]::after::marker{ content:"Fè l "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] #view-community > .res-search .res-search-head::after{ content:"Ekri l isit la."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] #view-community > .res-search .res-search-head::after::marker{ content:"Kisa ou bezwen? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] #view-events > h2[data-bwn]::after{ content:"e dove."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] #view-events > h2[data-bwn]::after::marker{ content:"Cosa c'è, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] #view-updates > h2[data-bwn]::after{ content:"a Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] #view-updates > h2[data-bwn]::after::marker{ content:"Cosa succede "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] #view-settings > h2[data-bwn]::after{ content:"tuo."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] #view-settings > h2[data-bwn]::after::marker{ content:"Rendilo "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] #view-community > .res-search .res-search-head::after{ content:"Scrivilo qui."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] #view-community > .res-search .res-search-head::after::marker{ content:"Di cosa hai bisogno? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] #view-events > h2[data-bwn]::after{ content:"i gdzie."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] #view-events > h2[data-bwn]::after::marker{ content:"Co się dzieje, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] #view-updates > h2[data-bwn]::after{ content:"w Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] #view-updates > h2[data-bwn]::after::marker{ content:"Co się dzieje "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] #view-settings > h2[data-bwn]::after{ content:"po swojemu."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] #view-settings > h2[data-bwn]::after::marker{ content:"Ustaw "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] #view-community > .res-search .res-search-head::after{ content:"Wpisz to tutaj."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] #view-community > .res-search .res-search-head::after::marker{ content:"Czego potrzebujesz? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] #view-events > h2[data-bwn]::after{ content:"и где."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] #view-events > h2[data-bwn]::after::marker{ content:"Что происходит, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] #view-updates > h2[data-bwn]::after{ content:"в Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] #view-updates > h2[data-bwn]::after::marker{ content:"Что происходит "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] #view-settings > h2[data-bwn]::after{ content:"под себя."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] #view-settings > h2[data-bwn]::after::marker{ content:"Настройте "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] #view-community > .res-search .res-search-head::after{ content:"Напишите здесь."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] #view-community > .res-search .res-search-head::after::marker{ content:"Что вам нужно? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] #view-events > h2[data-bwn]::after{ content:"και πού."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] #view-events > h2[data-bwn]::after::marker{ content:"Τι γίνεται, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] #view-updates > h2[data-bwn]::after{ content:"στο Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] #view-updates > h2[data-bwn]::after::marker{ content:"Τι συμβαίνει "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] #view-settings > h2[data-bwn]::after{ content:"δικό σας."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] #view-settings > h2[data-bwn]::after::marker{ content:"Κάντε το "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] #view-community > .res-search .res-search-head::after{ content:"Γράψτε το εδώ."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] #view-community > .res-search .res-search-head::after::marker{ content:"Τι χρειάζεστε; "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] #view-events > h2[data-bwn]::after{ content:"và ở đâu."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] #view-events > h2[data-bwn]::after::marker{ content:"Có gì, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] #view-updates > h2[data-bwn]::after{ content:"ở Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] #view-updates > h2[data-bwn]::after::marker{ content:"Chuyện gì đang diễn ra "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] #view-settings > h2[data-bwn]::after{ content:"thành của bạn."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] #view-settings > h2[data-bwn]::after::marker{ content:"Làm cho nó "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] #view-community > .res-search .res-search-head::after{ content:"Hãy gõ vào đây."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] #view-community > .res-search .res-search-head::after::marker{ content:"Bạn cần gì? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] #view-events > h2[data-bwn]::after{ content:"وأين."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] #view-events > h2[data-bwn]::after::marker{ content:"ما الذي يجري، "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] #view-updates > h2[data-bwn]::after{ content:"في Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] #view-updates > h2[data-bwn]::after::marker{ content:"ما يجري "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] #view-settings > h2[data-bwn]::after{ content:"لك."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] #view-settings > h2[data-bwn]::after::marker{ content:"اجعله "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] #view-community > .res-search .res-search-head::after{ content:"اكتبه هنا."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] #view-community > .res-search .res-search-head::after::marker{ content:"ماذا تحتاج؟ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] #view-events > h2[data-bwn]::after{ content:"在哪里。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] #view-events > h2[data-bwn]::after::marker{ content:"有什么活动，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] #view-updates > h2[data-bwn]::after{ content:"正在发生的事。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] #view-updates > h2[data-bwn]::after::marker{ content:"Woonsocket "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] #view-settings > h2[data-bwn]::after{ content:"您的样子。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] #view-settings > h2[data-bwn]::after::marker{ content:"把它设成"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] #view-community > .res-search .res-search-head::after{ content:"在这里输入。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] #view-community > .res-search .res-search-head::after::marker{ content:"您需要什么？"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] #view-events > h2[data-bwn]::after{ content:"在哪裡。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] #view-events > h2[data-bwn]::after::marker{ content:"有什麼活動，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] #view-updates > h2[data-bwn]::after{ content:"正在發生的事。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] #view-updates > h2[data-bwn]::after::marker{ content:"Woonsocket "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] #view-settings > h2[data-bwn]::after{ content:"您的樣子。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] #view-settings > h2[data-bwn]::after::marker{ content:"把它設成"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] #view-community > .res-search .res-search-head::after{ content:"在這裡輸入。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] #view-community > .res-search .res-search-head::after::marker{ content:"您需要什麼？"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] #view-events > h2[data-bwn]::after{ content:"어디에서."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] #view-events > h2[data-bwn]::after::marker{ content:"무엇이 열리는지, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] #view-updates > h2[data-bwn]::after{ content:"일어나는 일."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] #view-updates > h2[data-bwn]::after::marker{ content:"Woonsocket에서 "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] #view-settings > h2[data-bwn]::after{ content:"설정하세요."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] #view-settings > h2[data-bwn]::after::marker{ content:"당신에게 맞게 "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] #view-community > .res-search .res-search-head::after{ content:"여기에 입력하세요."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] #view-community > .res-search .res-search-head::after::marker{ content:"무엇이 필요하신가요? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] #view-events > h2[data-bwn]::after{ content:"និងនៅឯណា។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] #view-events > h2[data-bwn]::after::marker{ content:"មានអ្វី "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] #view-updates > h2[data-bwn]::after{ content:"នៅ Woonsocket។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] #view-updates > h2[data-bwn]::after::marker{ content:"អ្វីដែលកំពុងកើតឡើង "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] #view-settings > h2[data-bwn]::after{ content:"តាមចិត្តអ្នក។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] #view-settings > h2[data-bwn]::after::marker{ content:"កំណត់វា "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] #view-community > .res-search .res-search-head::after{ content:"សរសេរនៅទីនេះ។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] #view-community > .res-search .res-search-head::after::marker{ content:"តើអ្នកត្រូវការអ្វី? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] #view-events > h2[data-bwn]::after{ content:"ແລະ ຢູ່ໃສ."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] #view-events > h2[data-bwn]::after::marker{ content:"ມີຫຍັງ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] #view-updates > h2[data-bwn]::after{ content:"ຢູ່ Woonsocket."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] #view-updates > h2[data-bwn]::after::marker{ content:"ສິ່ງທີ່ກຳລັງເກີດຂຶ້ນ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] #view-settings > h2[data-bwn]::after{ content:"ຕາມໃຈທ່ານ."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] #view-settings > h2[data-bwn]::after::marker{ content:"ຕັ້ງມັນ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] #view-community > .res-search .res-search-head::after{ content:"ພິມຢູ່ນີ້."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] #view-community > .res-search .res-search-head::after::marker{ content:"ທ່ານຕ້ອງການຫຍັງ? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] #view-events > h2[data-bwn]::after{ content:"আর কোথায়।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] #view-events > h2[data-bwn]::after::marker{ content:"কী হচ্ছে, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] #view-updates > h2[data-bwn]::after{ content:"যা ঘটছে।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] #view-updates > h2[data-bwn]::after::marker{ content:"Woonsocket-এ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] #view-settings > h2[data-bwn]::after{ content:"সাজিয়ে নিন।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] #view-settings > h2[data-bwn]::after::marker{ content:"নিজের মতো "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] #view-community > .res-search .res-search-head::after{ content:"এখানে লিখুন।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] #view-community > .res-search .res-search-head::after::marker{ content:"আপনার কী প্রয়োজন? "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
}
@supports selector(::before::marker){
  html[lang="es"] #citysvc::before{ content:"cómo contactarla."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="es"] #citysvc::before::marker{ content:"La ciudad, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pt"] #citysvc::before{ content:"como contactá-la."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pt"] #citysvc::before::marker{ content:"A cidade, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="fr"] #citysvc::before{ content:"comment la joindre."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="fr"] #citysvc::before::marker{ content:"La ville, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ht"] #citysvc::before{ content:"kijan pou kontakte l."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ht"] #citysvc::before::marker{ content:"Vil la, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="it"] #citysvc::before{ content:"come contattarla."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="it"] #citysvc::before::marker{ content:"La città, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="pl"] #citysvc::before{ content:"jak się z nim skontaktować."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="pl"] #citysvc::before::marker{ content:"Miasto, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ru"] #citysvc::before{ content:"как с ним связаться."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ru"] #citysvc::before::marker{ content:"Город, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="el"] #citysvc::before{ content:"πώς να επικοινωνήσετε."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="el"] #citysvc::before::marker{ content:"Η πόλη, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="vi"] #citysvc::before{ content:"cách liên hệ."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="vi"] #citysvc::before::marker{ content:"Thành phố, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ar"] #citysvc::before{ content:"كيف تتواصل معها."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ar"] #citysvc::before::marker{ content:"المدينة، "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh"] #citysvc::before{ content:"如何联系。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh"] #citysvc::before::marker{ content:"这座城市，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="zh-Hant"] #citysvc::before{ content:"如何聯繫。"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="zh-Hant"] #citysvc::before::marker{ content:"這座城市，"; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="ko"] #citysvc::before{ content:"연락하는 방법."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="ko"] #citysvc::before::marker{ content:"이 도시, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="km"] #citysvc::before{ content:"របៀបទាក់ទង។"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="km"] #citysvc::before::marker{ content:"ទីក្រុង "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="lo"] #citysvc::before{ content:"ວິທີຕິດຕໍ່."; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="lo"] #citysvc::before::marker{ content:"ເມືອງນີ້ "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
  html[lang="bn"] #citysvc::before{ content:"কীভাবে যোগাযোগ করবেন।"; display:list-item; list-style:none; list-style-position:inside; font-style:italic }
  html[lang="bn"] #citysvc::before::marker{ content:"শহর, "; font-style:normal; font-family:var(--font-serif); font-weight:500; color:inherit }
}
/* ==== END CONSIDERED 2026-09-25 ==== */


/* ==================================================================================================
   SPACE2 2026-09-28. TWO SWITCHES. BOTH ARE OFF. NEITHER CHANGES ONE PIXEL OF THE SHIPPED APP.
   `[RYAN 2026-09-28]` "we are making a usable user-friendly app also it needs to work for people in
   their language, screen space maximized even when the top bar url bar and bottom keyboard pop up and
   steal half the screen" and "the flowery text taking up screenspace, is it balanced with
   functionality and aesthetics"

   THESE ARE THE TWO BIGGEST PIECES OF SCREEN IN THE APP THAT AN AGENT MUST NOT DECIDE.
   One is his brand. One is his navigation. Both are built, both are measured, both are screenshotted
   in both states in SPACE2_2026-09-28.md, and BOTH DEFAULT TO EXACTLY WHAT SHIPPED TONIGHT.
   To see either one, add its class to the `.phone` element. Nothing reads a flag, nothing is stored,
   no `localStorage` key is invented, and app.js is not touched, because app.js was not this job's
   file. `[RYAN]` "my eyes are the truth" - so these exist to be LOOKED AT, and then ruled on.
   ================================================================================================== */

/* --------------------------------------------------------------------------------------------------
   SWITCH 1 of 2: `.phone.space-bandrest` - the seven serif band sentences stand down on the smallest
   phone and at the largest text, and ONLY there.

   WHAT THEY ARE, SO NOBODY THINKS THIS IS A CUT OF SOMEBODY'S DRAFT. The VOICE 2026-09-20 block in
   this file names each of the seven as copied VERBATIM from BRANDSPEC_2026-09-17.md with its line
   number. A designer chose these words and Margaux's committee approved the fonts. SPACESCOUT
   corrected the premise the brief was working from and it is corrected here too: THESE ARE NOT AN
   AGENT'S FLOWERY DRAFTS. Nothing below deletes a word, changes a word, or touches a strings file.
   `content:none` suppresses the pseudo-element; remove the class and the sentence is back, whole.

   WHAT THEY COST, MEASURED, real headless Chromium 154 over real HTTP, 320px, English, never a fetch.
   Each figure is a live A-B-A delta on `document.scrollHeight` with the drift proved to be 0:
     Home           223px at Normal      414px at Extra Large
     News/Updates    74px at Normal      172px at Extra Large
     Events          43px at Normal      121px at Extra Large
     Help             0px                  0px   (its sentence rides on `.res-search-head`, which
                                                  PHONEFIT 2026-09-24 already set to `display:none`)
     390 Home        74px at Normal
   AT 320 EXTRA LARGE THE THREE SENTENCES ON HOME COST 414px AND THE WHOLE CONTENT BAND WITH THE URL
   BAR UP IS 293.5px. One decorative device is larger than the screen it sits on. That is the single
   fact this switch exists to put in front of him.

   WHY THIS SHAPE, AND WHY IT IS THE ONE THE GUIDE ARGUES FOR. The guide's own words, recorded verbatim
   in BRAND_KIT_2026-09-23.md, are one Cormorant italic line, and never for UI or fine print. At 390
   and at Normal these sentences ARE one line and they are doing exactly what the guide asks. At 320
   the same sentence wraps to TWO lines of 28px Cormorant, which is a paragraph-shaped block, and at
   Extra Large it is bigger than the viewport. So the gate is narrow on purpose: BELOW 360px, or at
   EXTRA LARGE. At 360px and up at Normal and Large, nothing changes and the guide's device is
   untouched. It is one media query plus one class and it is reversible by deleting this block.

   IT IS STILL HIS CALL, BECAUSE IT IS HIS BRAND, AND THE DEFAULT IS OFF FOR THAT REASON.
   -------------------------------------------------------------------------------------------------- */
@media (max-width:359px){
  .phone.space-bandrest .home-sec[data-sec="updates"] > .section-label::after,
  .phone.space-bandrest .home-sec[data-sec="events"] > .section-label::after,
  .phone.space-bandrest .home-sec[data-sec="quicklinks"] > .section-label::after,
  .phone.space-bandrest #view-events > h2[data-bwn]::after,
  .phone.space-bandrest #view-updates > h2[data-bwn]::after,
  .phone.space-bandrest #view-settings > h2[data-bwn]::after,
  .phone.space-bandrest #citysvc::before{ content:none; }
}
  .phone.space-bandrest.text-xlarge .home-sec[data-sec="updates"] > .section-label::after,
  .phone.space-bandrest.text-xlarge .home-sec[data-sec="events"] > .section-label::after,
  .phone.space-bandrest.text-xlarge .home-sec[data-sec="quicklinks"] > .section-label::after,
  .phone.space-bandrest.text-xlarge #view-events > h2[data-bwn]::after,
  .phone.space-bandrest.text-xlarge #view-updates > h2[data-bwn]::after,
  .phone.space-bandrest.text-xlarge #view-settings > h2[data-bwn]::after,
  .phone.space-bandrest.text-xlarge #citysvc::before{ content:none; }

/* --------------------------------------------------------------------------------------------------
   SWITCH 2 of 2: `.phone.space-dockrest` - the five nav buttons stand down WHILE SHE IS TYPING, and
   the crisis floor does not.

   THIS IS THE ONE THAT ANSWERS HIS SENTENCE, AND IT IS THE ONE I AM LEAST WILLING TO DECIDE.
   MEASURED BASELINE, real Chromium 154, 320x568 iOS Safari model, URL bar up, English, keyboard up:
   the content band between the bottom of the masthead and the top of the keyboard is 88.78px at
   Normal and 33.50px at Extra Large, and `#bw-dock` is 69.22px and 97.00px of it. THE DOCK IS 78
   PERCENT OF THE CONTENT BAND AT NORMAL AND 290 PERCENT OF IT AT EXTRA LARGE. Lines of answer in that
   band today: ONE at Normal, ZERO at Large, ZERO at Extra Large, on every view, in all 19 codes.
   WITH THIS SWITCH ON, same instrument, same cells: the band goes 88.78 -> 158.00 at Normal and
   33.50 -> 130.50 at Extra Large, and Home goes from 1 line to 2 at Normal.

   THE CRISIS PATH IS NOT TOUCHED AND THAT IS WHY THE SELECTOR IS `> nav.nav` AND NOT `#bw-dock`.
   `#bw-safetyrow`, which is where `#res-helpbar-fixed` and the 911 / 988 / 988-text doors live, is a
   SIBLING of `nav.nav` inside `#bw-dock`. It is not selected here, not by this block and not by any
   block I wrote tonight. The doors keep their 44px, their order, their literal ASCII digits and their
   offline reach in all nineteen codes, with the keyboard up and with it down, switch on or off.
   LAW L1 IS NOT BENT BY THIS AND IT IS NOT MEANT TO BE.

   WHAT IT COSTS HER, SAID PLAINLY, BECAUSE THIS IS THE ARGUMENT AGAINST IT: while the keyboard is up
   she cannot see the five tabs. She dismisses the keyboard and they are back, instantly, because the
   only thing holding them down is a `:focus`. Against that: right now, at the largest text an app for
   low vision offers, a resident typing on the smallest phone sees ZERO LINES of what she asked for.
   WHY `:has(...:focus)` AND NOT A KEYBOARD TEST. It is the SAME selector list
   `bottom_dock_2026-08-15.css` already uses to lift the dock, so it can never disagree with the file
   that owns the dock's position, and it needs no script. On Home there is no real field - `#home-help`
   is a decorative `.res-fauxinput` that hands her to Help - so Home is never in this state in life.

   WHAT I DID NOT DO, AND THE MEASUREMENT THAT SAYS SO. I did not add `interactive-widget` to the
   viewport meta. SPACESCOUT's FAIL 1 says the dock sits behind the keyboard in 70 of 70 model cells
   because `index.html:595` does not declare it. IT DOES NOT REPRODUCE. Modelled the honest way - the
   keyboard fires `visualViewport` 'resize' ONLY, `innerHeight` held, which is the spec default - the
   app's own `liftForKeyboard()` at bottom_dock_2026-08-15.js:454 publishes `--bw-kbdlift: 260px` and
   the dock's own CSS consumes it: dock BOTTOM edge lands on 214.00 and the top of the keyboard is
   214.00, flush, in 5 of 5 views and in 283 of 285 measured cells, 0 of 285 behind the keyboard.
   SPACESCOUT quoted the comment at line 424 of that file and missed the function thirty lines below
   it. So `interactive-widget=resizes-content` would buy 0px and 0 cells on the dock, while changing
   what `100vh` means, driving `--bw-kbdlift` to 0 and putting every measured divisor DOCKFIX, DOCK2
   and L1FIX built on those two custom properties into a state no harness in this project has ever
   run. It is the right thing to own in a quiet week and the wrong thing to write tonight.
   -------------------------------------------------------------------------------------------------- */
html:has(#res-q:focus,#ev-q:focus,#csv-input:focus,#news-q:focus,#lk-input:focus) .phone.space-dockrest #bw-dock > nav.nav{ display:none; }
/* ==== END SPACE2 2026-09-28 ==== */

/* ==================================================================================================
 * TRIM 2026-09-28/29. HIS RULING, APPLIED, AND THE ONE SWITCH THAT TURNED OUT TO BE DEAD.
 * --------------------------------------------------------------------------------------------------
 * `[RYAN 2026-09-28]` "Just because the flowery language is in the brand kit doesnt mean it fits this
 * app be smart about it", after "screen space maximized even when the top bar url bar and bottom
 * keyboard pop up and steal half the screen".
 *
 * Two agents established that the seven serif band sentences come verbatim from BRANDSPEC_2026-09-17
 * and that the brand guide requires a display line. HE HAS RULED THAT THIS IS NOT THE DECIDING
 * ARGUMENT. So this block is the "be smart about it": the sentence stands down in exactly the two
 * places where it stops being the guide's one italic line and becomes a paragraph bigger than the
 * band it sits in, and it is UNTOUCHED everywhere else. NOT ONE WORD IS DELETED. `content:none`
 * suppresses a pseudo-element; the sentence is whole again the moment the gate stops matching.
 * ================================================================================================== */

/* SWITCH 1 IS NOW ON, AND IT IS ON WITHOUT THE CLASS, BECAUSE A CLASS NO FILE APPLIES IS OFF FOREVER.
   SPACE2 2026-09-28 built `.phone.space-bandrest` and left it OFF for his ruling. `.phone`'s class
   list is written by index.html and app.js, NEITHER OF WHICH IS THIS AGENT'S FILE, so there is no
   honest way to "turn the class on" from a stylesheet. SPACE2's seven+seven class rules above are
   therefore KEPT BYTE-IDENTICAL as a manual override hook and are NOT deleted; what follows repeats
   them with the `.space-bandrest` requirement removed, which is what turning the switch on means.

   RE-PROVED TONIGHT ON THIS ENGINE, 320px, English, real Chromium 154, dsf 1, band measured from the
   bottom of the sticky masthead to min(dock top, visualViewport bottom):
     Home,  keyboard DOWN, Normal:  12 lines -> 14 lines
     News,  keyboard DOWN, Normal:   7 lines -> 10 lines
     News,  keyboard  UP,  Normal:   1 line  ->  4 lines
     Events,keyboard  UP,  Normal:   1 line  ->  4 lines
   SPACE2's page-height deltas reproduce: 223px on Home at Normal, 414px at Extra Large, against a
   293.50px content band with the URL bar up. ONE DECORATIVE DEVICE WAS LARGER THAN ITS OWN SCREEN.

   WHAT IS KEPT, AND THIS IS THE OTHER HALF OF HIS RULING. At 360px and up, at Normal and at Large,
   NOTHING CHANGES. That is where the sentence really is one italic line and where it makes this feel
   like a product rather than a form. The gate is BELOW 360px, OR EXTRA LARGE, and nowhere else.
   Undo: delete this block. SPACE2's class rules above still stand and the sentences come straight
   back. */
@media (max-width:359px){
  .phone .home-sec[data-sec="updates"] > .section-label::after,
  .phone .home-sec[data-sec="events"] > .section-label::after,
  .phone .home-sec[data-sec="quicklinks"] > .section-label::after,
  .phone #view-events > h2[data-bwn]::after,
  .phone #view-updates > h2[data-bwn]::after,
  .phone #view-settings > h2[data-bwn]::after,
  .phone #citysvc::before{ content:none; }
}
.phone.text-xlarge .home-sec[data-sec="updates"] > .section-label::after,
.phone.text-xlarge .home-sec[data-sec="events"] > .section-label::after,
.phone.text-xlarge .home-sec[data-sec="quicklinks"] > .section-label::after,
.phone.text-xlarge #view-events > h2[data-bwn]::after,
.phone.text-xlarge #view-updates > h2[data-bwn]::after,
.phone.text-xlarge #view-settings > h2[data-bwn]::after,
.phone.text-xlarge #citysvc::before{ content:none; }

/* --------------------------------------------------------------------------------------------------
 * SWITCH 2 IS NOT TURNED ON, AND IT IS NOT A CASCADE FAILURE. IT IS A DUPLICATE OF A RULE THAT
 * ALREADY SHIPS. THE RULE ABOVE IS KEPT BYTE-IDENTICAL AND NOTHING IS DELETED.
 * --------------------------------------------------------------------------------------------------
 * SPACE2 2026-09-28 reports that `.phone.space-dockrest` takes the content band from 88.78 to 158.00
 * at Normal and 33.50 to 130.50 at Extra Large, and my brief called it one of the two biggest wins in
 * the app. I was told to turn it on and prove it. IT DOES NOT REPRODUCE, AND I MEASURED WHY RATHER
 * THAN ADDING A NUMBER TO IT. 320x568 iOS Safari model, URL bar up, innerHeight 474, keyboard
 * modelled as visualViewport 214 with a visualViewport 'resize' ONLY and never a window resize:
 *
 *   view       real focusable field?   :has(...:focus) matches?   nav display OFF   nav display ON
 *   Home       NONE                    false                      block             block
 *   Find Help  NONE                    false                      block             block
 *   Help       #res-q                  TRUE                       none              none
 *   Events     #ev-q                   TRUE                       none              none
 *   News       #news-q                 TRUE                       none              none
 *
 * TWO FACTS KILL IT, AND THEY PULL IN OPPOSITE DIRECTIONS.
 * 1. On Help, Events and News the selector MATCHES and `nav.nav` is ALREADY `display:none` with the
 *    switch OFF. The app already stands the nav down while she types; SPACE2's own comment says the
 *    focus list was copied from bottom_dock_2026-08-15.css, which is the file that was already doing
 *    it. Dock height with the keyboard up measures 1.00px on Events and News and 45.00px on Help, and
 *    the 45.00 is `#res-helpbar-fixed`, the crisis floor, which is sacred and stays.
 * 2. On Home and Find Help the dock IS still 69.22px with the keyboard up, and there the switch can
 *    never fire, because THERE IS NO VISIBLE NON-READONLY INPUT IN EITHER VIEW - measured, zero of
 *    them - so none of the five listed fields exists. SPACE2's own comment already says Home's
 *    `#home-help` is a decorative `.res-fauxinput`. WHICH MEANS THE 88.78px BASELINE IS A MODEL OF A
 *    STATE A RESIDENT CANNOT REACH: with no field to type into, the keyboard does not come up.
 *
 * SO THE HEADLINE BASELINE NEEDS CORRECTING AND I AM SAYING IT PLAINLY. "88.78px and one line at
 * Normal" is the band on Home and Find Help with a keyboard that cannot appear there. On the three
 * views where she CAN raise the keyboard the band is ALREADY 157.00 at Normal and 129.50 at Large and
 * Extra Large, with the switch off. Turning the switch on is writing a rule that wins its cascade and
 * changes nothing, which is the "green that lies" this project has been bitten by six times today.
 * IT STAYS OFF. It is kept, not deleted, because if bottom_dock's own rule is ever narrowed this
 * block is the backstop.
 * -------------------------------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------------------------------
 * AND HERE IS WHAT SWITCH 2 WAS TRYING TO BE. THE MASTHEAD STANDS DOWN WHILE SHE IS TYPING.
 * --------------------------------------------------------------------------------------------------
 * The dock already stands down while she types. The MASTHEAD DOES NOT, and on the one reachable cell
 * that is worst in the whole app it is the entire problem. Help, 320px, iOS URL bar up, Large or
 * Extra Large text, `#res-q` focused, keyboard up: of 214px of glass, the masthead takes 83.50 and
 * the crisis floor takes 119.34, and THE CONTENT BAND IS 10.16px. Zero lines. She is typing into a
 * search box and there is no room left to show her what she found.
 *
 * MEASURED, this rule on and off, same browser, same page, same instant (+px of content band):
 *   Normal      Help  113.00 -> 169.00  (+56.00)   3 lines -> 5 lines
 *   Normal      News  157.00 -> 213.00  (+56.00)   1 line  -> 3 lines
 *   Normal    Events  157.00 -> 213.00  (+56.00)   1 line  -> 4 lines
 *   Large       Help   10.16 ->  93.66  (+83.50)   0 lines -> 0 lines  (band grows 9x, still 0)
 *   Large     Events  129.50 -> 213.00  (+83.50)   1 line  -> 1 line
 *   XLarge      Help   10.16 ->  93.66  (+83.50)   0 lines -> 0 lines
 *   XLarge      News  129.50 -> 213.00  (+83.50)   0 lines -> 1 line
 *
 * THE CRISIS PATH IS NOT TOUCHED AND I CHECKED IT RATHER THAN ASSUMING IT. `header.hdr` contains
 * ZERO `tel:` and ZERO `sms:` hrefs and no crisis element, verified by reading every href in the
 * masthead as a string in en, ar and el. Nothing was dialled. In all nine cells above the crisis
 * floor's own height and its three doors are IDENTICAL on and off: `tel:911`, `tel:988`, `sms:988`,
 * in that order, at 44.00px at Normal and 119.34px at Large and Extra Large. L1 is not bent.
 *
 * WHAT IT COSTS HER, SAID PLAINLY. While she is typing she cannot see the wordmark, the tagline
 * "Your community, in one place", or the Settings gear. She stops typing and all three are back
 * instantly, because the only thing holding them down is a `:focus`. Against that: at the largest
 * text an app built for low vision offers, a resident typing on the smallest phone has 10.16px of
 * screen to read the answer in. THE SELECTOR IS THE SAME FOCUS LIST bottom_dock_2026-08-15.css
 * ALREADY USES for the dock, so this can never disagree with the file that owns the dock's position,
 * and it needs no script and no new localStorage key.
 *
 * SCOPED TO PHONE WIDTHS ONLY (max-width:430px). On a tablet or desktop the masthead never moves.
 * Undo: delete this one rule. `[RYAN]` "my eyes are the truth" - the pictures are in
 * outputs\TRIM_2026-09-28_shots\.
 * -------------------------------------------------------------------------------------------------- */
@media (max-width:430px){
  html:has(#res-q:focus,#ev-q:focus,#csv-input:focus,#news-q:focus,#lk-input:focus) .phone > header.hdr{ display:none; }
}
/* ==== END TRIM 2026-09-28 ==== */

/* ==================================================================================================
 * RENDER 2026-09-29. THREE THINGS, ALL OF THEM HIS WORDS. NO IMPORTANT-FLAG ADDED: the live count of that token in
 * this file was MEASURED at 127 occurrences on 102 lines before this block and is 127 after it.
 * (The handover said 45. It is not 45 in this file as it stands today; reported, not silently
 * accepted, and nothing here changes it either way.)
 * NOTHING IS DELETED. Every rule below either sits at a lower specificity than the dated sheet it
 * sits under, or beats it on specificity with the reason written next to it.
 * Backup: NEVER_OVERWRITE_BACKUPS/render_2026-09-29/app.css.PRE_RENDER
 * ================================================================================================== */

/* -- 1. THE BLOB, RENDERED AS ROWS SHE CAN SCAN. evFactRows() in app.js is the one reader; these are
 *       the only styles it needs. MEASURED: exactly two records in 243 render any of this, so the
 *       757-783px TRIM recovered on Events and the 209 on Home stand untouched on the other 241. */
.ev-facts{margin:2px 0 10px;padding:0 0 10px;border-bottom:1px solid var(--border)}
.ev-fact{font-size:15px;line-height:1.5;margin-top:8px}
.ev-fact:first-child{margin-top:0}
.ev-fact-k{display:block;font-weight:700}
.ev-fact-v{display:block}
/* the twenty ride names, which were one comma run-on inside a 216-word paragraph. A list she can run
 * her eye down, two columns where the glass allows it and one where it does not. */
.ev-fact-list{list-style:none;margin:3px 0 0;padding:0;columns:2;column-gap:14px}
.ev-fact-list li{break-inside:avoid;padding:2px 0;font-size:14.5px;line-height:1.45}
@media (max-width:359px){ .ev-fact-list{columns:1} }
/* text-large and text-xlarge are CSS zoom on .phone, so the glass under the columns shrinks as the
 * type grows. Two columns of ride names at Extra Large on a 320px phone is four characters wide.
 * One column at both raised steps, on the same reasoning BW-21 wrote for the news clamp. */
.phone.text-large .ev-fact-list,
.phone.text-xlarge .ev-fact-list{columns:1}
/* the card face: the first two rows, one line each, the rest one tap away in the drawer. Same trade
 * `.ev-preview` already makes in this app. */
.ev-facts-face{margin-top:5px;font-size:13.5px;line-height:1.45}
.ev-fact-face{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ev-fact-face .ev-fact-k{display:inline;font-weight:700}
.ev-fact-face .ev-fact-v{display:inline;color:var(--muted)}

/* -- 2. "MeetingSep 17, 2026". `[RYAN 2026-09-29]`, quoted verbatim off a card.
 *       THE CAUSE, MEASURED: the category and the date are adjacent element siblings with no text
 *       between them, and the ONLY thing holding them apart was `gap:9px` on
 *       `.phone[class*="nf-"] #home-updates .nf-meta` in news_panel_2026-08-23.css. That selector
 *       needs an `nf-*` class on `.phone`, and that class is added by news_panel_2026-08-23.js,
 *       which index.html loads at line 3556, at the END of the body. app.js paints this same block
 *       in the FIRST frame on purpose (JANKLOAD 2026-09-02). So between the first paint and that
 *       script there is a window with no gap rule and no separator character, and that window is
 *       what he read off the card. It is also permanent for anyone whose browser never runs that
 *       file, and permanent for a screen reader and for copy-and-paste in every case, because a
 *       CSS gap is not a character.
 *       THE FIX IS AT THE JOINING, NOT AT THE STRING: app.js now emits a real space between the two
 *       (three call sites: updCard, _hlCardInner, _paintHomeHlGrid/_meta), and this base rule gives
 *       the block its gap from the first frame. A single space, not a middle dot, because the app
 *       must read correctly in all nineteen and a space is the one separator that is direction
 *       neutral: a dot between an Arabic label and a Latin date sits on the wrong side of the run.
 *       IT COSTS NOTHING VISUALLY: in a flex container a text node that is only white space is not
 *       rendered as a flex item at all (CSS Flexbox 1, anonymous flex items), so the space is
 *       invisible wherever the flex rule applies and does its job wherever it does not.
 *       LOWER SPECIFICITY THAN THE DATED SHEET ON PURPOSE, (0,1,1) against its (0,3,1), and this
 *       file loads first. The dated rule still wins and still says exactly what it said. Nothing is
 *       superseded; this only covers the frames in which that rule does not match at all. */
#home-updates .nf-meta{display:flex;align-items:center;gap:9px;flex-wrap:wrap}

/* -- 3. "the main story should show fully on every mode". `[RYAN 2026-09-29]`, about Candidate
 *       Connections.
 *       WHAT TRUNCATES IT: `.phone.nf-grid #home-updates .nf-row-what` in
 *       news_panel_2026-08-23.css line 72, `-webkit-line-clamp:2`, raised to 4 at text-large and
 *       taken off entirely at text-xlarge by BW-21 on 2026-09-24. `grid` is the live container
 *       (index.html line 67). So at Normal the lead story shows two lines and at Large four.
 *       WHY THAT CLAMP'S OWN REASON NO LONGER HOLDS FOR THIS CONTAINER, AND THIS IS THE HONEST
 *       PART: the clamp block's stated trade is "three stories on Home means the second and third
 *       show two lines each". DECLUTTER changed the grid painter to `L.slice(0,1)` on 2026-09-08.
 *       The block paints ONE cell. There is no second or third story to protect, so the clamp is
 *       now costing the lead story its words and buying nothing. That is a stale justification, not
 *       a disagreement with BW-21, and BW-21's own sentence - "Never fewer lines at a bigger step"
 *       - is what this finishes.
 *       SCOPED TO THE LEAD CELL ONLY. `:first-child` means that if the grid is ever put back to two
 *       cells, the second one keeps its clamp exactly as written and only the main story is full.
 *       CASCADE: news_panel_2026-08-23.css loads at index.html line 874, AFTER app.css at 678, so
 *       this has to win on specificity and does - (1,6,0) against that sheet's (1,4,0) at
 *       text-large and (1,3,0) at Normal. No important-flag anywhere below, and the live computed value is read back
 *       in the proof rather than assumed.
 *       THIS DOES NOT TOUCH `.ev-preview`. TRIM clamped `.ev-preview` to one line for a measured
 *       757-783px on Events and 209 on Home, and events_variants_2026-08-23.css line 338 hides it
 *       at Large and Extra Large for a measured +7,667px and +16,015px. Those are EVENT rows. This
 *       is the Home NEWS lead. Different elements, different sheets, no overlap: checked, not
 *       assumed. There is no conflict to report between his ruling and those two decisions. */
.phone.nf-grid #home-updates .nf-two .nf-cell:first-child .nf-row-what{
  -webkit-line-clamp:none; line-clamp:none; display:block; overflow:visible;
}
/* the same story in the three containers that name a lead element of their own, so "every mode"
 * means every mode and not just the one that ships today. */
.phone.nf-lead #home-updates .nf-lead .nf-what,
.phone.nf-paper #home-updates .nf-lead .nf-what{
  -webkit-line-clamp:none; line-clamp:none; display:block; overflow:visible;
}
/* ==== END RENDER 2026-09-29 ==== */


/* ==============================================================================================
 * HELPLAY 2026-10-01. SUPPORT FOR THE TWO REAL HEADINGS app.js NOW EMITS INTO #acc-wrap.
 * `[RYAN 2026-10-01]` "the help page can be laid out so much better with more screen space"
 *
 * WHY THIS IS TWO RULES AND NOT A REDESIGN. The Help page had ONE heading in its whole
 * accessibility tree above thirty focusable controls. app.js now emits a real <h2 class="acc-secth">
 * for the city-services section and wraps each accordion button in a real <h3 class="acc-hh">,
 * which is the WAI-ARIA accordion pattern (<h3><button aria-expanded>) and keeps BOTH semantics:
 * the button is still a button, still 44px+, still the same markup inside.
 *
 * `.acc-hh` MUST COST ZERO PIXELS. A default <h3> brings a font-size, a font-weight and a
 * margin-block with it, and `.acc-h` (app.css:1839) already sets every one of those on the button.
 * `font:inherit` and `margin:0` make the wrapper geometrically invisible, so the accordion row is
 * the same height, the same colour and the same hit target it was before. Measured 2026-10-01.
 *
 * `.acc-secth` IS SCREEN-READER-ONLY ON PURPOSE. The visible heading for this section already
 * exists one element above, `h3.nl-cityhead`, in the same words. Drawing a second one would spend
 * a row of the scarcest band in the app to say nothing new. This is the same clip pattern
 * helppage_2026-08-27.css:366 uses and documents for `.hp-cat > h3`, on this same page.
 *
 * NOT RED. Nothing here sets a colour at all: red is for crisis only.
 * Undo: delete this block and the two class names in app.js's renderCommunity().
 * ============================================================================================== */
#acc-wrap > .acc > .acc-hh{
  margin:0; padding:0; font:inherit; font-weight:inherit; letter-spacing:inherit;
  display:block; color:inherit; background:none; border:0;
}
#acc-wrap > .acc-secth{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%);
  white-space:nowrap; border:0;
}
/* ==== END HELPLAY 2026-10-01 ==== */

/* ============================================================================
   DESIGN 2026-10-01.  "THE TEXT GROWS. THE FRAME DOESN'T."
   ----------------------------------------------------------------------------
   [RYAN 2026-10-01] "there are UI especially improvements that can be made
   everywhere in the app, i am asking you to understand me and everything i have
   said and make the actual app better" and "the info page can be so much
   better, the help page can be laid out so much better with more screen space".

   THE FAULT THIS BLOCK FIXES, MEASURED IN A REAL BROWSER AT 320 WIDE:
   `.phone.text-large` is CSS `zoom:1.5` and `.phone.text-xlarge` is `zoom:2`
   (this file, above). CSS zoom scales EVERY length, not just type. A resident
   who turns TEXT up gets a bigger APP: at Extra Large the masthead went 56 ->
   84 device px, the view gutters 16 -> 32 each side, the "NEWS" eyebrow's air
   26 -> 52, the card padding 12 -> 24, and the search submit button fell onto
   a 96 px row of its own. Measured 2026-10-01 at 320x568, Extra Large, English,
   Spanish and Arabic: Home, Events, News and Info each completed ZERO rows of
   answer above the dock, and Events and News showed ZERO WORDS of an answer.
   The dock plus crisis floor owned 56 percent of the Help screen.

   THE DESIGN RULE, AND IT IS ONE RULE: at the two big text steps every
   dimension that is NOT type is divided back toward the size it paints at
   Normal, so the frame keeps roughly its Normal physical size and every pixel
   it gives up goes to the resident's answer. Type is never reduced. Leading is
   never reduced: leading is legibility for the reader who turned text up, and
   buying pixels from it would be taking back what she asked for.

   PRECEDENT IN THIS FILE, NOT A NEW IDEA: `.phone.text-xlarge #bw-dock{zoom:.75}`
   and `.phone.text-xlarge .hdr{zoom:.75}` already counter-zoom two containers for
   exactly this reason, and `.phone.text-xlarge .event-row .ev-tags .wrr-flag`
   already divides a fixed size by the zoom. This block finishes the job.

   WHAT IT BUYS, measured before and after on the live wall at Extra Large,
   "rows of answer" = an answer element whose box is entirely above the dock:
     Info   0 -> 1 complete door, at 320 AND 390, in English, Spanish AND Arabic
     News   0 -> 10 words of the lead story at 320 EN (0 before, in every cell)
     Home   5 -> 10 words at 320, 6 -> 21 at 390
     Help   content window 165 -> 220 device px, +34 percent
   NORMAL AND MEDIUM ARE UNTOUCHED: every selector here is gated on
   `.text-large` or `.text-xlarge`. Control cells at Normal measured identical
   before and after (News 0 rows/37 words, Info 5 rows/23 words, both ends).

   WHAT IT DOES NOT DO, SAID PLAINLY SO NOBODY RE-DERIVES IT:
   - Home still completes ZERO rows. Its lead paragraph is 753 device px against
     747 px of usable screen, because [RYAN 2026-09-29] "the main story should
     show fully on every mode" took the clamp off. That is HIS ruling against
     HIS ruling and it is a card, not an agent's call. Nothing here clamps it.
   - Events still completes ZERO rows. Measured: hiding `#evcal` entirely buys
     2 words and 0 rows. The blocker is the height of an event row itself.
   - NO IMPORTANT FLAG IS ADDED ANYWHERE IN THIS BLOCK. The file carried 131
     occurrences of that flag before this block and carries 131 after it.
   ========================================================================== */

/* 1. THE MASTHEAD. Was .75 at Extra Large (this file, above) and uncounter-zoomed
      at Large, so it painted 84 device px at BOTH big steps against 56 at Normal.
      The gear's touch target is the floor: .hdr-gear is min-height:44px, so at
      Extra Large it paints 44 x 2 x .58 = 51 device px and at Large 44 x 1.5 x
      .78 = 51.5, both still LARGER than the 44 it paints at Normal. */
.phone.text-large .hdr{zoom:.78}
.phone.text-xlarge .hdr{zoom:.58}

/* 2. THE GUTTERS. `.view{padding:16px 16px ...}` paints 32 device px each side at
      Extra Large: 64 of a 320 px screen, a fifth of the width, on a column that is
      only 160 CSS px wide to begin with. At 7px the column goes 128 -> 146 CSS px,
      about 14 percent more characters per line, which shortens every paragraph in
      the app. The bottom calc() is reproduced exactly: it reserves the dock. */
.phone.text-large .view{padding:10px 10px calc(var(--bw-dockh, 120px) / var(--bw-viewzoom, 1) + 10px)}
.phone.text-xlarge .view{padding:7px 7px calc(var(--bw-dockh, 120px) / var(--bw-viewzoom, 1) + 7px)}

/* 3. THE SECTION EYEBROWS. They STAY: Home's `h2.section-label` are Home's only
      headings and cutting them was declined with reasons. What stops is their air
      doubling: margin 26 + padding 11 paints 74 device px around a 24 px word. */
.phone.text-large .section-label{margin:12px 0 5px;padding:6px 0 0}
.phone.text-xlarge .section-label{margin:7px 0 3px;padding:4px 0 0}
.phone.text-large .section-label:first-child,.phone.text-xlarge .section-label:first-child{margin-top:2px}
.phone.text-large .view > h2[data-bwn],.phone.text-xlarge .view > h2[data-bwn]{padding-top:4px}
/* `#updates-list .upd-sec` is an ID rule (1,1,0) and a class-only override loses
   to it. Named with the ID so it wins at the source, not with a flag override. */
.phone.text-large #updates-list .upd-sec,.phone.text-xlarge #updates-list .upd-sec{padding-top:4px;margin:4px 0 4px}

/* 4. CARD PADDING. Was 12px at the big steps = 24 device px of air on all four
      sides of every card on a 160 CSS px column. */
.phone.text-large .card{padding:9px}
.phone.text-xlarge .card{padding:7px}
.phone.text-large .upd-card,.phone.text-xlarge .upd-card{padding:7px}

/* 5. THE 96 PX SUBMIT ROW, SOLVED WITHOUT SILENCING THE SPINNER.
      `.phone.text-*large .res-search-row{flex-wrap:wrap}` above gives `.res-go`
      `flex:1 1 100%`, so the submit button takes a full-width row: #news-search-wrap,
      #ev-search-wrap, #csv-search and Help's .res-search each measured 226 device px
      at Extra Large against 49 at Normal. The previous pass proposed `display:none`
      on `.res-go` and REFUSED IT, correctly: app.js:3108 makes `.res-go` the host of
      the busy spinner (`.res-search-row.is-busy .res-go::after` in this file), and
      hiding it is the only "working" signal gone on four surfaces for 2,196-3,722 ms.
      THIS KEEPS THE BUTTON VISIBLE AND KEEPS THE SPINNER. It just stops the button
      being a row: it returns to the field's row as a 44 CSS px square, which paints
      88 device px at Extra Large, twice the 44 px touch floor. Row 226 -> 98 device
      px on FOUR surfaces. NO CHANGE IS NEEDED IN app.js AND NONE IS ASKED FOR.
      THE COST, SAID PLAINLY AND SHOWN IN THE SCREENSHOTS: the field's placeholder
      truncates earlier ("Searc..." instead of "Search for help: food, rent..."). The
      field is still visibly a field and the button is still visibly a magnifier. */
.phone.text-large .res-search-row,.phone.text-xlarge .res-search-row{flex-wrap:nowrap;align-items:stretch;gap:6px}
.phone.text-large .res-search-row .res-combo,.phone.text-xlarge .res-search-row .res-combo{flex:1 1 auto;min-width:0;width:auto}
.phone.text-large .res-search-row .res-go,.phone.text-xlarge .res-search-row .res-go{flex:0 0 auto;width:48px;min-width:48px;margin-top:0}
.phone.text-xlarge .res-search-row .res-go{width:44px;min-width:44px}
.phone.text-large .res-search-row .res-combo input,.phone.text-xlarge .res-search-row .res-combo input{padding-left:9px;padding-right:9px}
.phone.text-large #ev-search-wrap,.phone.text-xlarge #ev-search-wrap,
.phone.text-large #news-search-wrap,.phone.text-xlarge #news-search-wrap{margin-bottom:6px}

/* 6. EVENTS. The week/month calendar and the filter bar are 389 device px of
      control above the first event. These compactions are everything that can be
      taken without removing a function from the resident who turned text up: the
      day chip reads SIDEWAYS instead of stacking, which is also how it reads at a
      glance. Measured: this is not enough and Events still completes zero rows.
      See the card in outputs\DESIGN_2026-10-01.md. */
.phone.text-large .evcal-head,.phone.text-xlarge .evcal-head{margin-bottom:2px}
.phone.text-large .evcal-strip,.phone.text-xlarge .evcal-strip{padding:1px 0 4px;gap:6px}
.phone.text-large .evcal-chip,.phone.text-xlarge .evcal-chip{flex-direction:row;gap:6px;min-height:44px;min-width:0;padding:4px 8px}
.phone.text-xlarge #evcal .evcal-toggle{flex-direction:row;gap:4px}
.phone.text-xlarge #evcal .evcal-tg{width:auto;flex:1 1 0;min-width:0}
.phone.text-large #evcal,.phone.text-xlarge #evcal{margin-bottom:6px}
/* FOUND BY FALSIFY_NAVFIXED AND FIXED BEFORE ANYONE ELSE HAD TO FIND IT. The first
   install of this block took NAVFIXED from 84 of 412 RED to 92, EIGHT NEW REDS, all
   of them "#bw-dock rest/scrolled pinned" on Events at both big steps. Cause:
   `#evcal.evcal-tight{margin-inline:-12px}` (this file, above) pulls the calendar out
   to what it assumes is a 12px view gutter. Section 2 above makes that gutter 7px at
   Extra Large and 10px at Large, so the calendar hung 5px/2px past the viewport on
   each side, the document went horizontally scrollable (scrollWidth 330 against
   clientWidth 320), and a horizontal scrollbar pushed the FIXED dock 18px (Extra
   Large) and 6px (Large) off the bottom of the screen. The negative margin now
   tracks the gutter. Re-measured: scrollWidth == clientWidth == 320 and the dock gap
   is 0.0px at both steps, NAVFIXED back to 84 of 412. */
.phone.text-large #evcal.evcal-tight{margin-inline:-10px}
.phone.text-xlarge #evcal.evcal-tight{margin-inline:-7px}
.phone.text-large .evcal-pager,.phone.text-xlarge .evcal-pager{margin-bottom:4px}
.phone.text-large .evcal-pg,.phone.text-xlarge .evcal-pg{padding:5px 10px}
.phone.text-large #filterbar,.phone.text-xlarge #filterbar{margin-bottom:6px;padding-bottom:4px}
.phone.text-large #filterbar .fbtn,.phone.text-xlarge #filterbar .fbtn{padding:5px 10px}

/* 7. THE CRISIS FLOOR. WASTED SPACE ONLY. NOTHING IS BURIED, MADE CONDITIONAL,
      SCROLLED OFF OR REMOVED. 911, 988 and 988-by-text keep their literal ASCII
      digits, their order, their colours, their hrefs and their 13px radius; no
      font-size here, no display, no visibility, no position, no z-index, no
      content. The floor measured 72.9 device px at Normal and 222.5 at Extra
      Large, 3.05x, because `.rrh-911` takes its own full-width row at Extra Large
      so the word "Emergency" fits whole (that rule stays and is what makes the
      banner below possible).
      WHAT CHANGES: the full-width 911 door stops being a TALL COLUMN on a WIDE
      row. Its numeral and its word sit side by side, so the empty space either
      side of a centred numeral becomes the word instead of height. It is the same
      width, the same colour, the same numeral size, the same dial target, and it
      reads MORE like a button, not less. Tile min-height 64 -> 54/50 CSS px still
      paints 81 device px at Extra Large and 75 at Large, BOTH LARGER THAN THE 72.9
      THE SAME TILE PAINTS AT NORMAL. No crisis tile paints smaller here than it
      does at Normal, and that is the test this block holds itself to.
      MEASURED: floor 222.5 -> 185.8 device px at Extra Large, 129.5 -> 117.5 at
      Large; Help's content window 165 -> 220 device px. */
.phone.text-large .res-helpbar .rrh-crisis,.phone.text-xlarge .res-helpbar .rrh-crisis{gap:5px}
.phone.text-large .res-helpbar .rrh-crisis a.rrh-911,
.phone.text-large .res-helpbar .rrh-crisis a.rrh-988,
.phone.text-large .res-helpbar .rrh-crisis a.rrh-988t,
.phone.text-xlarge .res-helpbar .rrh-crisis a.rrh-911,
.phone.text-xlarge .res-helpbar .rrh-crisis a.rrh-988,
.phone.text-xlarge .res-helpbar .rrh-crisis a.rrh-988t{padding:5px 4px}
.phone.text-large .res-helpbar .rrh-crisis a.rrh-911,
.phone.text-large .res-helpbar .rrh-crisis a.rrh-988,
.phone.text-large .res-helpbar .rrh-crisis a.rrh-988t{min-height:50px}
.phone.text-xlarge .res-helpbar .rrh-crisis a.rrh-911,
.phone.text-xlarge .res-helpbar .rrh-crisis a.rrh-988,
.phone.text-xlarge .res-helpbar .rrh-crisis a.rrh-988t{min-height:54px}
.phone.text-xlarge .res-helpbar .rrh-crisis a.rrh-911{flex-direction:row;align-items:center;justify-content:center;gap:10px}
/* ==== END DESIGN 2026-10-01 ==== */
/* ==================================================================================================
   LOOK2 2026-10-01.  --bw-gutter : ONE SOURCE OF TRUTH FOR THE PAGE GUTTER.

   THE BUG CLASS THIS ENDS. `#evcal.evcal-tight{margin-inline:-12px}` (this file, earlier) hard-codes
   a gutter the page stopped using. An earlier pass changed the gutter, that -12px silently disagreed
   with it, Events became horizontally scrollable and the fixed dock lifted 18 px off the bottom, and
   FALSIFY_NAVFIXED went from 84 RED to 92. That pass found it itself and put the number back. The
   number was never the defect: TWO FILES HOLDING THE SAME MEASUREMENT WAS. There is now one.

   MEASURED, real headless Chromium 154 over real HTTP, before this block existed: `--bw-gutter` did
   not resolve to anything on `.phone` at any text size, and `#evcal.evcal-tight` painted x=0 w=320
   at 320 Extra Large. The three gutter values below are the ones the page already paints, read off
   `.view` and off the DESIGN 2026-10-01 block: 16 px at Normal, 10 px at Large, 7 px at Extra Large.
   SO `.view` DOES NOT MOVE BY ONE PIXEL AT ANY TEXT SIZE. It is re-pointed at the variable, not
   re-valued. The selector list repeats the DESIGN block's own two selectors so it wins on source
   order at equal specificity rather than by raising specificity.

   THE ONE REAL PIXEL CHANGE, STATED PLAINLY: at NORMAL text the calendar's bleed goes from -12 px to
   -16 px, so it gets 4 px wider on each side and finally reaches the edge the way it already does at
   Large and at Extra Large, where the DESIGN block set -10 and -7 to match those gutters exactly.
   The -12 px was left over from a gutter of 12 px. At Large and Extra Large the painted value is
   UNCHANGED (-10 and -7). Checked after installing: no horizontal overflow on Events at 320 or 390,
   because the bleed is now exactly the gutter instead of 2 px past it, which is what caused the 92.

   NO IMPORTANT-FLAG WAS ADDED BY LOOK2. The count in this file is 131 before this block and
   131 after it, and the token is deliberately not written here because the project counts it
   with a grep and a comment that spelled it would have inflated the count by one. It did, once, and
   this block's own guard caught it and refused the write.
   Backup: NEVER_OVERWRITE_BACKUPS/look2_2026-10-01/app.css.PRE_LOOK2
   Undo: delete this block. Every value returns to the literal it came from.
   ------------------------------------------------------------------------------------------------ */
.phone{--bw-gutter:16px}
.phone.text-large{--bw-gutter:10px}
.phone.text-xlarge{--bw-gutter:7px}
.phone .view,.phone.text-large .view,.phone.text-xlarge .view{padding:var(--bw-gutter) var(--bw-gutter) calc(var(--bw-dockh, 120px) / var(--bw-viewzoom, 1) + var(--bw-gutter))}
#evcal.evcal-tight,.phone.text-large #evcal.evcal-tight,.phone.text-xlarge #evcal.evcal-tight{margin-inline:calc(-1 * var(--bw-gutter))}

/* ==================================================================================================
   LOOK2 2026-10-01.  --navy AND --sky ARE BOTH #7A2636, AND LOOK2 DID NOT CHANGE EITHER VALUE.

   VERIFIED LIVE, not read off a document: in real headless Chromium 154 over real HTTP,
   getComputedStyle on `.phone` returns --navy #7A2636 AND --sky #7A2636. Two named tokens on one
   colour. Counted in this file: var(--navy) 157 times, var(--sky) 39 times.

   IT IS NOT A TYPO. The 2026-09-18 rebrand header above says it out loud - "Blue is demoted: no
   token below is blue" - so `--sky` was collapsed onto the signature burgundy on purpose. The defect
   is that the NAME now lies: a designer who writes var(--sky) expects something other than what
   var(--navy) gives, and nothing in the cascade tells them otherwise. Note the blue `--navy:#0B4FB0;
   --sky:#0B4FB0` higher up THIS FILE IS INSIDE A COMMENT - the "Superseded 2026-09-02 values, kept
   for the undo" block - and is not live. The live blue pin is the per-selector crisis block
   (`.res-crisis, #res-helpbar-fixed, .res-211, .emg-call` and the rest), which deliberately freezes
   the crisis path through the rebrand and MUST NOT be touched.

   WHY LOOK2 DID NOT SEPARATE THEM, SAID PLAINLY RATHER THAN SHIPPED QUIETLY: giving `--sky` its own
   value repaints 39 sites, `--sky`/`--sky-soft` are re-set independently in the dark, high-contrast,
   dark-high-contrast AND crisis-pin blocks, and the crisis surfaces are inside the blast radius.
   Eight days before Autumnfest, on a disk that would not hold the screenshots needed to check 39
   sites in light and dark, that is a flourish with a crisis surface downstream. MAP_2026-10-01 CARD
   16 already puts it after the festival and it is HIS card. The separation is specified, with the
   counts, in outputs/LOOK2_2026-10-01.md so the next pass does not have to re-derive it.
   NOTHING BELOW THIS LINE CHANGES A COLOUR. This is a comment on purpose. */


/* ==================================================================================================
   SPACE 2026-10-06.  GIVE THE BAND BACK AT BIG TEXT. Fixes in this block, each with its undo line.
   Everything here is source-ordered LAST in the file on purpose: DESIGN 2026-10-01 overrides same-day
   work on source order alone, and so does this block. NO NEW IMPORTANT-FLAG IS WRITTEN HERE (count
   before and after is the same). No tap target, no line-height and no word of the news lead is touched.

   CHANGE 1 (undo: delete this CHANGE 1 block). THE SERIF BAND SENTENCES STAND DOWN AT LARGE TEXT, AND
   AT EVERY WIDTH BELOW 360 IN EVERY LANGUAGE. `[RYAN 2026-10-01]` "Just because the flowery language is
   in the brand kit doesnt mean it fits this app be smart about it."  TRIM 2026-09-28/29 already stood
   them down below 360 and at Extra Large, but its two rules lose on SPECIFICITY in every language but
   English: `html[lang="es"] .home-sec[...] > .section-label::after{content:"...";display:block}` is
   (0,4,2) and TRIM's `.phone .home-sec[...] > ...::after{content:none}` is (0,4,1). So at 320 Normal
   and Large the sentence still showed in Spanish, French, Arabic and Korean (eyebrow block 100 px at
   320 Normal against 25 px in English). And Large was never in the gate, so at 390 Large the three
   sentences cost 111 + 62 + 111 px of a 536 px band. These rules use `html .phone ...`, which is
   (0,4,2) and wins the tie on source order, and `.text-large`, which is (0,5,2).
   WHAT STAYS: the eyebrow itself ("News", "Coming Up", "Quick Links"), its gold hairline, its tracking,
   its h2/div tag. ONLY the decorative serif sentence on `::after` is suppressed. At Normal and at 360
   wide or more nothing in this change applies. Not one word is deleted; `content:none` only.
   ================================================================================================== */
@media (max-width:359px){
  html .phone .home-sec[data-sec="updates"] > .section-label::after,
  html .phone .home-sec[data-sec="events"] > .section-label::after,
  html .phone .home-sec[data-sec="quicklinks"] > .section-label::after,
  html .phone #view-events > h2[data-bwn]::after,
  html .phone #view-updates > h2[data-bwn]::after,
  html .phone #view-settings > h2[data-bwn]::after,
  html .phone #citysvc::before{ content:none; }
}
html .phone.text-large .home-sec[data-sec="updates"] > .section-label::after,
html .phone.text-large .home-sec[data-sec="events"] > .section-label::after,
html .phone.text-large .home-sec[data-sec="quicklinks"] > .section-label::after,
html .phone.text-large #view-events > h2[data-bwn]::after,
html .phone.text-large #view-updates > h2[data-bwn]::after,
html .phone.text-large #view-settings > h2[data-bwn]::after,
html .phone.text-large #citysvc::before{ content:none; }
/* END SPACE 2026-10-06 CHANGE 1 */

/* ==================================================================================================
   CHANGE 2 (undo: delete this CHANGE 2 block). THE SIXTEEN PIXELS AT 390 NORMAL. Measured before any
   edit, real headless Chromium 154 over real HTTP, 390x844, Normal, English, light: the first event row
   runs y=595.5 to 790.9 and the fixed dock's top is y=774.8, so the row missed the glass by 16.1 px.
   Where the pixels were, per eyebrow, read off getComputedStyle: margin-top 4 (first child only),
   padding-top 11 (the room under the 1 px gold hairline), the serif sentence's margin-top 10, then the
   sentence itself (32.2 px of Cormorant, TYPE, untouched), then margin-bottom 8. The two eyebrows above
   the first event row cost 67.6 px each. NEITHER type NOR line-height is touched: what moves is the
   padding under the hairline (11 to 8: -3) and the gap between the eyebrow word and its sentence
   (10 to 3: -7), which is -10 per eyebrow and -20 above the first event row, so the row clears the
   dock by 3.9 px at 390x844 with today's news story. The sentence line keeps its own 32.2 px leading.
   SCOPE: Home's three eyebrows, at Normal text only (Large and Extra Large already have their own
   rules in DESIGN 2026-10-01 and, with CHANGE 1, no sentence). At 360 wide or more the sentence still
   shows at Normal, which is where he likes it; at 359 and below it is gone (CHANGE 1) so the ::after
   rule is moot there.
   ================================================================================================== */
.phone:not(.text-large):not(.text-xlarge) .home-sec[data-sec="updates"] > .section-label,
.phone:not(.text-large):not(.text-xlarge) .home-sec[data-sec="events"] > .section-label,
.phone:not(.text-large):not(.text-xlarge) .home-sec[data-sec="quicklinks"] > .section-label{ padding-top:8px; }
html .phone:not(.text-large):not(.text-xlarge) .home-sec[data-sec="updates"] > .section-label::after,
html .phone:not(.text-large):not(.text-xlarge) .home-sec[data-sec="events"] > .section-label::after,
html .phone:not(.text-large):not(.text-xlarge) .home-sec[data-sec="quicklinks"] > .section-label::after{ margin-top:3px; }
/* END SPACE 2026-10-06 CHANGE 2 */

/* ==================================================================================================
   CHANGE 3 (undo: delete this CHANGE 3 block). HOME'S NEWS AND SEARCH FRAME STILL GREW WITH THE TEXT.
   DESIGN 2026-10-01 ruled "THE TEXT GROWS. THE FRAME DOESN'T." and divided back the masthead, the
   gutters, the eyebrows, the card padding, the search row and the dock. It did not reach these five
   pieces of Home, so they still paint at 1.5x (Large) and 2x (Extra Large) of what they paint at
   Normal. Measured with getComputedStyle times the effective zoom, 390 wide, English, painted px,
   Normal -> Large -> Extra Large:
     search-to-eyebrow gap    18 -> 27 -> 27     (the search box's own margin-bottom, `.res-search{margin:2px 0 18px}`)
     news lead top+bottom pad 11 -> 16.5 -> 22 each   (`.nf-two{padding:11px 0}`, consolidated_2026-10-01.css)
     lead meta line margin     6 -> 9 -> 12
     news card bottom margin  10 -> 15 -> 20   (`.phone[class*="nf-"] #home-updates.nf-on` at (1,3,0), consolidated_2026-10-01.css, which loads AFTER this file, so the override is (1,4,0))
     news actions top margin  10 -> 15 -> 20   (`.hna-acts{margin-top:10px}`, injected by home_news_actions_2026-08-16.js)
   EACH IS RE-VALUED SO IT PAINTS WHAT IT PAINTS AT NORMAL, ON THE DESIGN BLOCK'S OWN ARITHMETIC. NO TYPE,
   NO LEADING, NO TAP TARGET and no word of the news lead is touched: the lead is still unclamped
   (`[RYAN 2026-09-29]` "the main story should show fully on every mode"), "See the location" and
   "See all news" keep their painted 88 px at Extra Large.
   Selectors are `.phone.text-large` / `.phone.text-xlarge` only, so Normal does not move by one pixel.
   ================================================================================================== */
.phone.text-large #home-help{margin-bottom:11px}
.phone.text-large #home-help .res-search{margin-bottom:12px}
.phone.text-xlarge #home-help{margin-bottom:11px}
.phone.text-xlarge #home-help .res-search{margin-bottom:12px}
.phone.nf-grid.text-large #home-updates.nf-on{margin-bottom:7px}
.phone.nf-grid.text-xlarge #home-updates.nf-on{margin-bottom:5px}
.phone.text-large #home-updates .hna-acts{margin-top:7px}
.phone.text-xlarge #home-updates .hna-acts{margin-top:5px}
.phone.nf-grid.text-large #home-updates .nf-two{padding:7px 0}
.phone.nf-grid.text-xlarge #home-updates .nf-two{padding:5.5px 0}
.phone.nf-grid.text-large #home-updates .nf-cell .nf-meta{margin-top:4px}
.phone.nf-grid.text-xlarge #home-updates .nf-cell .nf-meta{margin-top:3px}
/* END SPACE 2026-10-06 CHANGE 3 */

/* ==================================================================================================
   FACE 2026-10-07.  A TIME IS NEVER CUT IN HALF ON THE CARD FACE.
   The card face (`.ev-fact-face`, one per row, two rows per card) was `white-space:nowrap` with an
   ellipsis, so at 390 wide the park's hours read "Autumnfest: Saturday · Sat, Oct 10 10:0..." and the
   closing time, the one thing a person standing at the park wants, was the part thrown away.
   NOW: the face wraps, and stops at seven lines (not unlimited) because one record's face (214, the
   crafters list, 60+ names) is 43 lines at 390 wide and a clamp is the only thing that keeps it a
   preview. Seven covers every hours, date and bus row in all 19 language codes (en, es, fr, ko, ar
   measured at 390 and 320 across all three sizes; the other 14 at 320 Extra Large, the worst cell).
   The longest real one is the Arabic walk-in-hours row at 320 Extra Large: exactly seven lines.
   NOT TOUCHED: font-size 13.5px, line-height 1.45 (leading is legibility), the drawer `.ev-fact`, the
   `.ev-fact-k` and `.ev-fact-v` rules, any colour, any tap target. Adds no flags.
   Added AFTER DESIGN 2026-10-01 and SPACE 2026-10-06 because this file is decided by source order.
   UNDO FACE-1: delete from the opener line of this comment to `END FACE 2026-10-07 CHANGE 1`, or
   restore NEVER_OVERWRITE\face_2026-10-07\app.css.PRE_FACE.
   ================================================================================================== */
.ev-fact-face{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:7;line-clamp:7;white-space:normal;overflow:hidden;overflow-wrap:break-word;text-overflow:ellipsis}
/* END FACE 2026-10-07 CHANGE 1 */

/* ==================================================================================================
   BAR 2026-10-07 CHANGE 1 (R-012 in RULINGS.json: "Leads with a search bar that is the exact height as
   the home page", `[RYAN 2026-08-15]`). MEASURED, real Chromium over http, 120 cells (4 widths x 3 text
   sizes x light/dark x en, es, ar, fr, ko), each view proven visible first. At Normal text 360 and wider
   Home and Help were equal at 48.8px. They were NOT equal at Large and Extra Large, nor at 320 in es, fr
   and ar. Two causes, both on Home only:
   (a) Home's decorative box `.res-fauxinput` is a DIV clamped to THREE lines, so a long prompt wrapped to
       two lines and the Home card grew (69.6px at 320 Normal in es/fr/ar, 104.4px at Large). Help's box is
       a real single-line INPUT and never grows. Home now clamps to ONE line, exactly as Help's input does.
   (b) index.html carries `.phone.text-xlarge #home-help{zoom:.75}` (DECLUTTER 2026-09-08) so Home's card
       painted at 1.5x while Help's painted at 2x: 73.2px against 97.6px. Home is the SHORTER card, so it is
       RAISED to Help's size, not Help lowered. Type on Home grows from 1.5x to 2x at Extra Large. Nothing is
       shrunk anywhere. The `:not(#bar)` adds one id of specificity so this wins over the inline rule in
       index.html without any forced-priority token.
   COST, said plainly: at Large and Extra Large a long Home prompt now ends in an ellipsis instead of
   wrapping, the way Help's box already clips it. Home's card at Extra Large is 24.4px taller.
   Added AFTER DESIGN 2026-10-01, SPACE 2026-10-06 and FACE 2026-10-07 because source order decides here.
   UNDO BAR-1: delete from the opener line of this comment to `END BAR 2026-10-07 CHANGE 1`, or restore
   NEVER_OVERWRITE\bar_2026-10-07\app.css.
   ================================================================================================== */
#home-help .res-search .res-fauxinput{-webkit-line-clamp:1;line-clamp:1}
.phone.text-xlarge #home-help:not(#bar){zoom:1}
/* END BAR 2026-10-07 CHANGE 1 */

/* ==================================================================================================
   BAR 2026-10-07 CHANGE 2. CORRECTS CHANGE 1's first rule, which I screenshotted and it was WRONG:
   `-webkit-line-clamp:1` on `.res-fauxinput` left the top half of a SECOND line peeking out under the
   first (the padding box is not clipped by the legacy clamp), so Home read "Search for help:... food, rent"
   cut through the middle of the word. Home's box is now a plain single-line block with a real ellipsis,
   which is what Help's input does. Height is unchanged by this correction (48.8px at Normal).
   UNDO BAR-2: delete from the opener line of this comment to `END BAR 2026-10-07 CHANGE 2`, or restore
   NEVER_OVERWRITE\bar_2026-10-07\app.css.PRE_CHANGE2.
   ================================================================================================== */
#home-help .res-search .res-fauxinput{display:block;white-space:nowrap;text-overflow:ellipsis;-webkit-line-clamp:none;line-clamp:none}
/* END BAR 2026-10-07 CHANGE 2 */

/* HEADLINES 2026-10-08. [RYAN 2026-10-08] "Can we drop the City notices and Good news, ; Trash day the bus the library".
   The decorative serif line under the gold NEWS and QUICK LINKS labels is switched off in every language. The gold
   label stays, so each section still says what it is. Nothing above is edited or deleted; this rule wins because it
   comes last and carries !important. Undo: delete this block. */
.home-sec[data-sec="updates"] > .section-label::after,
.home-sec[data-sec="quicklinks"] > .section-label::after,
html[lang] .home-sec[data-sec="updates"] > .section-label::after,
html[lang] .home-sec[data-sec="quicklinks"] > .section-label::after{ content:none !important; display:none !important; }
/* HEADLINES 2026-10-08, second pick from his card: "Events, near you." (Home), the Events and News tab
   headlines, and Info's "The city, how to reach it." go, in every language. "Make it yours." (Settings) and
   "Type it here." (Help) stay; he did not pick them. Undo: delete this block. */
.home-sec[data-sec="events"] > .section-label::after,
html[lang] .home-sec[data-sec="events"] > .section-label::after,
#view-events > h2[data-bwn]::after,
#view-updates > h2[data-bwn]::after,
html[lang] #view-events > h2[data-bwn]::after,
html[lang] #view-updates > h2[data-bwn]::after,
#citysvc::before,
html[lang] #citysvc::before{ content:none !important; display:none !important; }
