/* Town Guide - readability first. 19px base, high contrast, 48px+ targets. */
:root{
  /* Soft page, white cards. Pure white at full brightness causes halation - text
     appears to shimmer - which is worse with astigmatism and cataracts. This keeps
     contrast far above AAA while cutting the glare, and lets cards lift off the page. */
  --bg:#edeff4; --surface:#ffffff; --surface-2:#e2e5ec;
  --ink:#14181f; --ink-soft:#3d4654; --line:#cfd5e0; --line-strong:#848da0;
  --accent:#1d4ed8; --accent-ink:#fff;
  --ok:#14682f; --warn:#8a4b00; --danger:#a3121f;
  --radius:12px; --wrap:1180px;
}
:root[data-accent="blue"]{--bg:#dbe6f8;--surface:#f7fafe;--surface-2:#c3d6f2}
:root[data-accent="green"]{--bg:#daeee2;--surface:#f6fdf8;--surface-2:#bfe2cd}
:root[data-accent="maroon"]{--bg:#f6dde3;--surface:#fff9fa;--surface-2:#eec2cc}
:root[data-accent="purple"]{--bg:#e4daf6;--surface:#faf8ff;--surface-2:#cfbdee}
:root[data-accent="slate"]{--bg:#dee4ec;--surface:#f9fafc;--surface-2:#c8d1dd}

:root[data-theme="dark"]{
  --bg:#10141c; --surface:#1a212d; --surface-2:#242e3d;
  --ink:#f2f5fa; --ink-soft:#c3ccdb; --line:#3d4859; --line-strong:#6b7a91;
  --ok:#7ed99a; --warn:#f0b357; --danger:#ff9aa5;
}
@media (prefers-color-scheme:dark){
  :root[data-theme="system"]{
    --bg:#10141c; --surface:#1a212d; --surface-2:#242e3d;
    --ink:#f2f5fa; --ink-soft:#c3ccdb; --line:#3d4859; --line-strong:#6b7a91;
    --ok:#7ed99a; --warn:#f0b357; --danger:#ff9aa5;
  }
}
:root[data-accent="blue"][data-theme="dark"]{--bg:#0d1421;--surface:#162034;--surface-2:#22314f}
@media (prefers-color-scheme:dark){:root[data-accent="blue"][data-theme="system"]{--bg:#0d1421;--surface:#162034;--surface-2:#22314f}}
:root[data-accent="green"][data-theme="dark"]{--bg:#0b1710;--surface:#14241a;--surface-2:#1e3628}
@media (prefers-color-scheme:dark){:root[data-accent="green"][data-theme="system"]{--bg:#0b1710;--surface:#14241a;--surface-2:#1e3628}}
:root[data-accent="maroon"][data-theme="dark"]{--bg:#1a0d12;--surface:#28161e;--surface-2:#3b202b}
@media (prefers-color-scheme:dark){:root[data-accent="maroon"][data-theme="system"]{--bg:#1a0d12;--surface:#28161e;--surface-2:#3b202b}}
:root[data-accent="purple"][data-theme="dark"]{--bg:#130e21;--surface:#1e1732;--surface-2:#2c2049}
@media (prefers-color-scheme:dark){:root[data-accent="purple"][data-theme="system"]{--bg:#130e21;--surface:#1e1732;--surface-2:#2c2049}}
:root[data-accent="slate"][data-theme="dark"]{--bg:#0f1318;--surface:#191f27;--surface-2:#252d38}
@media (prefers-color-scheme:dark){:root[data-accent="slate"][data-theme="system"]{--bg:#0f1318;--surface:#191f27;--surface-2:#252d38}}
:root[data-accent="blue"]{--accent:#1d4ed8;--accent-ink:#fff}
:root[data-accent="blue"][data-theme="dark"]{--accent:#93b4ff;--accent-ink:#0b1020}
@media (prefers-color-scheme:dark){:root[data-accent="blue"][data-theme="system"]{--accent:#93b4ff;--accent-ink:#0b1020}}
:root[data-accent="green"]{--accent:#14682f;--accent-ink:#fff}
:root[data-accent="green"][data-theme="dark"]{--accent:#7ed99a;--accent-ink:#0b1020}
@media (prefers-color-scheme:dark){:root[data-accent="green"][data-theme="system"]{--accent:#7ed99a;--accent-ink:#0b1020}}
:root[data-accent="maroon"]{--accent:#9b1c31;--accent-ink:#fff}
:root[data-accent="maroon"][data-theme="dark"]{--accent:#f79db0;--accent-ink:#0b1020}
@media (prefers-color-scheme:dark){:root[data-accent="maroon"][data-theme="system"]{--accent:#f79db0;--accent-ink:#0b1020}}
:root[data-accent="purple"]{--accent:#5b21b6;--accent-ink:#fff}
:root[data-accent="purple"][data-theme="dark"]{--accent:#c9aaff;--accent-ink:#0b1020}
@media (prefers-color-scheme:dark){:root[data-accent="purple"][data-theme="system"]{--accent:#c9aaff;--accent-ink:#0b1020}}
:root[data-accent="slate"]{--accent:#334155;--accent-ink:#fff}
:root[data-accent="slate"][data-theme="dark"]{--accent:#cbd5e1;--accent-ink:#0b1020}
@media (prefers-color-scheme:dark){:root[data-accent="slate"][data-theme="system"]{--accent:#cbd5e1;--accent-ink:#0b1020}}

/* Maximum-contrast mode for low vision: pure black on white, heavy borders, and a
   link colour dark enough to stay legible. Declared after the accent rules so it wins
   regardless of which highlight colour is selected. */
:root[data-theme="contrast"]{
  --bg:#ffffff; --surface:#ffffff; --surface-2:#ffffff;
  --ink:#000000; --ink-soft:#000000; --line:#000000; --line-strong:#000000;
  --accent:#0000bb; --accent-ink:#ffffff;
  --ok:#005500; --warn:#5a3200; --danger:#a00000;
}
:root[data-theme="contrast"] .card,
:root[data-theme="contrast"] .catbtn,
:root[data-theme="contrast"] .status-list a,
:root[data-theme="contrast"] .res-group{border-width:3px}
:root[data-theme="contrast"] .section-note,
:root[data-theme="contrast"] .sl-note,
:root[data-theme="contrast"] .card-addr{color:#000000}

html{font-size:19px; -webkit-text-size-adjust:100%}
html[data-text="smaller"]{font-size:16px}
html[data-text="larger"]{font-size:23px}

/* The hidden attribute is only a user-agent rule, and ANY author rule beats it.
   Without this, .settings{display:flex} and .btn{display:inline-flex} make elements
   marked [hidden] visible anyway. This must stay. */
[hidden]{display:none!important}

*,*::before,*::after{box-sizing:border-box}
body{margin:0; background:var(--bg); color:var(--ink);
  font-family:'Segoe UI',system-ui,-apple-system,Roboto,Helvetica,Arial,sans-serif;
  line-height:1.65; font-size:1rem;
  /* Inherited by everything. Content comes from a web admin, so a pasted URL or a
     run-together list like "2nd,3rd,9th" - which a browser treats as one 40-character
     word, because it will not break after a comma with no space - would otherwise push
     straight out through the side of its card. */
  overflow-wrap:break-word}
.wrap{width:100%; max-width:var(--wrap); margin:0 auto; padding:0 1rem}
main{display:block; padding-bottom:3rem}
img{max-width:100%; height:auto}

h1,h2,h3{line-height:1.25; margin:0 0 .5rem}
h1{font-size:1.9rem} h2{font-size:1.5rem; margin-top:0} h3{font-size:1.15rem}
p{margin:0 0 .75rem}
/* Opt-in reading measure. This used to be on every <p>, which meant short notes,
   taglines and button rows all stopped short of the margin for no reason. */
.prose{max-width:72ch}

a{color:var(--accent); text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:2px}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{
  outline:4px solid var(--accent); outline-offset:2px; border-radius:4px}
.skip{position:absolute; left:-9999px; top:0; background:var(--accent); color:var(--accent-ink); padding:1rem; z-index:100}
.skip:focus{left:0}

.site-head{background:var(--surface); border-bottom:3px solid var(--line);
  border-top:5px solid var(--accent)}
.head-inner{display:flex; flex-wrap:wrap; gap:.6rem 1rem; align-items:center;
  justify-content:space-between; padding:.6rem 1rem}
.brand{text-decoration:none; color:var(--ink); display:block; margin:0; flex:0 0 auto}
.brand-name{display:block; font-size:1.1rem; font-weight:800; line-height:1.15; margin:0}
@media (min-width:520px){.brand-name{font-size:1.2rem}}
.brand-sub{display:block; font-size:.8rem; color:var(--ink-soft); font-weight:600; line-height:1.3}
.brand-loc{text-transform:uppercase; letter-spacing:.05em}
.brand-tag{display:block}
/* The separator is a real character in the markup, not a ::before. As generated CSS content
   it was invisible to copy-and-paste and to reader modes, so the two lines came out welded
   together - "Ione, CaliforniaEverything Ione". It is hidden, not removed, on a phone,
   where the tagline sits on its own line and needs no separator. */
.brand-sep{display:none}
@media (min-width:700px){
  .brand-tag{display:inline; text-transform:none; letter-spacing:0}
  .brand-sep{display:inline}
}
@media (min-width:700px){.brand-name{font-size:1.4rem}}

/* search lives in the header on every page */
.headsearch{flex:1 1 0; order:2; display:flex; gap:.45rem; align-items:center; margin:0; min-width:0}

.headsearch label{position:absolute; left:-9999px}
.headsearch input{flex:1 1 auto; min-width:0; min-height:48px; padding:.3rem .75rem;
  font:inherit; font-size:1.02rem; color:var(--ink); background:var(--surface);
  border:3px solid var(--line-strong); border-radius:var(--radius)}
.headsearch input:focus{border-color:var(--accent)}
.headsearch button{min-height:48px; padding:.3rem .85rem}
.settings-toggle{flex:0 0 auto; min-height:48px; padding:.3rem .75rem; font-size:.92rem}

/* Only this row is pinned. The title block scrolls away, so search and navigation
   stay reachable without a tall sticky header swallowing a phone screen. */
.navbar{background:var(--surface-2); border-top:1px solid var(--line);
  border-bottom:2px solid var(--line-strong); position:sticky; top:0; z-index:50}
.navrow{display:flex; flex-wrap:wrap; gap:.45rem; align-items:center; padding:.45rem .75rem}
/* Phone: the sticky bar is ONE compact row - Menu plus search - and the links drop
   below it as a full-width list at full size. Laid out as a grid of six cells it was
   ~100px of every screen, with short labels floating in wide cells. Descendant selectors
   on purpose - .navlinks .navlink outranks the plain .navlink further down the file
   whatever order these two end up in. */
/* narrow, not short - the 48px tap height stays, only the width comes down so the
   search box can share the row instead of being pushed onto one of its own */
.btn.menu-toggle{flex:0 0 auto; order:1; min-height:48px; padding:.3rem .45rem; gap:.3rem; font-size:.85rem}
.menu-icon{font-size:.95rem; line-height:1}
.headsearch input{font-size:1rem; padding:.3rem .55rem}
.headsearch button{padding:.3rem .6rem; font-size:.9rem}
.navlinks{display:none; order:3; flex:1 1 100%; flex-direction:column; gap:0; margin-top:.5rem;
  max-height:min(62vh, 430px); overflow-y:auto}
.navrow.nav-open .navlinks{display:flex}
.navlinks .navlink{width:100%; justify-content:flex-start; text-align:left; min-height:52px;
  padding:.55rem .9rem; font-size:1.02rem; border:0; border-top:2px solid var(--line); border-radius:0}
.navlinks .navlink:first-child{border-top:0}
/* the last row is a button, so it needs the styling a link gets for free */
button.navlink{background:none; font:inherit; font-weight:700; color:var(--ink); cursor:pointer}
@media (min-width:700px){
  .btn.menu-toggle{display:none}       /* everything fits inline, nothing to open */
  .navlinks{display:flex; flex-direction:row; flex-wrap:wrap; gap:.4rem; order:0;
    max-height:none; overflow:visible; margin-top:0}
  .navlinks .navlink{width:auto; min-height:48px; padding:.3rem .7rem; font-size:1rem;
    border:2px solid transparent; border-radius:var(--radius)}
  .headsearch{flex:1 1 100%; order:0; gap:.5rem}
  .headsearch input{font-size:1.02rem; padding:.3rem .75rem}
  .headsearch button{padding:.3rem .85rem; font-size:1rem}
  .navrow{gap:.45rem .9rem; padding:.45rem 1rem}
  .navlink.nav-settings{display:none}  /* the header carries it on a wide screen */
}
/* Must come AFTER the .navlinks rule above: a media query carries no extra specificity,
   so if this sat earlier in the file the base flex:1 1 auto would win, the link group
   would stretch, and the search box would be shoved to the far right. */
@media (min-width:900px){
  .navrow{flex-wrap:nowrap}
  .navlinks{flex:0 1 auto}
  .headsearch{flex:1 1 230px; min-width:230px}
}
.navlink{display:flex; align-items:center; min-height:48px; padding:.3rem .7rem; font-weight:700;
  text-decoration:none; color:var(--ink); border-radius:var(--radius); border:2px solid transparent;
  justify-content:center; text-align:center}
@media (hover:hover){.navlink:hover{background:var(--bg)}}
.navlink.is-active{background:var(--accent); color:var(--accent-ink); border-color:var(--accent)}

.settings-close{margin-left:auto; align-self:flex-start; min-height:44px; padding:.2rem .8rem;
  cursor:pointer; font:inherit; font-size:.88rem; font-weight:700; color:var(--ink);
  background:var(--surface); border:2px solid var(--line-strong); border-radius:var(--radius)}
@media (hover:hover){.settings-close:hover{border-color:var(--accent)}}
.settings{position:relative; display:flex; flex-wrap:wrap; gap:1.25rem; padding:1rem; margin-bottom:1rem;
  background:var(--bg); border:2px solid var(--line); border-radius:var(--radius)}
.setting{display:flex; flex-direction:column; gap:.4rem; align-items:flex-start}
.setting-label{font-weight:700; font-size:.95rem}
.seg{display:flex; flex-wrap:wrap; gap:.4rem}
/* All of these sit on one row, so they must share a height. The larger "A" inside the
   text-size buttons was making those taller than the Light/Dark ones; line-height:1 on
   the glyph stops it pushing the box open. */
.seg-btn{display:inline-flex; align-items:center; justify-content:center; gap:.4rem;
  min-height:52px; min-width:56px; padding:.4rem 1rem; cursor:pointer; font:inherit; font-weight:700;
  color:var(--ink); background:var(--surface); border:2px solid var(--line-strong); border-radius:var(--radius)}
.seg-btn[aria-pressed="true"]{background:var(--accent); color:var(--accent-ink); border-color:var(--accent)}
.seg-a{font-weight:900; line-height:1}
.seg-sm .seg-a{font-size:.85rem}
.seg-lg .seg-a{font-size:1.25rem}
.a-icon{font-weight:900; line-height:1; letter-spacing:-.02em}
.a-icon .a-sm{font-size:.72em}
.lbl-long{display:none}
@media (min-width:900px){.lbl-long{display:inline} .lbl-short{display:none}}
/* On a phone the header toggle ate a chunk of the top bar for a control pinch-to-zoom
   already covers. It is hidden there, but the panel also holds dark mode and the colour
   schemes, so the footer carries a way in rather than losing them on mobile. */
/* Compound .btn.x selectors on purpose. As plain .head-settings these lost to the later
   .btn{display:inline-flex} on equal specificity and the button stayed visible on phones.
   Tying them to .btn outranks it wherever either rule ends up in the file. */
.btn.head-settings{display:none}
@media (min-width:700px){.btn.head-settings{display:inline-flex}}
.color-btn{display:flex; align-items:center; gap:.5rem}
.dot{width:1.1rem; height:1.1rem; border-radius:50%; border:2px solid var(--line); display:inline-block}

.btn{display:inline-flex; align-items:center; justify-content:center; gap:.6rem; min-height:52px;
  padding:.6rem 1.25rem; cursor:pointer; font:inherit; font-weight:700; text-decoration:none; text-align:center;
  border-radius:var(--radius); border:2px solid var(--accent); background:var(--accent); color:var(--accent-ink)}
.btn-ghost{background:var(--surface); color:var(--ink); border-color:var(--line-strong)}
.card .btn-call{padding:.6rem .4rem; gap:.45rem}
.btn-call{width:100%; justify-content:center; margin:.5rem 0; background:var(--bg); color:var(--ink); border:3px solid var(--accent)}
.call-icon{width:1.5rem; height:1.5rem; flex:0 0 auto; color:var(--accent)}
.call-text{display:flex; align-items:center; line-height:1.2}
/* A number written "(209) 245-8899" has a space in it and was breaking onto a second
   line inside the button on narrow cards; "209-245-8899" has none, which is why only
   some listings showed it. Never break a phone number. */
/* No blanket nowrap. The digits are held together by non-breaking spaces instead, which
   leaves a trailing "ext. 110" free to wrap rather than pushing out of the button. */
/* No blanket nowrap: it held "(209) 245-8899" together but also stopped
   "209-274-2412 ext. 110" breaking anywhere, so it ran out of its button. Digits are
   joined with non-breaking spaces instead, and the size is capped the way cards do it -
   at the Larger setting 1.2rem is 27.6px, which no phone-width button can hold. */
.call-num{font-size:min(1.2rem, 5.1vw); font-weight:800; color:var(--accent)}
/* At the Larger setting a 14-digit number needs 232px inside a button with only 205px to
   give, once the emergency page puts three cards across a desktop. The vw cap above does
   not help there - the screen is wide, the column is not. So at that setting the phone
   icon goes, the padding tightens, and the number stops growing: it is already bold and
   in the accent colour, and legibility was never the thing that was short. */
html[data-text="larger"] .call-icon{display:none}
html[data-text="larger"] .btn-call{padding-left:.6rem; padding-right:.6rem; gap:0}
html[data-text="larger"] .call-num{font-size:1rem}

.hero{padding-top:1.75rem; padding-bottom:1.25rem}
/* A live summary, not a link list - the outage links live on their own page, and
   repeating them here was three copies of the same thing on one screen. */
/* Same tinted band as the nav above it. The separation comes from the nav's darker
   bottom border rather than a different colour - making this near-white read as blank. */
.statusstrip{background:var(--surface-2); border-bottom:3px solid var(--line-strong)}
.statusstrip.has-alerts{border-bottom-width:0}
/* Centred: the bar holds three or four short live facts, so left-aligning them left a
   long empty stretch under a full-width nav. Centring keeps it balanced however many
   items are present - a failed feed hides its item and the rest stay centred. */
/* Phone: date, then the three weather facts together, then alerts. The three are wrapped
   in .strip-group so they hold one line as a unit and wrap among themselves if the air
   quality label runs long ("Unhealthy for sensitive groups"), instead of overflowing. */
.wx-stamp{color:var(--ink-soft); font-weight:600; margin:.6rem 0 0}
/* Jump links across the top of the outages page. scroll-margin-top is the important bit:
   the nav bar is sticky, so without it an anchor lands with its heading tucked underneath. */
.jumpbar{display:flex; flex-wrap:wrap; justify-content:center; gap:.5rem; margin:1rem 0 0}
.jumpbar .btn{min-height:48px; padding:.4rem .9rem; font-size:.95rem}
.section[id]{scroll-margin-top:5rem}
/* no heading above it any more, so the temperature sits up against the rule
   instead of leaving a gap where the words used to be */
.wx-current{padding-top:.8rem}
.wx-now{text-align:center}
.wx-temp{font-size:3.2rem; font-weight:800; line-height:1; margin:0 0 .2rem; color:var(--accent)}
.wx-desc{font-size:1.25rem; font-weight:700; margin:0 0 .6rem}
.wx-facts{list-style:none; display:flex; flex-wrap:wrap; justify-content:center; gap:.4rem .9rem;
  margin:0; padding:0; font-weight:600; color:var(--ink-soft)}
.wx-list{list-style:none; margin:0; padding:0}
.wx-day{display:grid; grid-template-columns:minmax(0,1fr) auto; gap:.1rem .8rem; align-items:baseline;
  padding:.7rem .2rem; border-bottom:2px solid var(--line)}
.wx-day:last-child{border-bottom:0}
.wx-dayname{font-weight:800; font-size:1.05rem}
.wx-daytemp{font-weight:800; font-size:1.05rem; text-align:right; white-space:nowrap}
.wx-low{color:var(--ink-soft); font-weight:700}
.wx-daydesc{color:var(--ink-soft); font-weight:600}
.wx-rain{color:var(--ink-soft); font-weight:600; text-align:right; white-space:nowrap}
@media (min-width:560px){
  .wx-day{grid-template-columns:8rem minmax(0,1fr) auto auto; padding:.6rem .2rem}
  .wx-rain{min-width:6rem}
}
.striprow{display:flex; flex-direction:column; align-items:center;
  gap:.35rem; padding:.5rem 1rem; font-size:clamp(.78rem, 4.3vw, .95rem)}
.strip-group{display:flex; flex-wrap:wrap; justify-content:center; align-items:center; gap:.2rem .35rem}
.strip-item{font-weight:700; text-align:center}
.strip-item:empty{display:none}
/* separators only between the grouped facts; a dangling one at the start of a line reads
   as a typo, which is what the old per-id overrides were working around */
.strip-group > .strip-item + .strip-item::before{content:"·"; color:var(--ink-soft);
  margin-right:.35rem; font-weight:400}
@media (min-width:700px){
  .striprow{flex-direction:row; flex-wrap:wrap; justify-content:center; gap:.35rem .9rem}
  .strip-group{gap:.35rem .9rem}
  .striprow > * + *::before,
  .strip-group > .strip-item + .strip-item::before{content:"·"; color:var(--ink-soft);
    margin-right:.9rem; font-weight:400}
}
/* "Air quality:" is the longest label on the line; phones get "Air:" so all three
   facts fit at a readable size. Screen readers still hear the full wording. */
.aq-long{display:none}
@media (min-width:700px){.aq-long{display:inline}}
.aq-good{color:var(--ok)} .aq-mod{color:var(--warn)} .aq-bad{color:var(--danger)}

/* Notice bar. Normally nothing is here; entries expire by date so it empties itself. */
.bulletins:empty{display:none}
.bulletin{border-bottom:3px solid var(--line-strong); background:var(--surface-2)}
.bulletin-inner{display:flex; flex-wrap:wrap; gap:.5rem .85rem; align-items:center; padding:.6rem 1rem}
.bulletin p{margin:0; max-width:80ch; font-size:1rem}
.bulletin-msg{flex:1 1 240px}
.bulletin-btn{flex:0 0 auto; min-height:38px; padding:.2rem .75rem; cursor:pointer;
  font:inherit; font-size:.85rem; font-weight:700; color:inherit;
  background:transparent; border:2px solid currentColor; border-radius:999px; opacity:.85}
@media (hover:hover){.bulletin-btn:hover{opacity:1}}
.bulletin-detail{padding:0 1rem .8rem}
/* Both states reserve the same width. "View" and "Hide" are the same letter count but
   not the same pixel width (w is wider than e), so without this the button - and every
   strip item after it - shifted a few pixels on each press. tabular-nums keeps the
   count from nudging it too. The quiet state matches so nothing jumps on load. */
.strip-quiet{display:inline-flex; align-items:center; justify-content:center;
  min-width:13.5ch; color:var(--ink-soft); font-weight:600}
.strip-alert-btn{display:inline-flex; align-items:center; justify-content:center;
  min-width:13.5ch; min-height:34px; padding:.1rem .7rem; cursor:pointer; font:inherit;
  font-size:.88rem; font-weight:800; font-variant-numeric:tabular-nums;
  color:var(--accent); background:var(--surface);
  border:2px solid var(--accent); border-radius:999px}
@media (hover:hover){.strip-alert-btn:hover{background:var(--accent); color:var(--accent-ink)}}
.alerts-collapsed .bulletin-optional{display:none}
.bulletin-detail p{font-size:.95rem}
.bulletin-tag{flex:0 0 auto; font-size:.74rem; font-weight:800; text-transform:uppercase;
  letter-spacing:.06em; padding:.2rem .55rem; border-radius:999px; white-space:nowrap}
.bulletin-info{background:var(--surface-2); border-bottom-color:var(--line-strong)}
.bulletin-info .bulletin-tag{background:var(--line); color:var(--ink)}
.bulletin-warning{background:#fdf3e0; border-bottom-color:#a5690a}
.bulletin-warning .bulletin-tag{background:#a5690a; color:#fff}
.bulletin-warning p{color:#4a2f00}
.bulletin-urgent{background:#fdeaec; border-bottom-color:#a3121f}
.bulletin-urgent .bulletin-tag{background:#a3121f; color:#fff}
.bulletin-urgent p{color:#5c0009}
:root[data-theme="dark"] .bulletin-warning{background:#3a2a08; border-bottom-color:#f0b357}
:root[data-theme="dark"] .bulletin-warning p{color:#ffe6b8}
:root[data-theme="dark"] .bulletin-urgent{background:#3d0f14; border-bottom-color:#ff9aa5}
:root[data-theme="dark"] .bulletin-urgent p{color:#ffd9dd}
@media (prefers-color-scheme:dark){
  :root[data-theme="system"] .bulletin-warning{background:#3a2a08; border-bottom-color:#f0b357}
  :root[data-theme="system"] .bulletin-warning p{color:#ffe6b8}
  :root[data-theme="system"] .bulletin-urgent{background:#3d0f14; border-bottom-color:#ff9aa5}
  :root[data-theme="system"] .bulletin-urgent p{color:#ffd9dd}
}

.hero-compact{padding-top:1rem; padding-bottom:.2rem}
.dir-title{margin:0; font-size:1.5rem}
.savebox{background:var(--surface); border:3px solid var(--accent); border-radius:var(--radius); padding:1.1rem}
.savebox ol{margin:.5rem 0 0; padding-left:1.4rem}
.savebox li{margin-bottom:.4rem}
.savebox .key{display:inline-block; padding:.1rem .5rem; font-weight:800;
  background:var(--surface-2); border:2px solid var(--line); border-radius:6px}
.hero p.tagline{font-size:1.1rem; color:var(--ink-soft)}

/* margin-top sits ABOVE the divider line, padding-top below it. Keeping the margin
   small tucks the rule up close to whatever it is separating from. */
.section{padding-top:1.4rem; padding-bottom:1.4rem; border-top:3px solid var(--line); margin-top:.6rem}
.section-head{display:flex; flex-wrap:wrap; gap:.75rem; align-items:baseline; justify-content:space-between}
/* Exempt from the global p{max-width:70ch} reading cap. These are short notes, not
   prose, and the cap made them wrap early with dead space to the right. */
.section-note{color:var(--ink-soft); font-size:.95rem; max-width:none}

.grid{display:grid; gap:1rem; grid-template-columns:minmax(0,1fr)}
@media (min-width:640px){.grid-2,.grid-3{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1000px){.grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}}

/* Cards stretch to the tallest in their row (grid default), so making each one a
   column and pushing the footer down with margin-top:auto lines the phone buttons and
   "Hours & more" links up across the row however long the descriptions are. */
.card{min-width:0; background:var(--surface); border:2px solid var(--line); border-radius:var(--radius);
  padding:1.1rem; display:flex; flex-direction:column; height:100%}
.card-foot{margin-top:auto; padding-top:.6rem}
/* Image above the name, both centred - the logo sitting to the left while the name
   centred in the leftover space read as lopsided, and everything below it (badge,
   address, phone, link) was centred already. */
.card-head{display:flex; flex-direction:column; align-items:center; gap:.5rem}
.card-logo{flex:0 0 auto; width:84px; height:84px; object-fit:contain; background:var(--surface);
  border:2px solid var(--line); border-radius:10px; padding:5px}
/* Two lines reserved for the name, then the badge on its own centred line. A one-line
   name leaves a gap above the badge, a two-line name butts against it - either way every
   badge sits at the same height across a row, and it doubles as a divider between the
   name and the description. The category is gone; these cards sit under a heading. */
.card-head-text{width:100%}
.card-title{font-size:1.2rem; margin-bottom:0; min-height:2.5em; text-align:center}
.card-status{text-align:center; min-height:1.9rem; margin:.1rem 0 .6rem}
.card-status .badge-open,.card-status .badge-closed{margin-left:0}
.card-title a{color:var(--ink)}
/* Exactly four lines, ellipsis added by the browser. Counting characters could not do
   this: word lengths vary, and the bold venue lead-in is part of the same paragraph. */
.card-desc{font-size:.98rem; text-align:left; display:-webkit-box; -webkit-line-clamp:4; line-clamp:4;
  -webkit-box-orient:vertical; overflow:hidden} /* Sits inside the pinned footer, so address, phone and the link form one block at the
   bottom of every card - separate from the description, and level across a row. */
.card-addr{font-size:.95rem; color:var(--ink-soft); text-align:center; margin:0 0 .4rem}
/* One fixed phrase, centred, so card heights do not vary with the length of a business
   name. The name stays in the link for screen readers - 29 identical "Hours & More Info"
   links would be unusable read out of context. */
.card-more{margin-bottom:0; font-size:.95rem; text-align:center}
.card.is-hidden{display:none}

.cat-icon{font-size:1.9rem; line-height:1}

.open-flag{margin-left:.5rem; font-weight:800}
.is-open{color:var(--ok)} .is-closed{color:var(--ink-soft)}

.switch{display:flex; align-items:center; gap:.75rem; min-height:52px; cursor:pointer; font-weight:700; font-size:1.05rem}
.switch input{width:28px; height:28px; accent-color:var(--accent); cursor:pointer}

.res-group{background:var(--surface); border:2px solid var(--line); border-radius:var(--radius); padding:1.1rem}
.res-group h3{border-bottom:2px solid var(--line); padding-bottom:.4rem}
.res-item{padding:.6rem 0; border-bottom:1px solid var(--line)}
.res-item:last-child{border-bottom:0}
.res-label{font-weight:700; display:block}
.res-note{font-size:.9rem; color:var(--ink-soft); display:block}

.status-list{list-style:none; margin:0; padding:0; display:grid; gap:.6rem}
.status-list a{display:block; min-height:52px; padding:.7rem 1rem; font-weight:700; text-decoration:none;
  background:var(--surface); border:2px solid var(--line-strong); border-radius:var(--radius); color:var(--ink)}
@media (hover:hover){.status-list a:hover{border-color:var(--accent)}}
.status-list .sl-note{display:block; font-weight:500; font-size:.9rem; color:var(--ink-soft)}

.event{display:flex; gap:1rem; align-items:flex-start; background:var(--surface);
  border:2px solid var(--line); border-radius:var(--radius); padding:1.1rem}
.event-date{flex:0 0 auto; text-align:center; min-width:74px; padding:.5rem;
  background:var(--accent); color:var(--accent-ink); border-radius:var(--radius)}
.event-mon{display:block; font-size:.85rem; font-weight:800; text-transform:uppercase; letter-spacing:.05em}
.event-day{display:block; font-size:1.7rem; font-weight:800; line-height:1}
.event-yr{display:block; font-size:.78rem; font-weight:700; opacity:.85}
.event-body h3{margin-bottom:.25rem}
.event-compact{padding:.7rem .9rem; align-items:stretch}
.event-compact .event-date{min-width:60px; padding:.35rem}
.event-compact .event-day{font-size:1.35rem}
.event-compact h3{font-size:1.05rem; margin:0}
.event-compact .event-meta{margin:.15rem 0 0}
.event-summary{margin:.25rem 0 0; font-size:.95rem}
/* The compact card used to be one big link, with an ::after overlay covering it. That
   overlay would have swallowed every click on these buttons and on the posters, so it
   went when Details started showing the whole event inline. */
.event-actions{margin:.45rem 0 0; position:relative; z-index:1}
/* Both buttons, not just Details. Close had no rule at all and fell back to the browser
   default, which is a fraction of the size and nowhere near a usable tap target. */
.event-details,.event-close{min-height:44px; padding:.35rem 1rem; font:inherit; font-size:.9rem; font-weight:700;
  cursor:pointer; color:var(--accent); background:var(--surface);
  border:2px solid var(--line-strong); border-radius:var(--radius)}
.event-full{position:relative; z-index:1; margin:.5rem 0 0; font-size:.95rem}
.event-full .event-credit,
.event-full .event-link,
.event-full .event-actions{text-align:center}
.event-full p{margin:0}
/* Positioned so the Details and Close buttons can sit above the card background. */
/* not cursor:pointer any more - the card itself is no longer a link, its buttons are */
.event-compact{position:relative}
@media (hover:hover){.event-compact:hover{border-color:var(--accent)}}
/* Pushed to the bottom of the card with margin-top:auto, so the cue sits on the same
   line across every card in a row however long each description is. */
.event-compact .event-body{display:flex; flex-direction:column; flex:1 1 auto}
.event-more{margin-top:auto; padding-top:.35rem; text-align:right; font-size:.9rem;
  font-weight:700; color:var(--accent); text-decoration:underline;
  text-underline-offset:3px; text-decoration-thickness:2px}
.event-meta{font-size:.95rem; color:var(--ink-soft); margin-bottom:.4rem}
/* Posters and the link form one block pinned to the bottom of the card and centred, so
   they line up across a row however long each description runs. */
.event{align-items:stretch}
.event-body{display:flex; flex-direction:column; flex:1 1 auto}
.event-foot{margin-top:auto; padding-top:.6rem; text-align:center}
.event-link{margin:.5rem 0 0}
.event-posters{display:flex; flex-wrap:wrap; justify-content:center; gap:.5rem; margin:0 0 .3rem}
.event-posters img{height:150px; width:auto; border-radius:8px; border:2px solid var(--line); display:block}
.event-posters a:focus-visible img{outline:4px solid var(--accent)}
.event-credit{font-size:.82rem; color:var(--ink-soft); margin:0}
/* Phone: the date runs along the top as a short "SEP 11" strip. Down the left it stretched
   to the full height of the card - a tall coloured band beside the text, holding 74px of
   width the card cannot spare. Side by side again as soon as there is room for it. */
.event{flex-direction:column; gap:.6rem}
.event-date{align-self:stretch; min-width:0; padding:.28rem .7rem}
.event-mon,.event-day,.event-yr{display:inline}
.event-mon::after{content:" "}
.event-day{font-size:1.25rem}
.event-compact .event-date{min-width:0}
.event-compact .event-day{font-size:1.15rem}
@media (min-width:560px){
  .event{flex-direction:row; gap:1rem}
  .event-date{align-self:auto; min-width:74px; padding:.5rem}
  .event-mon,.event-day,.event-yr{display:block}
  .event-mon::after{content:none}
  .event-day{font-size:1.7rem}
  .event-compact .event-date{min-width:60px; padding:.35rem}
  .event-compact .event-day{font-size:1.35rem}
}

.spotlight{background:var(--surface-2); border:3px solid var(--accent); border-radius:var(--radius); padding:1.25rem}
.spotlight .card{background:var(--surface); margin-bottom:0}
.spotlight #spotlight-next{display:flex; margin-left:auto; margin-right:auto}
.spotlight #spotlight-card{margin-bottom:1rem}

.detail-top{display:flex; gap:1.25rem; align-items:flex-start; flex-wrap:wrap}
/* Uses the SAME two-column grid as the content below, so the call button starts exactly
   where the Hours column starts rather than being shoved to the page edge. On a phone it
   collapses to one column and the button sits under the name, where it is wanted most. */
.detail-head{display:grid; gap:1rem; grid-template-columns:minmax(0,1fr); align-items:center}
@media (min-width:640px){.detail-head{grid-template-columns:repeat(2,minmax(0,1fr))}}
.detail-head h1{margin-bottom:.25rem}
.detail-head-call .btn-call{margin:0}
.detail-pic{margin:.25rem 0 1rem}
.detail-pic img{width:100%; max-width:460px; height:auto; border:2px solid var(--line);
  border-radius:var(--radius); display:block}
.verified-line{text-align:center; margin-top:1.25rem}
.detail-address{margin-top:.75rem}
.part-of{margin:.15rem 0 0; font-size:.95rem; color:var(--ink-soft)}
/* Listings with no fixed hours - seasonal tours, 24/7 emergency cover - show their note
   here instead of a table, so it needs to read as the answer, not as a stray sentence. */
.hours-varies{font-size:1.05rem; background:var(--surface-2); border-left:5px solid var(--accent);
  border-radius:8px; padding:.7rem .9rem; margin:0 0 .6rem; max-width:460px}
.detail-hours{border-collapse:collapse; width:100%; max-width:460px}
.detail-hours th,.detail-hours td{text-align:left; padding:.5rem .75rem; border-bottom:1px solid var(--line)}
.detail-hours th{font-weight:700}
.verified{display:inline-block; padding:.35rem .75rem; font-size:.9rem; font-weight:700;
  background:var(--surface-2); border:2px solid var(--line); border-radius:999px; color:var(--ink-soft)}
.backlink{display:inline-flex; align-items:center; min-height:48px; font-weight:700}
.photos{display:grid; gap:.75rem; grid-template-columns:minmax(0,1fr)}
@media (min-width:640px){.photos{grid-template-columns:repeat(2,minmax(0,1fr))}}
.photos img{border:2px solid var(--line); border-radius:var(--radius)}
.factlist{list-style:none; margin:0 0 1rem; padding:0}
.factlist li{padding:.45rem 0; border-bottom:1px solid var(--line)}
.factlist li:last-child{border-bottom:0}
.detail-groups{margin:.75rem 0 1rem}
.detail-group h3{font-size:.95rem; margin:.6rem 0 .3rem; color:var(--ink-soft)}
.chips{display:flex; flex-wrap:wrap; gap:.35rem; list-style:none; margin:0; padding:0}
.chips li{padding:.2rem .6rem; font-size:.9rem; background:var(--surface-2);
  border:2px solid var(--line); border-radius:999px}
.checklist{margin:0 0 1rem; padding-left:1.5rem; max-width:70ch}
.checklist li{margin-bottom:.85rem}

.form-field{margin-bottom:1.25rem; max-width:640px}
.form-field label{display:block; font-weight:700; margin-bottom:.35rem}
.form-field .hint{display:block; font-weight:500; font-size:.9rem; color:var(--ink-soft); margin-bottom:.35rem}
.form-field input,.form-field select,.form-field textarea{width:100%; min-height:52px; padding:.6rem .8rem;
  font:inherit; color:var(--ink); background:var(--surface); border:3px solid var(--line-strong); border-radius:var(--radius)}
.form-field textarea{min-height:150px; resize:vertical}
.form-field input:focus,.form-field select:focus,.form-field textarea:focus{border-color:var(--accent)}
.hp{position:absolute; left:-9999px}

/* No max-height and no object-fit: a cap meant cover cropped roughly a third of the
   picture away on a wide screen. It scales to whatever width it is given instead. */
.foot-banner{max-width:var(--wrap); margin:2rem auto 0; padding:0 1rem}
.foot-banner img{display:block; width:100%; height:auto;
  border-radius:var(--radius); border:2px solid var(--line)}
.site-foot{margin-top:1.25rem; background:var(--surface); border-top:3px solid var(--line); padding:1.35rem 0; margin-top:2rem}
/* The global p{max-width:70ch} keeps body prose readable, but footer lines are short
   labels, not prose - the cap made them wrap early and look broken on wide screens. */
.site-foot{text-align:center}
/* every footer child is centred on the same axis, whatever its type */
.site-foot p,.site-foot div{max-width:none; margin-left:auto; margin-right:auto}
.site-foot p{font-size:.98rem}
/* the how-to-save steps are a list, so they stay left-aligned inside a centred footer */
.save-help{text-align:left; margin:0 auto 1rem; max-width:48ch}
.updated{font-size:1.05rem}
/* stacked, not side by side - squeezed onto one line on a phone they were unreadable */
.save-line{display:flex; flex-direction:column; align-items:center; gap:.55rem;
  justify-content:center; margin:.85rem 0}
.foot-note{font-size:.9rem; color:var(--ink-soft); margin-bottom:.4rem}
.credit-by{font-size:.98rem; margin:0; padding-top:.75rem; border-top:2px solid var(--line)}
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; border:0}
.empty{background:var(--surface); border:2px dashed var(--line); border-radius:var(--radius); padding:1.25rem; color:var(--ink-soft)}

/* ---- live status strip ---- */

/* ---- category bar (stays put, always) ---- */
.catbar{display:grid; gap:.5rem; grid-template-columns:minmax(0,1fr); margin:.4rem 0 .9rem}
@media (min-width:360px){.catbar{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:680px){.catbar{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (min-width:890px){.catbar{grid-template-columns:repeat(4,minmax(0,1fr))}}
/* six across returns above 1140px: without icons at the Larger setting the labels fit,
   and twelve categories fall into two even rows */
@media (min-width:1140px){.catbar{grid-template-columns:repeat(6,minmax(0,1fr))}}
.catbtn{display:flex; align-items:center; gap:.35rem; min-width:0; text-decoration:none; min-height:52px; padding:.4rem .35rem; cursor:pointer;
  font:inherit; font-weight:700; font-size:.98rem; text-align:left; color:var(--ink);
  background:var(--surface); border:3px solid var(--line-strong); border-radius:var(--radius)}
@media (hover:hover){.catbtn:hover{border-color:var(--accent)}
.event-details:hover,.event-close:hover{border-color:var(--accent)}}
/* One full-width row rather than a thirteenth tile: 13 leaves a ragged last row at every
   column count, and "everything" is not a peer of the categories anyway. */
.catbtn-all{grid-column:1 / -1; justify-content:center; text-decoration:none;
  background:var(--surface-2); border-color:var(--accent); color:var(--accent)}
/* Every :hover is behind (hover:hover). A tap on a touch screen leaves the hover state
   stuck on the element until something else is tapped, and this rule paints exactly what
   the selected state paints - so "Show all businesses" still looked selected after being
   switched off. The button was fine; the styling was lying about it. */
@media (hover:hover){.catbtn-all:hover{background:var(--accent); color:var(--accent-ink)}}
.cat-icon{width:1.2rem; height:1.2rem; flex:0 0 auto; color:var(--accent)}
.catbtn[aria-current="true"] .cat-icon{color:var(--accent-ink)}
.h2-icon{display:flex; align-items:center; gap:.6rem}
.cat-icon-h{width:1.5em; height:1.5em}
/* max-width:none matters here: this is a <p>, and the global p{max-width:70ch} reading
   cap would box it to ~665px, so justify-content:center would centre inside that box
   and land visibly left of the real centre. */

.section-centered{text-align:center; max-width:none}
.section-more{display:flex; justify-content:center; margin:1.25rem 0 0; max-width:none}
.dash-links{display:flex; flex-wrap:wrap; justify-content:center; gap:.6rem; margin:.5rem 0 0; max-width:none}
/* Selection swaps the category icon for a checkmark rather than inserting one before
   the label. Adding a tick made the button wider mid-click, which wrapped the longest
   labels onto a second line and shifted the whole grid. Same footprint either way. */
.cat-mark{position:relative; flex:0 0 auto; width:1.2rem; height:1.2rem}
.cat-mark .cat-icon,.cat-mark .cat-check{position:absolute; inset:0; width:100%; height:100%}
.cat-check{display:none; color:var(--accent-ink)}
.catbtn[aria-current="true"] .cat-icon{display:none}
.catbtn[aria-current="true"] .cat-check{display:block}
html[data-text="larger"] .cat-mark{display:none}
html[data-text="larger"] .cat-label{font-size:min(.98rem, 5.4vw)}
/* one-word labels like "Contractors" cannot wrap, so the label is what has to give when
   the column is narrow; full size wherever there is room for it */
.cat-label{min-width:0; font-size:min(.98rem, 4vw)}
.catbtn[aria-current="true"]{background:var(--accent); color:var(--accent-ink); border-color:var(--accent)}
.catbtn[aria-current="true"] .tick{display:inline}

/* ---- results region ---- */
.result-group{font-size:1.15rem; margin:1.25rem 0 .6rem; padding-bottom:.3rem;
  border-bottom:2px solid var(--line)}
.result-group:first-child{margin-top:0}
.results-head{display:flex; flex-wrap:wrap; gap:.6rem .9rem; align-items:center;
  justify-content:space-between; margin-bottom:.75rem}
/* its own line at every width, left-aligned under the heading - beside the heading it
   read as a stray control rather than something that acts on the list below */
.results-tools{display:flex; align-items:center; gap:.6rem; flex:1 1 100%;
  margin-left:0; justify-content:flex-start}
.results-head .switch{min-height:40px; font-size:.95rem}
.results-head h2{margin:0}
#results-clear{min-height:48px}

/* ---- open now: green only, never red ---- */
/* Both badges carry a 2px border so they are the same size; the open one just matches
   its own fill, so it reads as solid. Without it the bordered "Closed" badge was 4px
   bigger in every direction. */
.badge-open{display:inline-block; margin-left:.5rem; padding:.15rem .6rem; border-radius:999px;
  font-size:.82rem; font-weight:800; background:var(--ok); color:#fff; white-space:nowrap;
  border:2px solid var(--ok)}
:root[data-theme="dark"] .badge-open{color:#04210f}
@media (prefers-color-scheme:dark){:root[data-theme="system"] .badge-open{color:#04210f}}
/* A badge too, so it reads as a status rather than part of the business name when the
   two share a line. Deliberately neutral, not red - being closed at 9pm is normal. */
.badge-closed{display:inline-block; margin-left:.5rem; padding:.15rem .6rem; border-radius:999px;
  font-size:.82rem; font-weight:800; color:var(--ink-soft); white-space:nowrap;
  background:var(--surface-2); border:2px solid var(--line-strong)}

@media print{.site-head,.navbar,.settings,.site-foot,.filterbar{display:none}}
@media (prefers-reduced-motion:reduce){*{animation:none!important; transition:none!important}}
