/* ==========================================================================
   fallslocal.com -- Sioux Falls business directory
   Operated by ZVARIO LLC, a South Dakota company.

   No framework. No build step. One stylesheet. Mobile-first. No JavaScript
   anywhere on the site, and nothing in this file assumes any.

   ⛔ THE DESIGN BRIEF (2026-07 redesign): commit to the hero. A thin white
   header bar; a full-bleed photographic hero taking ~85% of the fold with
   huge centered type and the search bar as the visual centerpiece; then a
   QUIET centered welcome block on plain paper. Loud, then quiet -- that
   contrast is the design. The type scale is the other half of it: the hero
   H1 runs to 72px against an 18px body, which is what makes the page read as
   designed rather than defaulted.

   The one local colour cue that is NOT invented: SIOUX QUARTZITE. The falls
   the city is named for run over pink quartzite, every historic building
   downtown is faced in it, and it is in all five photographs. Deep quartzite
   (--quartz-d) is the accent -- the solid search button, card hovers. Pine
   green stays for links and the footer.

   ==========================================================================
   ⛔ WCAG AA CONTRAST -- COMPUTED, NOT EYEBALLED.
   Ratios below are calculated with the WCAG 2.1 relative-luminance formula
   (scratchpad/wcag.py; reproducible from the hex values here).
   AA requires 4.5:1 for body text, 3:1 for large text (>=24px, or >=18.66px
   bold). Every pair used in this file:

   --- base surfaces ---
     --ink    #1a1d21  on #ffffff (header)  = 16.91:1  AAA  (header nav, buttons)
     --ink    #1a1d21  on --paper #fdfcfa   = 16.50:1  AAA  (body text)
     --muted  #5a6068  on --paper #fdfcfa   =  6.19:1  AA   (meta, captions)
     --muted  #5a6068  on #ffffff           =  6.35:1  AA   (placeholder, welcome sub)
     --link   #0b5d51  on --paper #fdfcfa   =  7.59:1  AAA  (links)
     --link-h #083f37  on --paper #fdfcfa   = 11.53:1  AAA  (link hover)
     --ink    #1a1d21  on --panel #f2efe9   = 14.74:1  AAA  (text in panels)
     --muted  #5a6068  on --panel #f2efe9   =  5.53:1  AA   (meta in panels)
     --paper  #fdfcfa  on --pine  #12241f   = 15.78:1  AAA  (footer)
     --pine-t #cfe3dd  on --pine  #12241f   = 12.09:1  AAA  (footer links)
     #ffffff           on --quartz-d #7a2f3e =  9.12:1  AAA  (search button)
     --paper  #fdfcfa  on --link  #0b5d51   =  7.59:1  AAA  (primary .btn)
     --ink    #1a1d21  on --amber #f0c05a   =  9.98:1  AAA  (claim callout, CTA)
     --quartz-d #7a2f3e on --paper #fdfcfa  =  8.89:1  AAA  (accent text, counts)
     --link-h #083f37  on --amber #f0c05a   =  6.98:1  AA   (links inside callout)
     --amber  #f0c05a  on --ink   #1a1d21   =  9.98:1  AAA  (removal button)

   ⛔ ONE PAIR IS BANNED AND THE BAN IS ENFORCED BY A RULE FURTHER DOWN:
     --link-h #083f37  on --link  #0b5d51   =  1.52:1  ⛔ UNREADABLE
   That is what `.claim-callout a` did to `.claim-callout .btn` before this
   was measured. See the block above `.claim-callout .btn`.

   --- ⭐ TYPE NEVER SITS ON A PHOTOGRAPH (DAYLIGHT, 2026-07-21) ---------------
   ⛔ NO PHOTOGRAPH IS EVER DARKENED TO MAKE TEXT READABLE. No filter, no
   brightness, no opacity, no scrim, no plate. That rule is unchanged and it
   was always kept.

   ⭐ AND NOW NO WORD IS LAID OVER A PICTURE AT ALL. Every text-over-photo
   arrangement on this site has been dismantled: the hero (2026-07-20), and the
   category mastheads, the homepage section bands and the CTA band (2026-07-21).
   Words live on --paper; pictures live in their own frames.

   ⛔ WHY, BECAUSE THIS IS THE WHOLE LESSON OF THIS CODEBASE. When type sits on
   a photograph the contrast ratio becomes a property of THE PIXELS, so the
   pixels have to be controlled. That is what happened: 28 band images were
   generated to a spec demanding "a genuinely unlit black quadrant in the lower
   left", and CSS clamps kept the glyphs inside it. It measured beautifully
   (14.03:1 worst case) and it made the site dark -- the homepage bands rendered
   at 8.4% and 15.6% luminance. The no-darkening rule was honoured to the letter
   the entire time, because the darkness was baked into the source images.

     ⭐ A CONTRAST RATIO IS A CONSTRAINT, NOT A GOAL.
        Optimising for the number produces a black rectangle that scores 18:1.

   That lesson was recorded at home.html:66 after the hero was fixed, and never
   applied to the 28 band images generated to exactly the spec it warns about.
   It is applied everywhere now.

   ⭐ THE CONSEQUENCE: every ratio on this site is FLAT-ON-FLAT and fixed in
   this file, so no image can ever change one. --ink on --paper 16.50:1,
   --muted on --paper 6.19:1, --quartz-d on --paper 8.89:1, --ink on --amber
   9.98:1, --paper on --pine 15.78:1. There is nothing left to re-measure when
   a photograph is swapped, no per-image glyph scan, no object-position that is
   secretly a contrast control, and no clamp that is secretly a contrast
   control. Swapping a band image is now purely a question of whether the
   picture is any good. See IMAGE-SPEC.md.

   ⛔ IF YOU EVER WANT A SCRIM, THE TYPE HAS CREPT BACK ONTO A PICTURE. Move
   the type off; do not darken the picture.

   Note the probe must out-specificity `.btn-add`'s own `!important` colour or
   the glyphs stay painted and the button scores 1.00:1 against itself -- the
   probe script handles this.

   --rule #d6d1c7 on --paper is 1.48:1 and --quartz #e8b4bd on --paper is
   1.67:1: borders and rules only, never text. Quartzite for TEXT on paper is
   --quartz-d #7a2f3e (8.89:1).

   ⛔ If you change a colour, RECOMPUTE. If you change an image, an
   object-position or any of the clamps listed above, RE-MEASURE at real glyph
   positions at BOTH breakpoints -- there is no worst-case shortcut any more,
   because there is no plate to compute against.
   ========================================================================== */

:root {
  --paper:   #fdfcfa;
  --panel:   #f2efe9;
  --ink:     #1a1d21;
  --muted:   #5a6068;
  --link:    #0b5d51;
  --link-h:  #083f37;
  --pine:    #12241f;
  --pine-t:  #cfe3dd;
  --amber:   #f0c05a;
  --rule:    #d6d1c7;
  --hairline:#e7e3da;

  /* Sioux quartzite. --quartz is a rule/border colour and a sub-line colour
     over --shade only; --quartz-d is the accent safe for text on paper. */
  --quartz:   #e8b4bd;
  --quartz-d: #7a2f3e;
  --quartz-dd:#5f2430;   /* search button hover; darker than --quartz-d, so
                            white on it only gains contrast (9.12:1 floor). */

  /* ⛔ --shade IS GONE (2026-07-20) AND MUST NOT COME BACK. It was the opaque
     plate behind hero and CTA-band type. Every word over a photograph now sits
     on a genuinely unlit region of the photograph itself, exactly as the
     category mastheads do. If text over an image does not measure, the answer
     is a different IMAGE or a tighter width clamp -- never a plate, a scrim or
     a filter. See the two contrast blocks further down. */

  --measure: 68ch;
  --max:     1080px;
  --wide:    1280px;

  /* Spacing rhythm. One scale, used everywhere. */
  --s1: .35rem;
  --s2: .7rem;
  --s3: 1.1rem;
  --s4: 1.75rem;
  --s5: 2.75rem;
  --s6: 4rem;
  --s7: 5.5rem;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
           Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, sans-serif;
}

/* System fonts only, deliberately: a webfont is a render-blocking request on a
   phone with a bad connection, which is exactly the visitor we are optimising
   for. The serif stack resolves to a real book face on every major platform. */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.65;
  /* 18px base, not 16px: this is a reading page, and older business owners are
     a meaningful share of the audience. */
}

img { max-width: 100%; height: auto; }

a {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
}
a:hover, a:focus { color: var(--link-h); }

/* Focus is VISIBLE and never removed. The claim and removal forms must be
   completable by keyboard. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--link);
  outline-offset: 2px;
}

.wrap      { width: 100%; max-width: var(--max);  margin: 0 auto; padding: 0 1.25rem; }
.wrap-wide { width: 100%; max-width: var(--wide); margin: 0 auto; padding: 0 1.25rem; }

.skip {
  position: absolute; left: -9999px;
  background: var(--paper); color: var(--ink);
  padding: .75rem 1rem; z-index: 100;
}
.skip:focus { left: 1rem; top: 1rem; }

/* --- header ---------------------------------------------------------------
   A thin WHITE strip, ~88px on desktop. Wordmark hard left, category nav
   CENTERED with dropdown carets, search icon + bordered "Add Your Business"
   on the right, with real horizontal breathing room between the three.

   ⛔ THE MOBILE MENU IS A <details>/<summary> AND USES NO JAVASCRIPT. The
   browser opens and closes it natively; keyboard-operable and screen-reader-
   announced for free, and it works on the phone with the bad connection this
   whole site is built for.

   ⛔ EXACTLY ONE OF THE TWO NAVS IS DISPLAYED, EVER. Below 62em the
   disclosure is shown and the wide nav is display:none; above 62em the
   reverse. A closed <details> hides its slot at the UA level and no CSS on
   the child can force it open -- a previous build shipped a desktop nav that
   was INVISIBLE while every geometric probe said it was fine. Two
   containers, one data source. See base.html. */
.site-header {
  background: #ffffff;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
}
.header-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  min-height: 4rem;
  flex-wrap: wrap;
}

.brand-block { line-height: 1; }
.brand {
  display: block;
  font-family: var(--serif);
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
  line-height: 1.05;
}
.brand:hover, .brand:focus { color: var(--ink); }
.brand-sub {
  display: block;
  font-family: var(--sans);
  font-size: .66rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: .2rem;
}

/* Desktop nav: centered, with CSS-only dropdowns. Hidden below 62em. */
.site-nav-wide { display: none; }

/* Right cluster: search icon + bordered CTA. Hidden below 62em (the mobile
   menu carries its own CTA copy instead). */
.header-right { display: none; }

.icon-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  color: var(--ink);
  border-radius: 50%;
}
.icon-link:hover, .icon-link:focus { color: var(--quartz-d); }
.icon-link svg { display: block; }

/* ⛔ "Add Your Business": the header's one call to action -- a BORDERED
   button with an arrow glyph, per the reference direction. Ink on white =
   16.91:1. It points at the SAME claim flow the listing pages use; there is
   no second signup path. */
.btn-add {
  display: inline-block;
  font-family: var(--sans);
  font-size: .88rem;
  font-weight: 600;
  letter-spacing: .01em;
  padding: .55rem 1.15rem;
  background: transparent;
  color: var(--ink) !important;
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  text-decoration: none;
  white-space: nowrap;
}
.btn-add:hover, .btn-add:focus {
  background: var(--ink);
  color: #ffffff !important;
}
.btn-add .arrw { font-family: var(--sans); }

/* The mobile disclosure. Second flex item, so it sits to the RIGHT of the
   wordmark; when open its <nav> drops below on a full-width row. */
.nav-disclosure { font-family: var(--sans); margin-left: auto; }
.nav-disclosure[open] { flex: 1 1 100%; margin-left: 0; }
.nav-toggle {
  list-style: none;
  cursor: pointer;
  display: inline-block;
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink);
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  padding: .45rem 1rem;
  float: right;
}
.nav-toggle::-webkit-details-marker { display: none; }
.nav-toggle::marker { content: ""; }
.nav-disclosure[open] .nav-toggle { background: var(--ink); color: #ffffff; }

.site-nav-mobile {
  clear: both;
  font-family: var(--sans);
  font-size: 1rem;
  padding: var(--s3) 0 var(--s3);
}
.site-nav-mobile ul {
  list-style: none;
  margin: 0 0 var(--s2);
  padding: 0;
}
.site-nav-mobile li { border-top: 1px solid var(--hairline); }
.site-nav-mobile a {
  color: var(--ink);
  text-decoration: none;
  display: block;
  padding: .65rem .15rem;
}
.site-nav-mobile a:hover, .site-nav-mobile a:focus { color: var(--quartz-d); text-decoration: underline; }
.site-nav-mobile .nav-sec { font-size: .92rem; }
.site-nav-mobile .nav-sec a { color: var(--muted); }
.site-nav-mobile .nav-cta { border-top: 0; padding: var(--s3) 0 0; }
.site-nav-mobile .nav-cta .btn-add { display: inline-block; }

/* --- the hero -------------------------------------------------------------
   ⛔ FULL-BLEED, ~85% OF THE FOLD, TYPE CENTERED, PHOTOGRAPH AT FULL
   BRIGHTNESS. The <img> carries no filter, no opacity, no darkening of any
   kind.

   ⛔ NO PLACARD. There is no shade band, no scrim, no plate and no overlay of
   any kind between the photograph and the type. The opaque .hero-stage panel
   that used to sit here was removed on 2026-07-20; --shade is gone from this
   file entirely. What makes the type legible now is the same thing that makes
   the category mastheads legible: THE IMAGE CHOICE.

   ⛔ HOW THE CONTRAST IS ACTUALLY WON. hero-falls-night.jpg is generated with
   a genuinely unlit region -- the whole upper half of the frame is empty night
   sky and shadowed treeline, and the lit waterfall sits in the lower right.
   Measured on the shipped 2000x1125 file, worst SINGLE pixel:

     full width, top 55% of the image   18.80:1  for white
     full width, top 58%                12.18:1
     full width, top 60%                10.26:1
     full width, top 65%                 6.92:1   <- the falls start to intrude

   So the type is anchored to the TOP of the hero and the stack is height-
   limited, keeping every glyph inside the 55% band that measures 18.80:1.
   This is why `justify-content` is flex-start and not center: centering the
   stack would drop the search bar onto the lit water. It is a CONTRAST
   CONTROL, not a layout preference.

   ⛔ object-position is `center top`. The unlit region is the TOP of the
   photograph, so the top edge must stay pinned to the top of the hero at every
   viewport height. Changing this moves the glyphs onto lit pixels and
   invalidates every number above. Re-run band_contrast.py at BOTH breakpoints
   after any change here.

   The image is a real <img> so it is a normal, cacheable, width/height-
   attributed resource (no layout shift), and so it degrades to the pine
   fallback gradient if the file is ever missing rather than a broken frame. */
/* ⭐ THE PHOTOGRAPH IS A BAND, THE WORDS SIT BELOW IT ON CREAM (Dan, 2026-07-20).

   hero-falls-golden.jpg was measured and carries type in NO colour in ANY
   region: white peaks at 1.60:1, near-black at 1.30:1, against a 4.5:1 floor.
   The treeline and stone ruins punch dark holes through the bright sky; the
   white spray punches bright holes through everything below. Moving the type
   does not help because there is nowhere to move it to.

   Rather than darken a good photograph or put a plate back on it, the picture
   runs full-bleed at 100% as its own band and the type lives underneath on
   cream. Nothing is compromised: the photo keeps every pixel of its light, and
   the words sit on a background built for reading (--ink on --cream, >12:1).

   ⛔ Do NOT move the headline back over the image. ⛔ Do NOT add a scrim,
   plate, gradient or filter to make that possible. If text-on-photo is wanted
   here, the IMAGE must change first: it needs a genuinely low-key region with
   real detail (see DESIGN-CRITIQUE.md). A near-black image is NOT the answer;
   that shipped as hero-falls-night.jpg, scored 18.80:1, and Dan said the site
   "got dark". A contrast ratio is a CONSTRAINT, not a goal. */
.hero-band {
  position: relative;
  width: 100%;
  /* Tall enough to command the fold, short enough that the type below it is
     visible without scrolling on a laptop. */
  height: clamp(17rem, 42vh, 30rem);
  overflow: hidden;
  background: var(--pine);
}
.hero-band-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The falls and the lit ruins sit in the middle of the frame; centre keeps
     both in shot as the band shortens on small screens. */
  object-position: center center;
  display: block;
  /* ⛔ NO filter, NO brightness, NO opacity. Full brightness, always. */
}

.hero {
  position: relative;
  background: var(--cream);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

/* ⛔ NOT A PLACARD and no longer needs to be one. The type sits on cream, so
   there is no photograph underneath it and no text-shadow is required. */
.hero-stage {
  position: relative;
  z-index: 1;
  width: 100%;
  text-align: center;
  padding: var(--s5) 0 var(--s4);
}

.hero-eyebrow {
  font-family: var(--sans);
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--quartz-d);
  margin: 0 0 var(--s3);
}
/* The H1: the reason the page reads as designed. 72px at desktop against an
   18px body -- a 4:1 display ratio -- very heavy, tight letter-spacing,
   white, centered. */
.hero h1 {
  font-family: var(--sans);
  font-weight: 800;
  font-size: clamp(2.1rem, 1rem + 4.6vw, 4.5rem);
  line-height: 1.04;
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 0 0 var(--s3);
  max-width: none;
}
.hero-sub {
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(1.15rem, .85rem + 1.35vw, 2.125rem);
  line-height: 1.35;
  color: var(--muted);
  margin: 0 auto var(--s4);
  max-width: 46rem;
}

/* ⛔ THE SEARCH BAR: the centerpiece. ~630px wide, centered, white, generous
   internal padding, solid deep-quartzite button ATTACHED on the right. It
   posts GET to /search/, the route that already exists, input named `q` --
   works with JavaScript disabled because it is a form. */
.hero-search {
  display: flex;
  max-width: 39.5rem;
  margin: 0 auto;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 14px 40px rgba(6, 14, 11, .35);
}
.hero-search input[type="search"] {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 1.05rem 1.3rem;
  background: #ffffff;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.05rem;
}
.hero-search input::placeholder { color: var(--muted); opacity: 1; } /* 6.35:1 */
.hero-search button,
.page-search button {
  flex: 0 0 auto;
  border: 0;
  padding: 0 1.8rem;
  background: var(--quartz-d);    /* white on #7a2f3e = 9.12:1 */
  color: #ffffff;
  font-family: var(--sans);
  font-size: 1.02rem;
  font-weight: 700;
  cursor: pointer;
}
.hero-search button:hover, .hero-search button:focus,
.page-search button:hover, .page-search button:focus { background: var(--quartz-dd); }

.hero-meta {
  font-family: var(--sans);
  font-size: .85rem;
  color: var(--muted);           /* 7.29:1 worst case on the band */
  margin: var(--s4) auto 0;
  max-width: none;
}
.hero-meta a { color: var(--pine-t); }

/* The /search/ page's own copy of the search bar, on paper instead of the
   photograph, so that page stands alone now that the header carries only an
   icon. Same route, same input name. */
.page-search {
  display: flex;
  max-width: 39.5rem;
  margin: var(--s3) 0 var(--s4);
  border: 1px solid var(--rule);
  border-radius: 8px;
  overflow: hidden;
}
.page-search input[type="search"] {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: .9rem 1.1rem;
  background: #ffffff;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
}
.page-search input::placeholder { color: var(--muted); opacity: 1; }

/* The "find yourself first" bar on the add-business page. */
.searchbar { margin: var(--s3) 0; display: flex; gap: .5rem; max-width: 30rem; }
.searchbar input[type="search"] {
  flex: 1; min-width: 0;
  padding: .55rem .7rem;
  border: 1px solid var(--rule);
  background: #ffffff;
  color: var(--ink);
  font: inherit; font-size: .95rem;
}

/* --- the welcome block ----------------------------------------------------
   Calm after the loud hero: centered, on plain paper, a large bold serif
   line with a lighter smaller line beneath. The loud-then-quiet rhythm is
   the point -- do not decorate this. */
.welcome {
  text-align: center;
  padding: var(--s5) 0 var(--s3);
}
.welcome h2 {
  font-family: var(--serif);
  font-size: clamp(1.7rem, 1.2rem + 1.8vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 var(--s2);
}
.welcome-sub {
  font-family: var(--sans);
  font-size: 1.15rem;
  color: var(--muted);
  margin: 0 auto var(--s3);
  max-width: none;
}
.welcome-body {
  margin: 0 auto;
  max-width: 44rem;
  font-size: 1.02rem;
}

/* --- section bands (DAYLIGHT, 2026-07-21) ---------------------------------
   ⭐ TYPE NEVER SITS ON A PHOTOGRAPH. That single rule is what this rewrite is,
   and everything else here follows from it.

   ⛔ WHAT WAS HERE BEFORE, AND WHY IT WAS DELETED. The label used to be set on
   top of the picture, so its legibility depended on the pixels underneath it.
   To guarantee those pixels, all 28 band photographs were GENERATED with "a
   genuinely unlit black quadrant in the lower left", and this block carried a
   `max-width: 11.5rem` clamp whose only job was to keep the glyphs from
   wandering out of that black rectangle. The method worked on its own terms:
   the worst pixel under the type measured 14.03:1.

   It also made the whole site dark. The darkness was never a filter and never
   a scrim -- the "photos at 100%, no darkening" rule was kept perfectly -- it
   was BAKED INTO THE SOURCE PIXELS by the generation spec. The homepage bands
   rendered at 8.4% and 15.6% luminance: black slabs with white type in them.

   ⭐ THE PROJECT'S OWN LESSON, recorded at home.html:66 and then applied to
   exactly one hero and to none of the 28 band images:
       "A CONTRAST RATIO IS A CONSTRAINT, NOT A GOAL. Optimising for the number
        produces a black rectangle that scores 18:1."

   THE FIX IS STRUCTURAL, NOT COSMETIC. The label moved OUT of the band and
   down onto paper (see .band-caption below and category.html). With no glyph
   ever touching a photograph:
     - there is no contrast requirement on any band image, at any breakpoint;
     - the black-quadrant generation spec is obsolete and every image has been
       regenerated bright (see IMAGE-SPEC.md);
     - the 11.5rem clamp, the font-size clamp, the text-shadow, the
       `object-position: left 78%` anchor and the near-black `background` are
       all DELETED, because each existed only to serve type-on-photo.
   Do not reintroduce any of them. If you ever find yourself wanting a scrim,
   the type has crept back onto the picture and that is the bug. */

/* The band is now a plain photographic window: a bright picture in a rounded
   frame, with nothing on top of it. --band-h is set per weight (see below) so
   a category with one listing is not led by a billboard. */
.band {
  position: relative;
  margin: var(--s6) 0 var(--s2);
  height: var(--band-h, 13rem);
  border-radius: 14px;
  overflow: hidden;
  /* ⭐ QUARTZITE-TINTED PLACEHOLDER, NOT FLAT GREY (2026-07-24). This is only
     ever visible for the instant before an image paints -- but the impeccable
     critique caught that on a no-scroll landing, a slow connection, or any
     fold-capturing crawler/social-preview, "the instant" is the whole first
     impression, and a flat --panel grey rectangle reads as broken on a site
     that sells "not the janky directory". A low-cost tinted LQIP makes an
     un-painted band look DESIGNED, not empty: a soft diagonal wash from the
     site's own quartzite accent into paper, the same local cue as the rest of
     the page. It never flashes dark (both stops are light: --quartz #e8b4bd is
     a rule/border tint, --paper is the body ground) and it costs zero bytes --
     no blurhash image, just two gradients already in the paint pipeline. */
  background-color: #f6e7ea;   /* quartzite-tinted fallback where color-mix is unsupported */
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--quartz) 55%, var(--paper)) 0%,
      var(--panel) 55%,
      color-mix(in srgb, var(--quartz) 30%, var(--paper)) 100%);
}
/* ⛔ CENTRED, not anchored to a corner. The old `left 78%` existed to hold the
   manufactured dark corner under the type. With no type on the image, the crop
   should simply show the best part of the picture, which is its middle. */
.band-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

/* ⭐ WEIGHT-AWARE BAND. A page holding one business should not be introduced by
   the same billboard as one holding 178. The template sets .band--thin below 6
   listings. Heights are the only difference; there is no contrast implication
   either way now, so these are free to be chosen for looks. */
.band--thin { --band-h: 8.5rem; }
.band--full { --band-h: 13rem; }

/* ⭐ THE LABEL, NOW ON PAPER. This is the replacement for .band-label. It sits
   BELOW the photograph in normal document flow, so it is ordinary dark-on-paper
   text: --ink on --paper is 16.50:1 and --quartz-d on --paper is 8.89:1,
   fixed values that no image can ever change. */
.band-caption {
  margin: var(--s2) 0 var(--s4);
}
.band-caption .band-kicker {
  margin: 0 0 var(--s1);
  font-family: var(--sans);
  font-weight: 700;
  font-size: .8rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--quartz-d);      /* 8.89:1 on paper */
}
.band-caption h2 {
  margin: 0;
  font-family: var(--sans);
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--ink);           /* 16.50:1 on paper */
  font-size: 1.5rem;
  line-height: 1.2;
}
.band-caption p {
  margin: var(--s1) 0 0;
  font-family: var(--sans);
  font-size: .95rem;
  color: var(--muted);         /* 6.19:1 on paper */
  max-width: var(--measure);
}
.band + .grid { margin-top: var(--s3); }

/* --- typography ----------------------------------------------------------- */
main { padding: 2.25rem 0 3rem; }

/* --- the reading column ---------------------------------------------------
   ⛔ THE MEASURE IS KEPT AND THE COLUMN IS CENTERED. THIS RULE IS WHY.

   The 2026-07-20 bug: every paragraph is capped at var(--measure) (68ch,
   roughly 610px) for readability, but nothing centered that column inside the
   1080px .wrap, so on any page with written paragraphs the prose hugged the
   LEFT edge with a dead gap on the right, while the hero, the bands and the
   footer ran full width. It read as broken on every prose page at once.

   The fix is NOT to let paragraphs run full width; a 1032px line of 18px serif
   is unreadable and the 65-75 character measure is correct. Instead, the
   default content container is a three-track grid: a centered column exactly
   one measure wide, with flexible gutters either side. Every direct child of
   the page body lands in the centered column; the deliberately WIDE components
   (the photograph bands, the card grids, the CTA band, and home's full-width
   sections) opt out and span the full container. Component margins like
   `margin: 1rem 0` no longer decide horizontal placement, the grid does, so
   this cannot regress per-component.

   At <=610px content width, min(var(--measure), 100%) is 100% and the gutter
   tracks collapse to zero, so mobile is byte-for-byte the old layout.

   The admin pages (html.admin) keep the plain block container: tables and
   editing forms want the full width, and nobody reads prose there. */
main .wrap {
  display: grid;
  grid-template-columns:
    [full-start] minmax(0, 1fr)
    [col-start]  min(var(--measure), 100%)
    [col-end]    minmax(0, 1fr)
    [full-end];
}
main .wrap > * { grid-column: col; }
main .wrap > :is(.band, .cta-band, .grid, section) { grid-column: full; }
/* ⭐ The band caption is the words that used to sit ON the photograph. It must
   line up with the LEFT EDGE OF THE PICTURE it belongs to, not indent to the
   reading column, or it reads as detached from the image above it. */
main .wrap > .band-caption { grid-column: full; }
/* The SoS badge is an inline-block pill; as a grid item it would blockify and
   stretch to the full column. Keep it shrink-wrapped at the column's left. */
main .wrap > .badge { justify-self: start; }
.admin main .wrap { display: block; }

h1 {
  font-size: 2.1rem;
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 .5rem;
}
h2 { font-size: 1.45rem; line-height: 1.25; margin: 2rem 0 .5rem; }
h3 { font-size: 1.15rem; line-height: 1.3; margin: 1.5rem 0 .4rem; }
p  { margin: 0 0 1rem; max-width: var(--measure); }

.lede { font-size: 1.12rem; color: var(--ink); max-width: var(--measure); }
.meta {
  font-family: var(--sans);
  font-size: .85rem;
  color: var(--muted);
  margin: 0 0 .75rem;
}

.crumbs {
  font-family: var(--sans);
  font-size: .82rem;
  color: var(--muted);
  margin: 0 0 1.25rem;
}
.crumbs a { color: var(--link); }
.crumbs span[aria-current] { color: var(--muted); }

/* --- panels + cards ------------------------------------------------------- */
.panel {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 1.25rem;
  margin: 1.5rem 0;
}

.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin: 1.25rem 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 34em)  { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 56em)  { .grid { grid-template-columns: repeat(3, 1fr); } }

.card {
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: var(--s3) var(--s3) var(--s3);
  background: #ffffff;
}
.card h3 { margin: 0 0 .25rem; font-family: var(--sans); font-weight: 650; font-size: 1.02rem; }
.card p  { margin: 0; font-family: var(--sans); font-size: .9rem; color: var(--muted); }
.card a  { color: var(--ink); text-decoration: none; }
.card a:hover, .card a:focus { color: var(--quartz-d); text-decoration: underline; }
.card:hover { border-color: var(--quartz-d); box-shadow: 0 10px 26px rgba(26, 29, 33, .07); }
.card .count {
  display: block;
  font-family: var(--sans);
  font-size: .74rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--quartz-d);
  margin-top: var(--s2);
}

/* --- weighted category grid (2026-07-24) ----------------------------------
   ⭐ HIERARCHY FROM THE COUNT, NOT DECORATION. The homepage category grids were
   36 identical tiles with equal weight -- the critique's one "any directory
   could ship this" finding. The template now sorts by listing_count and marks
   the top two .card--marquee; this gives those two real presence so the eye has
   somewhere to land and the biggest categories read as the front door. It uses
   only weight, size and span -- no new colour, no new border, no gradient text
   -- so it stays inside the site's committed quartzite-on-paper world.

   The marquee tile is a promoted version of the SAME card: it spans two of the
   three columns, sets its name larger, and lets its count carry weight as the
   thing that earned it the billboard. Below 56em the grid is a single column,
   so there is nothing to span and every tile is full width -- the marquee rule
   simply enlarges the type there, which still reads as "these come first". */
@media (min-width: 56em) {
  .grid--weighted { grid-auto-flow: dense; }
  .grid--weighted .card--marquee { grid-column: span 2; }
}
.card--marquee {
  /* A hair more room and a subtle lift so the promoted tiles feel considered,
     not just wider. Offset + soft blur (never a zero-offset halo). */
  padding: var(--s4) var(--s4) var(--s3);
  box-shadow: 0 6px 22px rgba(122, 47, 62, .08);
}
.card--marquee h3 {
  font-size: 1.4rem;
  letter-spacing: -0.01em;
}
.card--marquee .count {
  font-size: .82rem;
  font-weight: 700;
}
@media (min-width: 66em) {
  .card--marquee h3 { font-size: 1.65rem; }
}

/* --- section headings on the homepage ------------------------------------- */
.section-intro {
  font-size: 1rem;
  color: var(--muted);
  max-width: var(--measure);
  margin: 0 0 var(--s3);
}

/* --- the "Add Your Business" call to action ------------------------------
   The homepage's own version of the header button, given a full band. Points
   at the SAME claim flow as the listing pages.

   ⭐ IT IS NOW A SPLIT CARD, NOT TYPE ON A PHOTOGRAPH (DAYLIGHT, 2026-07-21).
   This was the last place on the site where words sat on pixels, and it was
   held together by the most fragile measurement in the stylesheet: the old
   cta-downtown-night.jpg was generated with a hard tonal split (empty unlit
   night on the left, lit storefronts on the right) and the text max-width was
   a CONTRAST CONTROL sitting right on the cliff edge -- 14.12:1 at the left
   50% of the frame and 2.43:1 at 52%. Two percent of crop was the whole
   safety margin, at both breakpoints, forever.

   The photograph and the words are now SIDE BY SIDE in a grid, each on its own
   ground. The type is --ink on --paper (16.50:1), fixed, and the picture is a
   bright daylight street rather than a night shot chosen for its dark half.
   The cliff, the 18%/left-center crop rules and the text-shadow are deleted. */
.cta-band {
  margin: var(--s6) 0 var(--s4);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  overflow: hidden;
  background: var(--paper);
  display: grid;
  grid-template-columns: 1fr;   /* stacked on mobile; split at 48em below */
}
/* The picture half. A fixed height on mobile so the 16:9 frame does not eat
   the screen; on desktop it stretches to whatever the copy needs. */
.cta-media {
  position: relative;
  min-height: 11rem;
  /* Same quartzite-tinted LQIP as .band, for the same reason: the CTA picture
     is lazy, and an un-painted flat-grey half reads as broken. */
  background-color: #f6e7ea;
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--quartz) 55%, var(--paper)) 0%,
      var(--panel) 55%,
      color-mix(in srgb, var(--quartz) 30%, var(--paper)) 100%);
}
.cta-band .band-img { object-position: center; }
/* The words half, on paper. No z-index, no text-shadow, no max-width contrast
   control: nothing here depends on an image any more. */
.cta-inner {
  padding: var(--s4) var(--s4) var(--s5);
}
.cta-inner h2 { margin: 0 0 var(--s2); color: var(--ink); font-size: 1.5rem; }
.cta-inner p {
  color: var(--ink);
  margin: 0 0 var(--s3);
  max-width: var(--measure);
  font-size: 1rem;
}
.cta-inner .fine {
  font-family: var(--sans);
  font-size: .85rem;
  color: var(--muted);          /* 6.19:1 on paper */
  margin: var(--s2) 0 0;
}
/* Amber is reserved for exactly this one object on the page. Ink on amber
   = 9.98:1. */
.cta-inner .btn-add {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--ink) !important;
}
.cta-inner .btn-add:hover, .cta-inner .btn-add:focus {
  background: var(--pine);
  border-color: var(--pine);
  color: var(--paper) !important;
}

/* --- business entries on category pages -----------------------------------
   ⛔ Every business is a real H2 with real prose beneath it, never a bare
   <li> of links. The directories that rank put 14-18 business headings on a
   category page; the one that renders a link list measured 885 visible
   characters and has zero top-10 results. */
.entries { list-style: none; margin: 1.5rem 0 0; padding: 0; }
.entry {
  border-top: 1px solid var(--rule);
  padding: 1.4rem 0;
  /* ⭐ content-visibility skips layout + paint for entries scrolled far off
     screen on a long list, which is the perf half of the scan-aid work. The
     contain-intrinsic-size is a placeholder box height so the scrollbar and
     the anchor offsets stay honest before an entry is painted -- a rough
     average entry height (heading + address + a line of prose). It never
     changes what renders; it only defers WHEN. */
  content-visibility: auto;
  contain-intrinsic-size: auto 9rem;
}
/* A filtered-out entry is hidden with the `hidden` attribute by scan.js; make
   sure nothing re-shows it (some resets set [hidden]{display:revert}). */
.entry[hidden] { display: none; }

/* --- category scan tools (2026-07-24) -------------------------------------
   ⛔ JS-ONLY, AND GATED ON html.has-js. The toolbar and the back-to-top button
   are `hidden` in the markup; these rules only take effect under .has-js, so a
   no-JS visitor never sees a control that would not work. The site's no-JS
   promise is intact -- the list below the toolbar is fully server-rendered and
   A-Z sorted on its own. */
.scan-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
  margin: var(--s3) 0 0;
  padding: var(--s3);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  background: #ffffff;
  font-family: var(--sans);
  /* Sticks to the top of a tall page so the filter stays in reach mid-scroll.
     Sits below the header's own space; on mobile the header is not sticky, so
     this is the one sticky element and it does not stack. */
  position: sticky;
  top: .5rem;
  z-index: 20;
}
.scan-field { flex: 1 1 14rem; min-width: 0; }
.scan-input {
  width: 100%;
  padding: .6rem .8rem;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: #ffffff;
  color: var(--ink);
  font: inherit;
  font-family: var(--sans);
  font-size: 1rem;
}
.scan-input::placeholder { color: var(--muted); opacity: 1; } /* 6.35:1 */
.scan-sort { display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; }
.scan-sort label {
  font-size: .82rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .06em;
}
.scan-select {
  padding: .55rem 2rem .55rem .7rem;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: #ffffff;
  color: var(--ink);
  font: inherit;
  font-family: var(--sans);
  font-size: .95rem;
  cursor: pointer;
  /* A quartzite caret drawn as a background image, so no library and no glyph. */
  -webkit-appearance: none;
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--quartz-d) 50%),
    linear-gradient(135deg, var(--quartz-d) 50%, transparent 50%);
  background-position:
    right 1.05rem center,
    right .75rem center;
  background-size: .32rem .32rem, .32rem .32rem;
  background-repeat: no-repeat;
}
.scan-count {
  flex: 1 1 100%;
  margin: 0;
  font-size: .85rem;
  color: var(--muted);       /* 6.19:1 on white/paper */
}
.scan-count:empty { display: none; }

.scan-empty {
  margin: var(--s4) 0;
  padding: var(--s3);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  background: var(--panel);
  font-family: var(--sans);
  color: var(--ink);         /* 14.74:1 on --panel */
}
.scan-clear {
  font: inherit;
  font-family: var(--sans);
  color: var(--link);
  background: none;
  border: 0;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.scan-clear:hover, .scan-clear:focus { color: var(--link-h); }

/* Everything above is JS-only. Off .has-js, force it out of the layout so a
   no-JS visitor never meets an inert control even if `hidden` is stripped. */
:root:not(.has-js) .scan-tools,
:root:not(.has-js) .scan-empty,
:root:not(.has-js) .to-top { display: none !important; }

/* --- back to top ----------------------------------------------------------
   Fixed to the lower-right, out of the reading column. Revealed by scan.js
   after ~a screenful of scroll; it eases up from slightly below with a soft
   shadow (offset + blur, never a flat halo). Respects reduced-motion via the
   global rule at the foot of this file. */
.to-top {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 40;
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .55rem .9rem;
  font-family: var(--sans);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
  color: #ffffff;                     /* white on --quartz-d = 9.12:1 */
  background: var(--quartz-d);
  border-radius: 999px;
  box-shadow: 0 8px 24px rgba(26, 29, 33, .22);
  opacity: 0;
  transform: translateY(.6rem);
  transition: opacity .18s ease-out, transform .18s ease-out;
}
.to-top.is-in { opacity: 1; transform: translateY(0); }
.to-top:hover, .to-top:focus { color: #ffffff; background: var(--quartz-dd); }
.to-top-arrow {
  width: .5rem;
  height: .5rem;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  transform: rotate(-45deg);
  margin-top: .12rem;
}
.entry:last-child { border-bottom: 1px solid var(--rule); }
.entry h2 { margin: 0 0 .3rem; font-size: 1.3rem; }
.entry h2 a { text-decoration: none; }
.entry h2 a:hover { text-decoration: underline; }
.entry .addr {
  font-family: var(--sans);
  font-size: .88rem;
  color: var(--muted);
  margin: 0 0 .5rem;
}
.entry p { margin: 0 0 .4rem; }

/* --- the NAP block on a listing page -------------------------------------- */
.nap {
  border-left: 4px solid var(--link);
  padding: .25rem 0 .25rem 1rem;
  margin: 1.25rem 0;
}
.nap .line { margin: 0 0 .3rem; }
.nap .phone { font-size: 1.15rem; }

.hours { border-collapse: collapse; margin: 1rem 0; font-size: .95rem; }
.hours th, .hours td {
  text-align: left;
  padding: .35rem 1.5rem .35rem 0;
  border-bottom: 1px solid var(--rule);
}
.hours th { font-family: var(--sans); font-size: .82rem; color: var(--muted); font-weight: 600; }

.badge {
  display: inline-block;
  font-family: var(--sans);
  font-size: .78rem;
  letter-spacing: .04em;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--ink);
  padding: .2rem .6rem;
  margin: 0 .35rem .35rem 0;
}

/* --- the claim callout ----------------------------------------------------
   ⛔ THIS BLOCK IS WHY A LISTING WITH NO WEBSITE READS AS INTENTIONAL RATHER
   THAN BROKEN. It sits where a visitor expects the website link to be and
   explains, unprompted, that we left it off on purpose and how to have it
   added. Amber: a notice, not an error and not a sales banner. Ink on amber
   is 9.98:1. Restyled in the redesign but NEVER removed -- it is load-
   bearing to the outreach. */
.claim-callout {
  background: var(--amber);
  color: var(--ink);
  border: 1px solid #c99a2e;
  border-radius: 10px;
  padding: 1.25rem;
  margin: 1.75rem 0;
}
.claim-callout h2 { margin: 0 0 .5rem; font-size: 1.2rem; }
.claim-callout p  { margin: 0 0 .75rem; max-width: none; }
/* Inline links inside the amber panel. #083f37 on #f0c05a = 6.98:1. */
.claim-callout a  { color: var(--link-h); }

/* ==========================================================================
   ⛔ MEASURED BUG, FOUND BY SAMPLING REAL GLYPH PIXELS, FIXED HERE.

   The rule above (`.claim-callout a { color: var(--link-h) }`) is correct
   for a sentence link but it also matched `.btn`, which is an <a>. So the
   PRIMARY "Update this listing" button was painting --link-h #083f37 text on
   its own --link #0b5d51 background:

       #083f37 on #0b5d51 = 1.52:1     ⛔ UNREADABLE

   A coarse "is the callout readable" check passes, because the callout IS
   readable -- the amber panel is 9.98:1. The button inside it was not, and
   it is the link the outreach email asks people to click.

   The fix is specificity: restate the button's own foreground so the panel's
   link rule cannot win.

       --paper #fdfcfa on --link   #0b5d51 =  7.59:1  AAA   (rest)
       --paper #fdfcfa on --link-h #083f37 = 11.53:1  AAA   (hover/focus)
       --amber #f0c05a on --ink    #1a1d21 =  9.98:1  AAA   (removal, rest)
       --paper #fdfcfa on --ink    #1a1d21 = 16.50:1  AAA   (removal, hover)

   ⛔ Both buttons stay AAA and the removal option remains visually equal to
   the update option, which is the rule the callout's own comment sets out.
   ========================================================================== */
.claim-callout .btn {
  color: var(--paper) !important;
  background: var(--link);
  border-color: var(--link);
}
.claim-callout .btn:hover,
.claim-callout .btn:focus {
  color: var(--paper) !important;
  background: var(--link-h);
  border-color: var(--link-h);
}
.claim-callout .btn-secondary {
  color: var(--amber) !important;
  background: var(--ink);
  border-color: var(--ink);
}
.claim-callout .btn-secondary:hover,
.claim-callout .btn-secondary:focus {
  color: var(--paper) !important;
  background: var(--ink);
  border-color: var(--ink);
}

/* --- buttons + forms ------------------------------------------------------ */
.btn {
  display: inline-block;
  font-family: var(--sans);
  font-size: .95rem;
  font-weight: 600;
  padding: .6rem 1.1rem;
  border: 2px solid var(--link);
  border-radius: 8px;
  background: var(--link);
  color: var(--paper);
  text-decoration: none;
  cursor: pointer;
}
.btn:hover, .btn:focus { background: var(--link-h); border-color: var(--link-h); color: var(--paper); }
.btn-secondary { background: transparent; color: var(--link); }
.btn-secondary:hover, .btn-secondary:focus { background: var(--link); color: var(--paper); }

.field { margin: 0 0 1.1rem; max-width: 34rem; }
.field label {
  display: block;
  font-family: var(--sans);
  font-size: .88rem;
  font-weight: 600;
  margin-bottom: .3rem;
}
.field .hint {
  display: block;
  font-family: var(--sans);
  font-size: .8rem;
  color: var(--muted);
  margin-top: .25rem;
}
.field input[type="text"],
.field input[type="email"],
.field input[type="url"],
.field input[type="tel"],
.field input[type="password"],
.field select,
.field textarea {
  width: 100%;
  padding: .55rem .65rem;
  border: 1px solid var(--muted);
  border-radius: 6px;
  background: #ffffff;
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
}
.field textarea { min-height: 7rem; font-family: var(--sans); font-size: .95rem; }
.check { display: flex; gap: .6rem; align-items: flex-start; margin: 0 0 1.1rem; max-width: 34rem; }
.check input { margin-top: .35rem; width: 1.1rem; height: 1.1rem; flex: none; }
.check label { font-family: var(--sans); font-size: .92rem; font-weight: 400; }

/* The honeypot. Hidden from humans, present in the DOM for bots. Not
   display:none, because some bots skip those; off-screen still gets filled. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.notice {
  border-left: 4px solid var(--link);
  background: var(--panel);
  padding: .9rem 1rem;
  margin: 1.25rem 0;
  max-width: var(--measure);
}

/* --- footer ---------------------------------------------------------------
   ⛔ ZVARIO LLC IS NAMED HERE ON EVERY PAGE. Not buried, not abbreviated.
   This is what makes "a South Dakota company built a directory and listed
   your business" honest rather than a disguised funnel, and it is the first
   thing a suspicious recipient scrolls to look for. */
.site-footer {
  background: var(--pine);
  color: var(--paper);
  padding: 3rem 0;
  margin-top: 3.5rem;
  border-top: 4px solid var(--quartz);
  font-family: var(--sans);
  font-size: .9rem;
}
.site-footer a { color: var(--pine-t); }
.site-footer a:hover, .site-footer a:focus { color: var(--paper); }
.site-footer p { max-width: var(--measure); }
.footer-nav { margin: 0 0 1rem; }
.footer-nav a { margin-right: 1.15rem; display: inline-block; }
.owner { font-weight: 700; color: var(--paper); }

/* --- admin ---------------------------------------------------------------- */
.admin body { font-family: var(--sans); }
.admin-table { width: 100%; border-collapse: collapse; font-family: var(--sans); font-size: .88rem; }
.admin-table th, .admin-table td {
  text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
.admin-table th { background: var(--panel); font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }
.admin-table tr:hover td { background: var(--panel); }
.admin-stats { display: flex; flex-wrap: wrap; gap: 1rem; margin: 1rem 0 2rem; padding: 0; list-style: none; }
.admin-stats li {
  border: 1px solid var(--rule); padding: .75rem 1rem; min-width: 8rem;
  font-family: var(--sans);
}
.admin-stats .n { display: block; font-size: 1.6rem; font-weight: 700; }
.admin-stats .k { font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.inline-form { display: inline; }
.table-scroll { overflow-x: auto; }

/* ==========================================================================
   RESPONSIVE

   ⛔ NO HORIZONTAL SCROLL AT 375px. Everything is width-constrained by .wrap
   or .wrap-wide; the search rows keep `min-width: 0` on the input so it can
   shrink below its intrinsic size rather than pushing the row wide. Mobile
   is most of the traffic, so this is checked, not assumed.
   ========================================================================== */
@media (min-width: 66em) {
  h1 { font-size: 2.6rem; }

  /* The CTA becomes a true split card once there is room: picture on the left,
     words on paper on the right. No crop contrast rule any more, because no
     glyph touches the picture. */
  .cta-band { grid-template-columns: 1fr 1fr; align-items: stretch; }
  .cta-media { min-height: 100%; }
  .cta-inner { padding: var(--s5); align-self: center; }

  /* ⛔ SWAP THE NAVS. The whole disclosure goes away; the wide nav and the
     right cluster appear. No forcing a <details> open -- that cannot be done
     in CSS.

     ⛔ THE BREAKPOINT IS 66em BECAUSE THE BAR WAS MEASURED. At 62em the full
     row (brand + 8 centered items + icon + CTA) needed 1061px and overflowed
     a 1024px viewport with a horizontal scrollbar. Compact sizing below fits
     from 1056px; the roomy sizing arrives at 80em where it measured 1214px
     against 1280. If you add a nav item, re-measure scrollWidth at 1056 and
     1280, do not eyeball it. */
  .nav-disclosure { display: none; }
  .header-right {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex: 0 0 auto;
  }

  /* The thin white strip: ~88px. Brand left, nav centered, actions right. */
  .header-bar { min-height: 5.5rem; flex-wrap: nowrap; gap: var(--s3); }

  .site-nav-wide {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--sans);
  }
  .nav-main {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: .1rem;
  }
  .nav-main > li { position: relative; }
  .nav-main > li > a {
    display: inline-flex;
    align-items: center;
    gap: .38em;
    color: var(--ink);
    text-decoration: none;
    font-size: .88rem;
    font-weight: 500;
    padding: .5rem .45rem;
    border-radius: 6px;
    white-space: nowrap;
  }
  .nav-main > li > a:hover, .nav-main > li > a:focus {
    color: var(--quartz-d);
    background: var(--panel);
  }

  /* The dropdown caret: a small chevron drawn in CSS, no glyph, no image. */
  .caret {
    display: inline-block;
    width: .42em;
    height: .42em;
    border-right: 1.6px solid currentColor;
    border-bottom: 1.6px solid currentColor;
    transform: rotate(45deg) translate(-12%, -12%);
    margin-top: -.2em;
  }

  /* ⛔ CSS-ONLY DROPDOWNS: :hover for the mouse, :focus-within for the
     keyboard. No gap between trigger and panel (the panel starts at
     top:100%), so the hover never drops crossing into it. */
  .drop {
    display: none;
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    z-index: 60;
    list-style: none;
    margin: 0;
    padding: .5rem 0;
    min-width: 14.5rem;
    background: #ffffff;
    border: 1px solid var(--hairline);
    border-radius: 10px;
    box-shadow: 0 18px 44px rgba(26, 29, 33, .13);
  }
  .has-drop:hover .drop,
  .has-drop:focus-within .drop { display: block; }
  .drop a {
    display: block;
    padding: .48rem 1.1rem;
    color: var(--ink);
    text-decoration: none;
    font-size: .92rem;
    white-space: nowrap;
  }
  .drop a:hover, .drop a:focus { background: var(--panel); color: var(--quartz-d); }

  /* ⛔ CONTRAST CONTROL. The old rule here grew the hero's bottom padding on
     desktop, which was harmless when the type sat on an opaque plate. With the
     plate gone it would push the search bar DOWN out of the photograph's unlit
     upper region and onto the lit falls. Desktop gets more TOP room only; the
     bottom stays tight so the whole stack stays inside the measured 55% band.
     The ::before/::after shade ramps are gone with the plate. */
  .hero-stage { padding: var(--s6) 0 var(--s4); }

  .welcome { padding: var(--s7) 0 var(--s4); }

  /* Desktop gets a taller band so more of the photograph reads. There is no
     label inside it any more, so there is nothing to re-measure: these are
     picture-framing numbers only. The thin/full split is preserved. */
  .band--thin { --band-h: 12rem; }
  .band--full { --band-h: 21rem; }
  .band-caption h2 { font-size: 1.75rem; }
}

/* Roomy header sizing once there is genuinely room for it (measured 1214px
   of content at this sizing; 80em = 1280px). */
@media (min-width: 80em) {
  .header-bar { gap: var(--s4); }
  .header-right { gap: .9rem; }
  .nav-main { gap: .25rem; }
  .nav-main > li > a { font-size: .95rem; padding: .55rem .65rem; }
}

/* ⛔ Respect a reduced-motion preference. There is almost no motion here by
   design, but the rule is stated once so a future addition inherits it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .site-header, .site-footer, .claim-callout, .searchbar, .page-search,
  .hero-img, .band-img, .cta-band, .hero-search { display: none; }
  /* The hero keeps its words but loses its picture, so a printed page is ink on
     paper rather than a solid block of green. The band is now purely a
     photograph with no words in it at all, so in print it collapses to nothing
     and its caption prints as ordinary text below. */
  .hero { background: none; border-bottom: 1px solid #000; min-height: 0; }
  .band { display: none; }
  /* The hero has no background on screen either now that the plate is gone;
     print just drops the text-shadow so it is clean ink on paper. */
  .hero-stage { text-shadow: none; padding: 0; }
  .band-caption { margin: 0 0 1rem; }
  .hero h1, .hero-sub, .band-caption h2, .band-caption .band-kicker { color: #000; }
  .hero-eyebrow, .hero-meta, .band-caption p { color: #333; }
  body { font-size: 12pt; }
}
