/* ══════════════════════════════════════════════════════════════════════════
   EDITORIAL LAYER — the shared premium surface
   --------------------------------------------------------------------------
   One design system for every page. The homepage, the seasonal hubs, the DIY
   page and Local Table all pull from the tokens at the top; a page that needs
   a dark palette re-declares them and everything below follows.

   The four things this fixes, in the order they were hurting:

     1. CARD LOAD. A listing card carried nine rows — photo, title, five
        stars, three coloured age chips, address, phone, date, description,
        then a footer of four buttons and two more labels. Premium listing
        cards carry three or four. Everything removed here is still in the
        DOM and still in the detail modal; it is off the CARD FACE, which is
        the only place it was doing harm.
     2. COLOUR COUNT. Orange slab, blue map button, green/red/yellow/purple
        age chips, gold stars, orange link — six hues fighting. Two now: ink
        and one accent, with a hairline for structure.
     3. EMOJI AS INTERFACE. A pin, a calendar, a map, a star and a megaphone
        in the card chrome read as a hobby project. Type and rules instead.
     4. TYPE HIERARCHY. Title, meta and chips were all a similar size and
        weight, so nothing led. A real scale now: 17/13/11.
   ══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── palette ───────────────────────────────────────────────────────────
     Two brand colours, used sparingly, over a sunset ground. The orange and
     the blue are darkened from the brand values so they can carry TEXT: the
     brand #FF9900 measures 2.2:1 on a light ground and #4FC3F7 is worse, so
     they stay for fills and artwork while these two do the talking. */
  --ed-ink:#20160E;            /* warm near-black; 13-16:1 on every ground */
  --ed-ink-2:#5C4A3A;          /* meta text; 6.5-7.9:1 */
  --ed-hair:#F0DCC4;
  /* Two accents, split by role.

     The brief asks for #C96A12 "so white button text passes AA (4.8:1)".
     White on #C96A12 is 3.78:1, so that value would not have met the bar it
     was chosen for. #AE5C0F is the same hue walked down until white clears
     it — 4.84:1 — and it is used wherever white text sits ON the colour.

     As TEXT on the page's light grounds #AE5C0F only reaches 3.77-4.53:1,
     under the 4.5:1 needed for body copy, so the darker #A8380A stays for
     links, labels and headings. One value cannot do both jobs. */
  --ed-accent:#A8380A;
  --ed-accent-fill:#AE5C0F;         /* orange — >=5.0:1 on every sunset stop */
  --ed-sky:#0F4C93;            /* sky blue — >=6.6:1 */
  --ed-accent-ink:#FFFFFF;
  --ed-card:#FFFFFF;
  --ed-radius:14px;

  /* The sunset itself: warm at the top where the sun is, cooling to sky at
     the bottom. One gradient over the whole document rather than a fixed
     layer — no compositing cost, and it never janks on iOS. */
  --ed-sunset:linear-gradient(180deg,
    #FFF6EC 0%, #FFEDD9 12%, #FFDDBE 32%, #FDE3D0 56%, #EDF2FA 82%, #E9F2FB 100%);
}

/* ── section header: a label, a hairline, a label ─────────────────────── */
.ed-sec{display:flex;align-items:center;gap:18px;margin:clamp(30px,4vw,54px) auto clamp(16px,2vw,22px)}
.ed-sec .ed-n{font-size:11px;font-weight:900;letter-spacing:.20em;text-transform:uppercase;
  color:var(--ed-ink-2);white-space:nowrap}
.ed-sec .ed-rule{height:1px;background:var(--ed-hair);flex:1}

/* ══ THE CARD ══════════════════════════════════════════════════════════ */
.ed .card{
  background:var(--ed-card)!important;
  border:1px solid var(--ed-hair)!important;
  border-radius:var(--ed-radius)!important;
  box-shadow:none!important;
  overflow:hidden;
  transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.ed .card:hover{
  transform:translateY(-3px);
  border-color:#D9CDBB!important;
  box-shadow:0 16px 34px rgba(23,19,16,.10)!important;
}

/* photo */
.ed .card-img-wrap{aspect-ratio:4/3!important;border-radius:0!important;background:#EFE9E0!important}
.ed .card-img-wrap img{transition:transform .55s ease}
.ed .card:hover .card-img-wrap img{transform:scale(1.045)}
/* The scrim only existed to hold the city pill; the pill is gone from the
   photo, so the gradient is too. */
.ed .card-scrim{display:none!important}
.ed .card-city{display:none!important}

/* one pill, top left, and it says the price — real data, not a label
   invented to match a reference */
.ed .card-img-wrap .badge-free{
  top:12px!important;left:12px!important;right:auto!important;
  background:var(--ed-accent)!important;color:var(--ed-accent-ink)!important;
  font-size:10.5px!important;font-weight:900!important;letter-spacing:.06em!important;
  text-transform:uppercase;padding:6px 11px!important;border-radius:6px!important;
  box-shadow:none!important;
}
.ed .card-img-wrap .card-heart{
  top:10px!important;right:10px!important;left:auto!important;
  background:rgba(255,255,255,.92)!important;box-shadow:0 2px 8px rgba(0,0,0,.16)!important;
}

/* body: title, then ONE meta line */
.ed .card-body{padding:15px 16px 4px!important;gap:0!important}
.ed .card-title{
  font-size:16.5px!important;font-weight:800!important;line-height:1.3!important;
  letter-spacing:-.012em!important;color:var(--ed-ink)!important;margin:0!important;
}
/* everything that made the card a wall of rows */
.ed .card-body .stars,
.ed .card-body .info{display:none!important}

/* the meta line: date, then ages as plain text rather than five coloured
   pills. The data is unchanged; it stops shouting. */
.ed .card-body .date-line{
  font-size:12.5px!important;color:var(--ed-ink-2)!important;font-weight:700!important;
  margin-top:7px!important;
}
.ed .card-body .date-line strong{color:var(--ed-ink)!important;font-weight:800!important}
.ed .card-body .chips{
  display:flex!important;flex-wrap:wrap;gap:0 10px!important;margin-top:4px!important;
}
.ed .card-body .chips .chip{
  background:none!important;border:none!important;padding:0!important;
  font-size:12px!important;font-weight:700!important;color:var(--ed-ink-2)!important;
}
.ed .card-body .chips .chip-price{color:var(--ed-ink)!important;font-weight:800!important}

/* foot: a hairline and two text links. No boxes, no icons. */
.ed .card-foot{
  padding:12px 16px 0!important;margin-top:12px!important;gap:16px!important;
  border-top:1px solid var(--ed-hair);align-items:center;
}
.ed .card-foot .btn{
  background:none!important;border:none!important;padding:0!important;
  font-size:12.5px!important;font-weight:800!important;color:var(--ed-ink-2)!important;
  min-height:44px;display:inline-flex;align-items:center;
}
.ed .card-foot .btn:hover{color:var(--ed-ink)!important}
.ed .card-meta-row{
  padding:0 16px 14px!important;border-top:none!important;
  font-size:11px!important;
}
.ed .card-src{color:#7A6E63!important;font-weight:700}
.ed .card-details{color:var(--ed-accent)!important;font-weight:900!important}

/* ── card grid alignment ────────────────────────────────────────────────────── */
/* One rule for the whole site: 3 across on desktop, 2 on tablet, 1 on phone,
   every card the same size, and every card in a row the same height.

   This used to be align-items:start above 980px, on the reasoning that a short
   card should not stretch to match a taller neighbour. In practice it left the
   row ragged - the Halloween hub ran 230/204/204px because one title wrapped to
   two lines - and ragged bottoms read as broken rather than airy. Local Table
   had already switched to stretch on its own; this makes every page agree.
   height:100% is what actually fills the row: the grid item stretches, and the
   card inside it has to follow. No effect on phones, where a row is one card. */
.grid{align-items:stretch}
.grid > .card{height:100%}

/* ── the hero ─────────────────────────────────────────────────────────── */
.ed-hero{
  position:relative;overflow:hidden;border-radius:20px;
  min-height:clamp(320px,46vw,460px);display:flex;align-items:center;
  isolation:isolate;
}
.ed-hero::after{
  content:'';position:absolute;inset:0;z-index:1;
  background:linear-gradient(94deg,
    rgba(12,9,6,.86) 0%, rgba(12,9,6,.66) 42%, rgba(12,9,6,.16) 78%, rgba(12,9,6,0) 100%);
}
@media(max-width:820px){
  .ed-hero::after{background:linear-gradient(180deg,
    rgba(12,9,6,.62) 0%, rgba(12,9,6,.82) 52%, rgba(12,9,6,.90) 100%)}
}

/* ══ THE HOMEPAGE HERO ══════════════════════════════════════════════════
   An ink panel, not a photograph. The seasonal photo path still works and
   still wins when an image is present (.hero[data-hero-img="on"]), but the
   only hero art in the repo is 913x327 — far too small for a full-bleed
   band on a 1440px screen, and upscaling it is what makes a page look
   cheap. A deep ink panel with real typographic hierarchy does not.
   The selectors are body-qualified on purpose: the rules they override live
   in index.html's inline <style>, which comes after the stylesheet links and
   therefore wins every specificity tie. Without the extra element in the
   selector the white headline applied and the dark panel behind it did not.
   ══════════════════════════════════════════════════════════════════════ */
body.ed-shell .hero{
  --ed-pad-t:clamp(30px,4vw,52px);
  --ed-pad-x:clamp(22px,3.5vw,48px);
  /* A sunset panel, not an ink one. Every colour here is light, so the hero
     text is dark: white on this would be 1.5:1. */
  /* Translucent, so the autumn backdrop reads through the hero instead of
     being sealed under it. The tint is still heavy enough that the dark
     hero text sits on a near-flat ground rather than on photograph — the
     contrast gate measures this at 390 and 1440 and would fail it
     otherwise. */
  background:linear-gradient(135deg,
    rgba(255,231,201,.88) 0%, rgba(255,214,174,.84) 36%,
    rgba(251,217,206,.82) 62%, rgba(220,234,247,.88) 100%)!important;
  border:1px solid #F3DCC0;
  border-radius:20px;
  margin:6px 16px 0;
  padding:var(--ed-pad-t) var(--ed-pad-x) clamp(26px,3vw,40px)!important;
  overflow:hidden;position:relative;isolation:isolate;
}
/* A soft low sun in the corner rather than a flat wash. */
body.ed-shell .hero::before{
  content:'';position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(58% 74% at 84% 6%, rgba(255,153,0,.34) 0%, transparent 64%),
    radial-gradient(70% 60% at 4% 96%, rgba(79,195,247,.26) 0%, transparent 62%);
}
body.ed-shell .hero > *:not(.ed-leaves){position:relative;z-index:1}

body.ed-shell .hero .hero-sub{color:#6B5647!important;font-size:12.5px;font-weight:800;
  letter-spacing:.14em;text-transform:uppercase}
body.ed-shell .hero .p-h1{color:var(--ed-ink)!important;letter-spacing:-.03em!important;
  line-height:1.04!important}
body.ed-shell .hero .p-h1 #heroLine2{color:var(--ed-accent)}
body.ed-shell .hero .p-lede{color:#4A3B2E!important}
body.ed-shell .hero .p-badge{background:rgba(255,255,255,.66)!important;
  border-color:#EFD4B4!important;color:#7A3A0B!important}
body.ed-shell .hero .p-stat b{color:var(--ed-ink)!important}
body.ed-shell .hero .p-stat.is-free b{color:var(--ed-accent)!important}
/* 4.48:1 against 4.5 once the taller header moved the hero onto a
   lighter part of the backdrop. Two hundredths short is still short. */
body.ed-shell .hero .p-stat span{color:#4A3B2E!important}
body.ed-shell .hero .p-stat-rule{background:rgba(32,22,14,.18)!important}
body.ed-shell .hero .p-wcard,body.ed-shell .hero .p-pick{
  background:rgba(255,255,255,.74)!important;border:1px solid rgba(255,255,255,.9)!important;
  box-shadow:0 6px 18px rgba(90,60,20,.10)!important}
body.ed-shell .hero .p-wlabel{color:#6B5647!important}
body.ed-shell .hero .p-wtemp,body.ed-shell .hero .p-pick-title{color:var(--ed-ink)!important}
body.ed-shell .hero .p-wnote,body.ed-shell .hero .p-pick-meta{color:#5C4A3A!important}
body.ed-shell .hero .hero-logo img{filter:none!important}

/* When a real photograph IS present the panel steps aside and the existing
   [data-hero-img="on"] rules take over. */
body.ed-shell .hero[data-hero-img="on"]{background:transparent!important;border-color:transparent}
body.ed-shell .hero[data-hero-img="on"]::before{display:none}

/* ── page shell: paper ground and calmer section furniture ────────────── */
body.ed-shell{background:var(--ed-sunset) no-repeat;background-attachment:scroll;
  background-size:100% 100%;min-height:100vh}
.ed-shell .status-bar{color:var(--ed-ink-2)!important;font-weight:800!important;
  letter-spacing:.01em!important}
.ed-shell .status-bar strong{color:var(--ed-ink)!important}
.ed-shell .geo-sec .geo-idx{color:var(--ed-ink-2)!important}
.ed-shell .geo-sec .geo-label{color:var(--ed-ink)!important}
.ed-shell .geo-sec .geo-rule{background:var(--ed-hair)!important}

/* ── the hero's own navigation ────────────────────────────────────────────
   Five buttons in five different gradients was most of the "too many
   colours" problem. On the ink panel they become quiet outlines with one
   filled call to action, which is what makes the headline read first. */
body.ed-shell .hero .hide-on-mobile > a,
body.ed-shell .hero .hide-on-mobile > button{
  background:rgba(255,255,255,.60)!important;
  border:1px solid rgba(32,22,14,.16)!important;
  color:#3A2A1A!important;
  border-radius:99px!important;
  padding:9px 16px!important;
  font-weight:800!important;
  min-height:44px;display:inline-flex;align-items:center;
  transition:background .2s ease,border-color .2s ease;
}
body.ed-shell .hero .hide-on-mobile > a:hover,
body.ed-shell .hero .hide-on-mobile > button:hover{
  background:#FFFFFF!important;border-color:rgba(32,22,14,.34)!important;
}
/* one filled action: Plan My Day */
body.ed-shell .hero .hide-on-mobile > button:first-child{
  background:var(--ed-accent)!important;border-color:transparent!important;color:#fff!important;
}

/* ── the seasonal cross-link ──────────────────────────────────────────────
   It was a saturated blue band directly under a warm page. Ink, with the
   accent doing the pointing. */
/* The dark banner these rules painted is three .way tiles now. The id
   survives only so applySeason() can show or hide the seasonal one, and an
   !important dark background on it was covering the whole tile — the tile
   text measured 2.7:1 and 2.1:1 against it. */

/* Two pictographs were left in the card foot. */
.ed .card-foot .btn[title],
.ed .card-foot button.btn{display:none!important}

/* ══ DARK PAGES ═════════════════════════════════════════════════════════
   A page that re-declares these six tokens gets the same card, section
   header and hairline system on a dark ground. No second copy of the rules.
   ══════════════════════════════════════════════════════════════════════ */
.ed-dark{
  --ed-ink:#F3F7FA;
  --ed-ink-2:#AFC0D0;
  --ed-hair:rgba(255,255,255,.13);
  --ed-paper:#0B1220;
  --ed-card:#131C29;
  --ed-accent:#FFA274;          /* lighter than the light-mode accent: the
                                   light-mode #D9560A is 2.4:1 on this ground */
  --ed-accent-ink:#2A1000;
}
.ed-dark .card:hover{border-color:rgba(255,255,255,.24)!important;
  box-shadow:0 18px 40px rgba(0,0,0,.45)!important}
.ed-dark .card-img-wrap{background:#1B2634!important}
.ed-dark .card-img-wrap .badge-free{color:var(--ed-accent-ink)!important}
.ed-dark .card-src{color:#8FA0B2!important}

/* ══ THE SEASONAL HUB CARD ══════════════════════════════════════════════
   Same system, different class names — this hub's card markup predates the
   homepage's. Mapped here rather than rewritten, so the filter code that
   reads .card[data-id] keeps working untouched.
   ══════════════════════════════════════════════════════════════════════ */
.ed-dark .card{padding:0!important;overflow:hidden}
/* NOT display:block. The slot is deliberately collapsed until a photo has
   actually loaded — forcing it open put an empty band above every title on
   any card whose photo failed, which is worse than no photo feature at all.
   scripts/test-photo-fallback.js is the gate that caught it. */
.ed-dark .card .c-img{
  margin:0!important;height:auto!important;aspect-ratio:4/3;
  background:#1B2634;position:relative}
.ed-dark .card .c-img::after{display:none!important}
.ed-dark .card h3{
  font-size:16.5px!important;font-weight:800!important;line-height:1.3!important;
  letter-spacing:-.012em!important;padding:15px 16px 0!important;color:var(--ed-ink)!important}
.ed-dark .card .c-meta{
  font-size:12.5px!important;font-weight:700!important;color:var(--ed-ink-2)!important;
  padding:7px 16px 0!important}
/* badges as plain labels, not five coloured pills */
.ed-dark .card .c-badges{gap:0 12px!important;padding:5px 16px 0!important}
.ed-dark .card .c-badges .badge{
  background:none!important;border:none!important;padding:0!important;
  font-size:12px!important;font-weight:700!important;color:var(--ed-ink-2)!important}
/* except the two that carry the warning — those keep the accent */
.ed-dark .card .c-badges .b-s3,
.ed-dark .card .c-badges .b-s4{color:var(--ed-accent)!important;font-weight:900!important}
/* description and the unverified note come off the face */
.ed-dark .card .c-note{display:none!important}
.ed-dark .card .c-unver{
  /* 11.5px is the nav-label floor, not the body-text one. */
  font-size:12px!important;font-weight:700!important;color:var(--ed-accent)!important;
  padding:8px 16px 0!important;font-style:normal!important}
/* links become a hairline row of text, matching the homepage card */
.ed-dark .card .c-links{
  margin:14px 0 0!important;padding:12px 16px!important;gap:18px!important;
  border-top:1px solid var(--ed-hair)}
.ed-dark .card .c-links a{
  background:none!important;padding:0!important;border-radius:0!important;
  font-size:12.5px!important;font-weight:800!important;min-height:44px;
  display:inline-flex;align-items:center}
.ed-dark .card .c-links .l-site{color:var(--ed-accent)!important}
.ed-dark .card .c-links .l-map{color:var(--ed-ink-2)!important}
.ed-dark .card .c-links .l-map:hover{color:var(--ed-ink)!important}
@media(min-width:980px){
  /* The lead card's photo needs the same cap here; this hub's photo element
     is .c-img, not .card-img-wrap, so the homepage rule does not reach it
     and a 4:3 photo across two columns became half a screen tall. */
  .ed-dark .grid > .card:first-child .c-img.has-img{
    aspect-ratio:auto!important;height:clamp(230px,24vw,320px)!important}
  .ed-dark .grid > .card:first-child h3{font-size:21px!important}
}

/* the geo section header on the dark ground */
.ed-dark .geo-sec .geo-idx{color:var(--ed-accent)!important}
.ed-dark .geo-sec .geo-label{color:var(--ed-ink)!important}
.ed-dark .geo-sec .geo-rule{background:var(--ed-hair)!important}


/* ══ SEASONAL HEADER ART ════════════════════════════════════════════════
   A band of paper-cut autumn leaves across the top of a hero. The source
   art arrives on a white rectangle; scripts/prepare-header-art.js floods the
   background out to alpha so it hangs over any ground, light or dark.

   It is decorative and it is a decoration only: aria-hidden, behind every
   piece of content, and the hero pads itself down by the band height so no
   text ever sits on a leaf. That is the whole reason it reads as premium
   rather than as clutter — nothing has to fight it.
   ══════════════════════════════════════════════════════════════════════ */
.ed-leaves{
  /* inset:0 is against the hero's PADDING box, which is flush with its
     border — so the band spans edge to edge with no compensation needed.
     What did break it: two `.hero > *` rules that set position:relative on
     every child. Both now exclude this element; while it was relative, the
     offsets shifted it instead of stretching it and the band stopped one
     padding-width short of each side. */
  position:absolute;top:0;left:0;right:0;z-index:0;pointer-events:none;
  height:var(--leaf-h,clamp(34px,5vw,62px));
  background-image:image-set(url("/images/header/autumn-leaves.webp") type("image/webp"),
                             url("/images/header/autumn-leaves.png") type("image/png"));
  background-repeat:repeat-x;
  background-position:center top;
  background-size:auto 100%;
}
/* The homepage hero's padding is set in a body-qualified rule above, which
   outranks the generic .has-leaves one — so the nav row sat inside the leaf
   band and its white labels measured 1.41:1 against a yellow leaf. Restated
   at the same weight. */
body.ed-shell .hero.has-leaves{
  padding-top:calc(var(--leaf-h,clamp(34px,5vw,62px)) + clamp(12px,2vw,26px))!important}

/* Content clears the band rather than competing with it. */
.has-leaves{position:relative;
  padding-top:calc(var(--leaf-h,clamp(34px,5vw,62px)) + clamp(12px,2vw,26px))!important}
/* :not(.ed-leaves) matters. Without it this rule wins the specificity tie
   and turns the band from absolute to relative, which is why it stopped one
   padding-width short of each edge. */
.has-leaves > *:not(.ed-leaves){position:relative;z-index:1}
@media(prefers-reduced-data:reduce){ .ed-leaves{display:none} }

/* ══ LOCAL TABLE HERO ═══════════════════════════════════════════════════
   The supplied food banner, cropped to the photographic region — the source
   carries another brand's headline, a Lorem ipsum block and an ORDER NOW
   button baked into the pixels, none of which can appear here.

   It sits at 22% opacity behind an ink panel rather than full strength: the
   usable crop is only 609px wide, and a low-resolution photograph shown
   large and sharp is exactly what makes a header look cheap. At this weight
   it reads as texture, and the softness never shows.
   ══════════════════════════════════════════════════════════════════════ */
.lt-hero-art{
  position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  border-radius:inherit;
}
.lt-hero-art::before{
  content:'';position:absolute;inset:0;
  background-image:image-set(url("/images/header/local-table-food.webp") type("image/webp"),
                             url("/images/header/local-table-food.jpg") type("image/jpeg"));
  background-size:cover;background-position:center;
  opacity:.42;filter:saturate(.95);
}
.lt-hero-art::after{
  content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(23,19,16,.34),rgba(23,19,16,.72));
}
@media(prefers-reduced-data:reduce){ .lt-hero-art::before{background-image:none} }

/* ══ PAGE SWITCHER (homepage, mobile) ═══════════════════════════════════
   The homepage's bottom bar is its FILTER bar — Free Camps, Paid, Outdoor
   and so on, all of which proxy into setTab(). Page links do not belong in
   it, and the header pills that do carry them are hide-on-mobile. So on a
   phone the homepage had no route to Local Table, DIY or the Halloween hub
   at all. This row is those four destinations, above the fold, phone only.
   The other three pages carry the same four in their bottom bar instead.
   ══════════════════════════════════════════════════════════════════════ */
/* NAV-02: the phone-only page pill row is gone. It duplicated the
   bottom bar's four destinations on the same screen, at the same
   breakpoint. One navigation system per screen. */
body.ed-shell .ed-pages{display:none!important}

/* ══════════════════════════════════════════════════════════════════════════
   BOTTOM NAV — one bar, five tabs, identical on every page.

   QA findings NAV-01 (seven tabs, seventh clipped mid-word at 390px),
   A11Y-01 (emoji announced literally: the Halloween tab read as
   "jack-o-lantern") and A11Y-04 (10px labels, sub-44px targets).

   Five tabs divide 390px into 78px columns, so `flex:1 1 0` fits the row
   without the horizontal scroll the seven-tab bar needed. The overflow-x
   and min-width that made it scrollable are reset here rather than deleted
   from each page's inline block, because those blocks come after this
   stylesheet in the cascade — an id-prefixed selector wins on specificity
   regardless of order.

   Icons are glyphs, not emoji: a glyph renders identically on iOS, Android
   and Windows, and is marked aria-hidden so the visible label is the only
   thing a screen reader announces.
   ══════════════════════════════════════════════════════════════════════════ */
#bottom-nav .bnav-items{
  display:flex;
  justify-content:space-between;
  overflow-x:visible;
  padding:0 2px;
}
#bottom-nav .bnav-btn{
  flex:1 1 0;
  min-width:0;
  min-height:44px;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:3px;
  padding:4px 2px;
  text-decoration:none;
  /* #aaa on white measured 2.32:1. This is 5.4:1 and still reads as the
     unselected state next to the accent. */
  color:#6A6158;
}
#bottom-nav .bnav-icon{
  /* Nunito carries none of these glyphs, so name the symbol fonts
     explicitly instead of letting each platform pick its own fallback. */
  font-family:"Segoe UI Symbol","Apple Symbols","Noto Sans Symbols 2",
              "DejaVu Sans",Nunito,sans-serif;
  font-style:normal;
  font-size:1.25rem;
  line-height:1;
}
#bottom-nav .bnav-label{
  /* 10px -> 11.5px, the floor the QA doc sets for tab labels. */
  font-size:11.5px;
  font-weight:700;
  line-height:1.15;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
  max-width:100%;
}
/* Selected state is not colour alone (A11Y-03): the active tab also carries
   a rule above it, which survives greyscale and bright sunlight. */
#bottom-nav .bnav-btn.active{
  color:#8a5200;
  font-weight:800;
  box-shadow:inset 0 2px 0 0 currentColor;
}

/* ══════════════════════════════════════════════════════════════════════════
   FILTER CHIPS — A11Y-03 and A11Y-04.

   Selected state was signalled by colour alone, which disappears under
   colour vision deficiency and in bright sun — the two conditions a parent
   at a park is most likely to be in. Every selected chip now also carries a
   checkmark and a 2px border, so the state survives greyscale.

   Chips measured ~30px tall against a 44px minimum. Raised here rather than
   in each page's inline block so all four pages move together.
   ══════════════════════════════════════════════════════════════════════════ */
body.ed-shell .city-btn,
body.ed-shell .age-btn{
  min-height:44px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:6px;
  border:2px solid transparent;
}
body.ed-shell .city-btn.on::before,
body.ed-shell .age-btn.on::before,
body.ed-shell .tab-btn.on .tab-check{
  content:"\2713";
  font-weight:900;
  font-size:.85em;
  line-height:1;
}
body.ed-shell .city-btn.on,
body.ed-shell .age-btn.on{
  border-color:currentColor;
}
/* The desktop category tabs get the same treatment. .tab-btn.on already
   lifts and casts a shadow; the border is what survives a greyscale print. */
body.ed-shell .tab-btn{border:2px solid transparent}
body.ed-shell .tab-btn.on{border-color:rgba(0,0,0,.32)}

/* Mobile city/age sheets — same rule, different markup. */
body.ed-shell .mob-city-opt,
body.ed-shell .mob-age-opt,
body.ed-shell .dtcr,
body.ed-shell .dtec{
  min-height:44px;
  display:flex;
  align-items:center;
  justify-content:center;
  border-width:2px;
  border-style:solid;
}
body.ed-shell .mob-city-opt.on::before,
body.ed-shell .mob-age-opt.on::before,
body.ed-shell .dtcr.on::before,
body.ed-shell .dtec.on::before{
  content:"\2713\00a0";
  font-weight:900;
}

/* The Free / Paid chip pair is gone: those two filters live in the
   header's Explore control, and nowhere else. */

/* ══════════════════════════════════════════════════════════════════════════
   PAGE BACKDROP — the autumn river behind home, Local Table and DIY.

   Same construction as the Halloween hub's haunted house: a position:fixed
   layer, NOT background-attachment:fixed, which janks badly on iOS Safari.

   The photograph sits at low opacity over the sunset gradient rather than
   replacing it, so the ground stays the warm orange-and-sky the brand asks
   for and the scene reads as texture in the margins. That is deliberate and
   not timidity: every page here puts dark text straight onto this ground,
   and a photograph at full strength gives contrast that varies pixel to
   pixel — the exact defect A11Y-02 was raised against on the Halloween
   hero. The card grid stays legible because it sits on white cards above
   this layer.

   z-index:-1 keeps it behind everything in normal flow while still painting
   over body's own background.
   ══════════════════════════════════════════════════════════════════════════ */
/* Why body's background is cleared here.

   index.html carries `html,body{background:...!important}`. The root
   element's background paints first, but BODY's background is an ordinary
   block background, and block backgrounds paint ABOVE negative-z-index
   children of the root stacking context. So while both carried the ground,
   this layer was painted and then covered — at any opacity, including 1.
   html keeps the ground; body must not repaint it. Specificity, not order,
   is what beats the !important rule: 0,1,1 against its 0,0,1. */
body.ss-bg{
  background-image:none!important;
  background-color:transparent!important;
}
/* Local Table and DIY wrap their content in an opaque .page, which covered
   the layer just as body's background did. Cards and panels inside keep
   their own grounds, so clearing this one costs no contrast — the gate
   checks all four pages at both widths. */
body.ss-bg > .page{background:rgba(247,240,231,.62)!important}
/* On the two pages whose backdrop IS their subject, the shell only needs to
   soften the photograph, not hide it. Every text run over it is still gated
   by scripts/check-page-contrast.js at 1440 and 390. */
body.ss-bg[data-page="diy"] > .page,
body.ss-bg[data-page="local-table"] > .page{background:rgba(247,240,231,.30)!important}
.ss-backdrop{
  position:fixed;
  inset:0;
  z-index:-1;
  pointer-events:none;
  /* Two layers, top first: the sunset tint that keeps text legible, then
     the photograph under it. Putting the tint here rather than on body is
     what lets the photograph be seen at all. */
  /* Tint raised now that the content sits in a 1048px column: with wide
     margins the photograph was the loudest thing on the screen and the
     column read as floating on top of a holiday snap. It should be
     texture behind the page, not a competitor to it. */
  background-image:
    linear-gradient(180deg,
      rgba(255,246,236,.90) 0%, rgba(255,237,217,.88) 12%, rgba(255,221,190,.86) 32%,
      rgba(253,227,208,.87) 56%, rgba(237,242,250,.90) 82%, rgba(233,242,251,.92) 100%),
    image-set(
      url("/images/seasonal/autumn-river.webp") type("image/webp"),
      url("/images/seasonal/autumn-river.jpg")  type("image/jpeg"));
  background-repeat:no-repeat,no-repeat;
  background-position:center,center 28%;
  background-size:100% 100%,cover;
}
@media(max-width:700px){
  /* A portrait viewport shows a thin vertical slice of the wide crop, which
     is mostly water. The tall crop keeps the maple and the river. */
  .ss-backdrop{
    background-image:
      linear-gradient(180deg,
        rgba(255,246,236,.90) 0%, rgba(255,237,217,.88) 12%, rgba(255,221,190,.86) 32%,
        rgba(253,227,208,.88) 56%, rgba(237,242,250,.91) 82%, rgba(233,242,251,.93) 100%),
      image-set(
        url("/images/seasonal/autumn-river-tall.webp") type("image/webp"),
        url("/images/seasonal/autumn-river-tall.jpg")  type("image/jpeg"));
    background-position:center,center top;
  }
}
/* ── the page's own subject, not the generic season ────────────────────
   The Halloween hub reads instantly because its backdrop is its subject: a
   haunted house behind a Halloween page. DIY and Local Table were both
   showing the shared autumn river under a .86-.90 tint, so the page never
   said what it was about. DIY now carries its own photograph — the painted
   rocks — at a lighter tint, so the subject is legible behind the content.

   The image is the page's existing hero art, already fetched by the time
   this paints, so the backdrop costs no extra bytes.

   Local Table carries the same treatment with the food banner. Its hero is
   an opaque ink panel (see the page's own .lt-hero rules), so the backdrop
   passes behind it rather than through it. */
body.ss-bg[data-page="diy"] .ss-backdrop{
  background-image:
    linear-gradient(180deg,
      rgba(255,246,236,.58) 0%, rgba(255,240,222,.52) 18%, rgba(255,231,203,.50) 40%,
      rgba(253,231,214,.58) 64%, rgba(250,240,232,.70) 86%, rgba(248,242,236,.78) 100%),
    url("/images/diy-rocks-hero.jpg");
  background-position:center,center 22%;
}
body.ss-bg[data-page="local-table"] .ss-backdrop{
  background-image:
    linear-gradient(180deg,
      rgba(255,246,236,.58) 0%, rgba(255,240,222,.52) 18%, rgba(255,231,203,.50) 40%,
      rgba(253,231,214,.58) 64%, rgba(250,240,232,.70) 86%, rgba(248,242,236,.78) 100%),
    image-set(url("/images/header/local-table-food.webp") type("image/webp"),
              url("/images/header/local-table-food.jpg")  type("image/jpeg"));
  background-position:center,center 30%;
}
@media(max-width:700px){
  /* A portrait viewport crops hard; anchor on the subject rather than the
     middle of a landscape frame, same reasoning as the seasonal tall crop. */
  body.ss-bg[data-page="diy"] .ss-backdrop,
  body.ss-bg[data-page="local-table"] .ss-backdrop{background-position:center,center top}
}

/* Do not spend a visitor's data on decoration they have asked us not to
   send. Same rule the Halloween backdrop follows. */
@media(prefers-reduced-data:reduce){
  /* Drop the photograph, keep the ground — body no longer paints one. */
  .ss-backdrop,
  body.ss-bg[data-page="diy"] .ss-backdrop,
  body.ss-bg[data-page="local-table"] .ss-backdrop{background-image:var(--ed-sunset)}
}

/* ══════════════════════════════════════════════════════════════════════════
   HEADER — logo, one search input, four nav items.

   The search bar carries the filters instead of a separate row below it.
   Collapsed it is a single line: a caption of the filters currently applied,
   a placeholder, and a round accent button. Focus anywhere inside opens the
   panel.

   The open/close is :focus-within, so there is no script and no state to get
   out of sync. It only works because every control in the panel is a real
   button — a div with an onclick cannot hold focus, and the panel would shut
   the instant you reached for a filter.
   ══════════════════════════════════════════════════════════════════════════ */
/* Scoped to the home page. Local Table and DIY use .hdr too, for a
   full-bleed sticky bar of their own — unscoped, this max-width and
   padding shrank both of them to 1200px with auto margins, which is what
   made them read as white slabs floating over the backdrop. */
body.ed-shell .hdr{
  display:flex;
  align-items:center;
  gap:clamp(10px,2vw,20px);
  flex-wrap:wrap;
  max-width:1200px;
  margin:0 auto 10px;
  padding:0 4px;
}
.hdr-logo{flex:0 0 auto;display:block;line-height:0}
.hdr-logo img{height:clamp(42px,6vw,54px);width:auto;display:block}

/* The rainbow edge is the wrapper's background, with the white bar sitting
   on top of it inset by the wrapper's own padding.

   The first attempt used a ::before at z-index:-1. Within a stacking
   context an element's background paints FIRST and negative-z children
   paint after it, so the gradient landed on top of the white fill and the
   whole bar came out rainbow with the labels tinted. Same painting-order
   trap as the page backdrop.

   The fill stays white: a rainbow behind these labels fails contrast at
   every stop, and they are the four controls the page runs on. */
.hsearch{
  position:relative;width:100%;
  padding:3px;
  border-radius:999px;
  background:linear-gradient(95deg,
    #E4572E 0%, #F2A93B 20%, #6FBF73 44%, #2E9BD6 66%, #7A5FBF 86%, #E4572E 100%);
  box-shadow:0 3px 16px rgba(70,45,10,.14);
}

/* Its own full-width row, directly under the hero. It carried a 1200px
   max-width from when it lived inside the header; as a top-level block it
   joins the same 1048px column as everything else, which qa-layout's
   one-left-edge assertion requires. */
.hsearch-row{
  max-width:var(--shell,1048px);
  margin:clamp(14px,2vw,22px) auto clamp(4px,1vw,10px);
  padding:0;
}

/* The bar shows every filter at rest — no panel, no hover, no focus needed
   to see what is applied. Same shape as the Halloween hub's bar so the two
   pages read as one product. */
/* The rainbow edge is a gradient painted behind the bar and revealed by a
   3px inset, not a border-image: border-image cannot follow a 999px radius,
   and a gradient border on a pill otherwise squares off at the ends.

   The fill stays white. A rainbow behind the labels would fail contrast at
   every stop, and these are the four controls the whole page runs on. */
.hsearch-bar{
  display:flex;
  align-items:stretch;
  background:#fff;
  border:none;
  border-radius:999px;
  padding:5px 5px 5px 6px;
  min-height:60px;
}
.hseg{position:relative;min-width:0;display:flex;flex:1 1 0}
.hseg-search{flex:1.8 1 0}
.hseg-explore{flex:1.1 1 0}
.hseg > button,.hseg-search{
  width:100%;min-width:0;min-height:48px;
  display:flex;flex-direction:column;justify-content:center;gap:1px;
  padding:4px 14px;border:none;background:none;text-align:left;
  font-family:Nunito,sans-serif;border-radius:999px;cursor:pointer;
}
.hseg-search{cursor:text}
.hseg > button:hover{background:#FFF8F0}
.hseg > button:focus-visible,.hseg-search:focus-within{
  outline:3px solid #0F4C93;outline-offset:-2px;border-radius:999px;
}
.hseg-l{
  font-size:12px;font-weight:900;letter-spacing:.07em;text-transform:uppercase;
  color:#6A5B4B;line-height:1.1;
}
.hseg-v{
  font-size:13.5px;font-weight:700;color:var(--ed-ink,#20160E);line-height:1.2;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;
}
.hseg-search input{
  border:none;outline:none;background:none;width:100%;min-width:0;padding:0;
  font-family:Nunito,sans-serif;font-size:13.5px;font-weight:700;
  color:var(--ed-ink,#20160E);line-height:1.2;
}
.hseg-search input::placeholder{color:#8A7A68;font-weight:600}
.hseg-div{width:1px;background:var(--ed-hair,#F0DCC4);margin:10px 0;flex:0 0 auto}

.hsearch-acts{display:flex;align-items:center;gap:6px;padding-left:6px;flex:0 0 auto}
.hsearch-go,.hsearch-wx{
  width:48px;height:48px;min-height:44px;border:none;border-radius:50%;
  cursor:pointer;display:inline-flex;align-items:center;justify-content:center;
  font-size:19px;
  font-family:"Segoe UI Symbol","Apple Symbols","Noto Sans Symbols 2","DejaVu Sans",sans-serif;
}
.hsearch-go{background:var(--ed-accent-fill,#AE5C0F);color:#fff}
.hsearch-wx{background:#FFF1DE;color:#8A5200;border:1px solid var(--ed-hair,#F0DCC4)}
.hsearch-go:focus-visible,.hsearch-wx:focus-visible{outline:3px solid #0F4C93;outline-offset:2px}

/* The dropdowns hang off their own segment. */
.hseg > div[id^="ssd-"]{left:0;right:auto;min-width:250px}

@media(max-width:720px){
  /* Four segments will not fit a phone in one line, so the bar becomes a
     card. Every row and column is placed explicitly: auto-placement put
     Explore off the grid entirely and stretched the buttons down a whole
     column. */
  .hsearch{border-radius:24px}
  .hsearch-bar{
    border-radius:22px;
    display:grid;
    grid-template-columns:1fr 1fr auto;
    grid-template-rows:auto auto auto;
    gap:8px;
    padding:10px;
    align-items:stretch;
  }
  .hseg-search{grid-column:1 / 3;grid-row:1}
  /* Placed by class, not :nth-of-type — the hairline dividers are divs
     too, so nth-of-type counted them and Explore fell off the grid. */
  /* Five segments now, so row 3 is a pair like row 2. Explore was still on
     1/-1 from the four-segment version, which pushed Cost to a fourth row
     on its own. */
  .hseg-city{grid-column:1;grid-row:2}
  .hseg-age{grid-column:2;grid-row:2}
  .hseg-cost{grid-column:1;grid-row:3}
  .hseg-explore{grid-column:2;grid-row:3}
  .hsearch-acts{
    grid-column:3;grid-row:1 / 4;
    flex-direction:column;justify-content:center;padding-left:0;
  }
  .hseg-div{display:none}
  .hseg > button,.hseg-search{
    border:1px solid var(--ed-hair,#F0DCC4);border-radius:14px;background:#FFFCF7;
    min-height:52px;
  }
  .hsearch-go,.hsearch-wx{width:44px;height:44px;font-size:17px}
  .hseg > div[id^="ssd-"]{min-width:0;left:0;right:0}
}

/* ── Nav: four items, active carries the accent AND a 2px rule ─────────── */
.hdr-nav{flex:1 1 100%;display:flex;gap:clamp(6px,1.6vw,22px);flex-wrap:wrap;align-items:center}
.hdr-nav a{
  min-height:44px;display:inline-flex;align-items:center;
  padding:0 2px;
  font-family:Nunito,sans-serif;font-size:14px;font-weight:800;
  color:var(--ed-ink-2,#5C4A3A);text-decoration:none;
  border-bottom:2px solid transparent;
}
.hdr-nav a:hover{color:var(--ed-ink,#20160E)}
.hdr-nav a.is-here{
  color:var(--ed-accent,#A8380A);
  border-bottom-color:var(--ed-accent,#A8380A);
}
.hdr-nav a:focus-visible{outline:3px solid #0F4C93;outline-offset:3px}

/* Logo left, nav right — the whole row is only those two now. */
body.ed-shell .hdr{justify-content:space-between}
.hdr-nav{flex:0 1 auto;justify-content:flex-end}

/* Selected options inside the header panel carry a checkmark and a 2px
   border, never colour alone. .ssac already used an outline for its on
   state; the mark is what survives greyscale and bright sun. */
.ssac.on::before,.ssec.on::before,.sscr[aria-selected="true"]::before{
  content:"\2713\00a0";font-weight:900;
}
.ssac.on,.ssec.on{border-color:currentColor!important}

/* Leading round icon on every activity card. Decorative — alt="" and
   aria-hidden, so a screen reader reads the title and nothing else. The
   title becomes the flex row rather than adding a wrapper, so no existing
   selector that targets .card-title changes meaning. */
.card-title{display:flex;align-items:center;gap:10px}
.card-ico{
  flex:0 0 auto;
  width:28px;height:28px;
  border-radius:50%;
  object-fit:cover;
  display:block;
}

/* ══════════════════════════════════════════════════════════════════════════
   TYPE AND TARGET FLOORS — enforced by scripts/qa-a11y.js.

   Nothing under 12px except nav labels, which floor at 11.5px, and 44px on
   the short side of anything you can tap. Both are set here, in one place
   across all four pages, rather than chased through each page's own block:
   the gate walks every visible element, so a rule that only half-lands
   fails the build.

   These are the classes the gate actually found, not a guess at what might
   be small.
   ══════════════════════════════════════════════════════════════════════════ */
.eyebrow,.stat-l,.fbar-l,.deals-tag,.badge,.chip-loc,.tag,.tile-label,
.badge-diff,.badge-play,.live,.seg-l,.bar-hint,.tile-kicker,.geo-idx,
.geo-label,.geo-pause,.p-wlabel,.p-wnote,.c-unver,.tier-flag,.sscs,
.hh-stats dd,.card-foot,.c-links{
  font-size:12px!important;
}
.bnav-label{font-size:11.5px!important}

/* Tap targets. A logo, a nav link and a save button are all things a thumb
   has to hit; 27-40px is not enough for any of them. */
.hdr-logo,.brand,.navlink,.navcta,.geo-pause,
.fbar-seg,.age-pill,.chip-btn,.sens-btn,.c-links a,
.hdr-nav a,.tile a,.save-btn,[class*="save"]{
  min-height:44px;
}
.geo-pause{min-width:44px;display:inline-flex;align-items:center;justify-content:center}
.hdr-logo,.brand{display:inline-flex;align-items:center}
.navlink,.navcta{display:inline-flex;align-items:center}
/* The remaining sub-44px controls the gate named: heart buttons on the DIY
   and Local Table cards, the pill filter sets, and the header links on
   those two pages. */
.heart,.pill,.fbar-seg,.navlink,.navcta,.hdr-nav a,.mob-city-opt,.mob-age-opt{
  min-height:44px;min-width:44px;
  display:inline-flex;align-items:center;justify-content:center;
}
.pill{padding-left:14px;padding-right:14px}
/* The last of the sub-12px text the gate named, by the path it reported:
   the hero badge and stat captions, the ticker items, the footer column
   headings and the unverified-listing note on hub cards. */
.p-badge,.p-badge span,#pHeroBadge,
.p-foot-col b,
.geo-ticker .geo-track span,
.c-unver{
  font-size:12px!important;
}
/* Footer links and buttons, the Local Table and DIY brand lockups, the hub's
   footer and its Clear-all control — the last targets the gate named. The
   selectors are deliberately specific: a blanket `a{min-height:44px}` would
   break inline links inside paragraphs. */
.p-foot-col a,.p-foot-col button,
.p-foot-cols a,.p-foot-cols button,
.hh-foot a,.hh-foot button,.reset,
.brand,a.brand,.hdr .brand,
.ftr a,.ftr-note a{
  min-height:44px;
  display:inline-flex;
  align-items:center;
}
/* .c-unver is set with !important on the hub; match it. */
p.c-unver{font-size:12px!important}
/* These three lost to each page's own inline <style>, which the browser
   parses after this linked file and which wins on equal specificity.
   !important is the honest fix: the floor is not negotiable per page. */
.p-foot-col a,.p-foot-col button,
.ftr-links a,.bar-foot .reset,.reset{
  min-height:44px!important;
  min-width:44px!important;
  display:inline-flex!important;
  align-items:center!important;
}
.p-foot-col a,.p-foot-col button{justify-content:flex-start!important}
.ftr-links a,.reset{justify-content:center!important}
/* Logo links are navigation too — both of these were 38-40px on their short
   side. The image inside keeps its own size; the link gets the target. */
.hh-top > a,.hdr .brand,.brand,.hdr-logo{
  min-height:44px!important;min-width:44px!important;
  display:inline-flex!important;align-items:center!important;justify-content:center!important;
}

/* Local Table and DIY header nav — same treatment as the home and hub navs:
   accent plus a 2px bottom rule on the current page, and one style for the
   four cross-page items rather than three pills and an orange CTA. */
.navlink.navlink-cross{
  border:none!important;border-bottom:2px solid transparent!important;
  border-radius:0!important;background:none!important;
  padding:0 2px!important;min-height:44px;
  font-weight:800;
}
.navlink.navlink-cross.is-here{
  color:var(--ed-accent,#A8380A)!important;
  border-bottom-color:var(--ed-accent,#A8380A)!important;
}
.nav-cross{display:flex;gap:clamp(8px,1.6vw,20px);align-items:center}
.nav-page{display:flex;gap:8px;align-items:center}
@media(max-width:640px){
  /* The bottom bar carries the four on a phone. */
  .nav-cross{display:none}
}

/* The marquee edge fade is left to each page.

   Widening it here made every label under the fade partly transparent, so
   two Local Table tile labels dropped to 4.1:1 and 3.0:1 — the mask applies
   to the band's content, not just its edges, and contrast under it cannot
   be guaranteed. The tile labels keep the opaque chips added alongside;
   those are worth having either way.
   (Local Table's band hard-clips, DIY's fades 60px. Both are legible.) */

/* The same rainbow edge on the other two pages' search controls, so the
   three read as one component. Wrapper background plus inset fill, for the
   painting-order reason noted on .hsearch above. */
.fbar,
.search-wrap{
  position:relative;
  padding:3px!important;
  border:none!important;
  border-radius:999px;
  background:linear-gradient(95deg,
    #E4572E 0%, #F2A93B 20%, #6FBF73 44%, #2E9BD6 66%, #7A5FBF 86%, #E4572E 100%)!important;
  box-shadow:0 3px 16px rgba(70,45,10,.14);
}
/* The inner surface each one needs, since the wrapper now owns the edge. */
.fbar > *,
.search-wrap > *{position:relative;z-index:1}
.fbar::after,
.search-wrap::after{
  content:"";position:absolute;inset:3px;border-radius:inherit;
  background:#fff;z-index:0;
}

/* .p-stat's caption measured 4.48:1 against 4.5 once the taller header
   moved the hero down onto a lighter part of the backdrop. Two hundredths
   short is still short. */
.p-stat span{font-size:12px!important;color:#4A3B2E!important}

/* ══════════════════════════════════════════════════════════════════════════
   ONE CONTENT COLUMN, ON ALL FOUR PAGES.

   This is the difference between the Halloween hub and the other three, and
   it is measurable rather than a matter of taste. On the hub every block
   shares one pair of edges: .wrap sits at 180-1260 on a 1440 viewport and
   all seven sections at 196-1244. On the home page nothing was contained at
   all — the hero ran 1408px wide, and the ticker, cost chips, card grid and
   footer all ran the full 1440 edge to edge. Local Table and DIY inset only
   their hero and let everything else bleed. Four different left edges on one
   screen is what "messy" looks like.

   Two rules, not one, because full-bleed surfaces are not a mistake:
     - blocks that ARE cards get a max-width and centre
     - bands that carry a background across the viewport (sticky header,
       marquee, footer) keep the bleed and inset their CONTENT to the same
       column, with padding that collapses to a sane gutter on a phone
   ══════════════════════════════════════════════════════════════════════════ */
:root{ --shell:1048px; --shell-gutter:16px; }

/* — cards: constrain the box — */
body.ed-shell > .hero,
body.ed-shell > .city-fact,
body.ed-shell > .ed,
.page > section.hero,
.page > section.bar-wrap,
.page > section.filters,
.page > section.deals,
.page > section.projects,
.page > section.sec{
  max-width:var(--shell);
  margin-left:auto;
  margin-right:auto;
}

/* — bands: keep the bleed, inset the content — */
body.ed-shell > .geo-ticker-wrap,
body.ed-shell > .footer,
.page > .band,
.page > .ftr,
.page > .hdr{
  padding-left:max(var(--shell-gutter), calc((100% - var(--shell)) / 2))!important;
  padding-right:max(var(--shell-gutter), calc((100% - var(--shell)) / 2))!important;
}
/* The marquee is meant to run off both edges, so its track keeps the bleed
   while the band around it holds the column. */
.page > .band{padding-left:0!important;padding-right:0!important}

/* ══════════════════════════════════════════════════════════════════════════
   SEASONAL HERO PHOTOGRAPH, UNDER THE EDITORIAL HERO.

   index.html's [data-hero-img="on"] rules belong to the hero this design
   replaced: a navy ground, a heavy scrim and white text. That path had
   never actually run, because hero-fall.jpg did not exist — the first time
   a season's photograph landed it would have flipped the headline to white
   over a pale sunset sky and made the hero panel transparent, taking the
   logo, the nav and the search bar's ground with it.

   So the photograph goes UNDER the sunset gradient instead of replacing it.
   The gradient is already translucent, so the sky reads as warmth and
   texture behind the panel, and not one text colour changes.
   ══════════════════════════════════════════════════════════════════════════ */
body.ed-shell .hero[data-hero-img="on"]{
  /* top layer first: the panel's own tint, then the season's sky. */
  background-image:
    linear-gradient(135deg,
      rgba(255,231,201,.88) 0%, rgba(255,214,174,.84) 36%,
      rgba(251,217,206,.82) 62%, rgba(220,234,247,.88) 100%),
    var(--hero-bg, none)!important;
  background-size:auto, cover!important;
  background-position:center, center 42%!important;
  background-repeat:no-repeat, no-repeat!important;
  background-color:#FFF6EC!important;
  border-color:var(--ed-hair,#F0DCC4);
}
/* The dark text stays dark. Every one of these is white in index.html. */
body.ed-shell .hero[data-hero-img="on"] .p-h1{color:var(--ed-ink,#20160E)!important}
body.ed-shell .hero[data-hero-img="on"] .p-h1 #heroLine2{color:var(--ed-accent,#A8380A)!important}
body.ed-shell .hero[data-hero-img="on"] .p-stat b{color:var(--ed-ink,#20160E)!important}
body.ed-shell .hero[data-hero-img="on"] .p-lede{color:#4A3B2E!important}
body.ed-shell .hero[data-hero-img="on"] .hero-sub{color:#6B5647!important}
body.ed-shell .hero[data-hero-img="on"] .p-stat span{color:#4A3B2E!important}
body.ed-shell .hero[data-hero-img="on"] .p-stat-rule{background:rgba(0,0,0,.09)!important}
/* The halo exists to lift a dark logo off a dark photo; on this ground it
   just smears. */
body.ed-shell .hero[data-hero-img="on"] .hdr-logo img,
body.ed-shell .hero[data-hero-img="on"] .hero-logo img{filter:none!important}

/* ══════════════════════════════════════════════════════════════════════════
   THREE WAYS IN — replaces the dark promo banner.

   Card treatment across the site moves to the shape a listing site uses:
   the photograph carries the weight, the border goes, and the card lifts on
   hover instead of sitting inside a box. A hairline is kept at rest so the
   cards still read as separate objects on a photographic ground.
   ══════════════════════════════════════════════════════════════════════════ */
.ways{
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:clamp(12px,1.6vw,20px);
  margin:clamp(18px,2.4vw,28px) 0 0;
}
.way{
  /* display:flex, because applySeason() sets it inline on the first tile. */
  display:flex;
  flex-direction:column;
  background:var(--ed-card,#fff);
  border:1px solid var(--ed-hair,#F0DCC4);
  border-radius:16px;
  overflow:hidden;
  text-decoration:none;
  text-align:left;
  color:inherit;
  font-family:Nunito,sans-serif;
  padding:0;
  cursor:pointer;
  transition:transform .18s ease, box-shadow .18s ease;
}
.way:hover{transform:translateY(-3px);box-shadow:0 12px 28px rgba(70,45,10,.14)}
.way:focus-visible{outline:3px solid #0F4C93;outline-offset:3px}
.way-art{
  display:block;
  height:clamp(124px,13vw,150px);
  background:#2A1E14 center/cover no-repeat;
  flex:0 0 auto;
}
.way-art-hw{background-image:url("/images/halloween/haunted-house.jpg")}
.way-art-free{background-image:url("/park.jpg")}
.way-art-diy{background-image:url("/stem.jpg")}
.way-b{display:flex;flex-direction:column;gap:5px;padding:14px 16px 16px}
.way-k{
  font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ed-accent,#A8380A);
}
.way-b b{font-size:17px;font-weight:800;line-height:1.28;color:var(--ed-ink,#20160E)}
.way-b > span:last-child{font-size:13.5px;font-weight:600;color:var(--ed-ink-2,#5C4A3A);line-height:1.5}

@media(max-width:900px){ .ways{grid-template-columns:1fr 1fr} }
@media(max-width:620px){ .ways{grid-template-columns:1fr} }
@media(prefers-reduced-motion:reduce){
  .way{transition:none}
  .way:hover{transform:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   CARD TREATMENT — the listing-site shape.

   The photograph carries the card, the frame gets out of the way: a hairline
   at rest so cards still read as separate objects on a photographic ground,
   a lift on hover instead of a heavier box, and the meta line stepped down
   from the title so the two do not compete. Restrained on purpose — the
   accent is already spent on the search bar's edge.
   ══════════════════════════════════════════════════════════════════════════ */
.ed .card{
  border:1px solid var(--ed-hair,#F0DCC4);
  border-radius:16px;
  overflow:hidden;
  transition:transform .18s ease, box-shadow .18s ease;
}
.ed .card:hover{transform:translateY(-3px);box-shadow:0 12px 28px rgba(70,45,10,.14)}
.ed .card:focus-within{box-shadow:0 0 0 3px rgba(15,76,147,.35)}
.ed .card-body{padding:14px 16px 16px;gap:6px}
.ed .card-title{font-size:15.5px;font-weight:800;line-height:1.3}
.ed .card-foot{padding:12px 16px 14px}
@media(prefers-reduced-motion:reduce){
  .ed .card{transition:none}
  .ed .card:hover{transform:none}
}

/* Section rules get more air, so the page breathes between blocks rather
   than running one grid straight into the next. */
.ed .geo-sec,.ed-sec{margin-top:clamp(28px,3.4vw,46px)}

/* The Cost listbox. The other three dropdowns carry inline styles from the
   original bar; this one is classed, so it needs the same treatment here —
   without it the panel rendered in flow inside the segment and squashed the
   button to 28px, which qa-a11y caught. */
.ss-dd{
  display:none;
  position:absolute;
  top:calc(100% + 8px);
  left:0;
  min-width:230px;
  background:#fff;
  border:1px solid var(--ed-hair,#F0DCC4);
  border-radius:16px;
  box-shadow:0 12px 34px rgba(70,45,10,.18);
  padding:12px;
  z-index:9999;
}
.ss-dd-h{
  font-size:12px;font-weight:900;letter-spacing:.07em;text-transform:uppercase;
  color:#6A5B4B;margin-bottom:10px;
}
.ss-dd-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ss-dd .ssec{
  color:#7A3F0D;background:#FFFBF5;border:2px solid var(--ed-hair,#F0DCC4);
}
.ss-dd .ssec:hover{border-color:var(--ed-accent,#A8380A)}
@media(max-width:720px){ .ss-dd{left:0;right:0;min-width:0} }

/* The three ways-in tiles are a top-level block, so they join the column. */
body.ed-shell > .ways{
  max-width:var(--shell);
  margin-left:auto;margin-right:auto;
  padding-left:var(--shell-gutter,16px);
  padding-right:var(--shell-gutter,16px);
}
@media(min-width:1081px){ body.ed-shell > .ways{padding-left:0;padding-right:0} }
